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