]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/api/GroupInviteLinkUrl.java
b21aeb97c2b150601dda057c8707b4cebab675c5
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / api / GroupInviteLinkUrl.java
1 package org.asamk.signal.manager.api;
2
3 import com.google.protobuf.ByteString;
4
5 import org.asamk.signal.manager.groups.GroupLinkPassword;
6 import org.signal.libsignal.zkgroup.InvalidInputException;
7 import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
8 import org.signal.storageservice.protos.groups.GroupInviteLink;
9 import org.signal.storageservice.protos.groups.local.DecryptedGroup;
10 import org.whispersystems.util.Base64UrlSafe;
11
12 import java.io.IOException;
13 import java.net.URI;
14 import java.net.URISyntaxException;
15
16 public final class GroupInviteLinkUrl {
17
18 private static final String GROUP_URL_HOST = "signal.group";
19 private static final String GROUP_URL_PREFIX = "https://" + GROUP_URL_HOST + "/#";
20
21 private final GroupMasterKey groupMasterKey;
22 private final GroupLinkPassword password;
23 private final String url;
24
25 public static GroupInviteLinkUrl forGroup(GroupMasterKey groupMasterKey, DecryptedGroup group) {
26 return new GroupInviteLinkUrl(groupMasterKey,
27 GroupLinkPassword.fromBytes(group.getInviteLinkPassword().toByteArray()));
28 }
29
30 /**
31 * @return null iff not a group url.
32 * @throws InvalidGroupLinkException If group url, but cannot be parsed.
33 */
34 public static GroupInviteLinkUrl fromUri(String urlString) throws InvalidGroupLinkException, UnknownGroupLinkVersionException {
35 var uri = getGroupUrl(urlString);
36
37 if (uri == null) {
38 return null;
39 }
40
41 try {
42 if (!"/".equals(uri.getPath()) && uri.getPath().length() > 0) {
43 throw new InvalidGroupLinkException("No path was expected in uri");
44 }
45
46 var encoding = uri.getFragment();
47
48 if (encoding == null || encoding.length() == 0) {
49 throw new InvalidGroupLinkException("No reference was in the uri");
50 }
51
52 var bytes = Base64UrlSafe.decodePaddingAgnostic(encoding);
53 var groupInviteLink = GroupInviteLink.parseFrom(bytes);
54
55 switch (groupInviteLink.getContentsCase()) {
56 case V1CONTENTS -> {
57 var groupInviteLinkContentsV1 = groupInviteLink.getV1Contents();
58 var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey()
59 .toByteArray());
60 var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.getInviteLinkPassword()
61 .toByteArray());
62
63 return new GroupInviteLinkUrl(groupMasterKey, password);
64 }
65 default -> throw new UnknownGroupLinkVersionException("Url contains no known group link content");
66 }
67 } catch (InvalidInputException | IOException e) {
68 throw new InvalidGroupLinkException(e);
69 }
70 }
71
72 /**
73 * @return {@link URI} if the host name matches.
74 */
75 private static URI getGroupUrl(String urlString) {
76 try {
77 var url = new URI(urlString);
78
79 if (!"https".equalsIgnoreCase(url.getScheme()) && !"sgnl".equalsIgnoreCase(url.getScheme())) {
80 return null;
81 }
82
83 return GROUP_URL_HOST.equalsIgnoreCase(url.getHost()) ? url : null;
84 } catch (URISyntaxException e) {
85 return null;
86 }
87 }
88
89 private GroupInviteLinkUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) {
90 this.groupMasterKey = groupMasterKey;
91 this.password = password;
92 this.url = createUrl(groupMasterKey, password);
93 }
94
95 private static String createUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) {
96 var groupInviteLink = GroupInviteLink.newBuilder()
97 .setV1Contents(GroupInviteLink.GroupInviteLinkContentsV1.newBuilder()
98 .setGroupMasterKey(ByteString.copyFrom(groupMasterKey.serialize()))
99 .setInviteLinkPassword(ByteString.copyFrom(password.serialize())))
100 .build();
101
102 var encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.toByteArray());
103
104 return GROUP_URL_PREFIX + encoding;
105 }
106
107 public String getUrl() {
108 return url;
109 }
110
111 public GroupMasterKey getGroupMasterKey() {
112 return groupMasterKey;
113 }
114
115 public GroupLinkPassword getPassword() {
116 return password;
117 }
118
119 public final static class InvalidGroupLinkException extends Exception {
120
121 public InvalidGroupLinkException(String message) {
122 super(message);
123 }
124
125 public InvalidGroupLinkException(Throwable cause) {
126 super(cause);
127 }
128 }
129
130 public final static class UnknownGroupLinkVersionException extends Exception {
131
132 public UnknownGroupLinkVersionException(String message) {
133 super(message);
134 }
135 }
136 }