]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/api/GroupInviteLinkUrl.java
cc3690f44e0aa5418ea7b551be245cf334d2084e
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / api / GroupInviteLinkUrl.java
1 package org.asamk.signal.manager.api;
2
3 import org.asamk.signal.manager.groups.GroupLinkPassword;
4 import org.signal.libsignal.zkgroup.InvalidInputException;
5 import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
6 import org.signal.storageservice.protos.groups.GroupInviteLink;
7 import org.signal.storageservice.protos.groups.local.DecryptedGroup;
8 import org.whispersystems.util.Base64UrlSafe;
9
10 import java.io.IOException;
11 import java.net.URI;
12 import java.net.URISyntaxException;
13
14 import okio.ByteString;
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.inviteLinkPassword.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().isEmpty()) {
43 throw new InvalidGroupLinkException("No path was expected in uri");
44 }
45
46 var encoding = uri.getFragment();
47
48 if (encoding == null || encoding.isEmpty()) {
49 throw new InvalidGroupLinkException("No reference was in the uri");
50 }
51
52 var bytes = Base64UrlSafe.decodePaddingAgnostic(encoding);
53 GroupInviteLink groupInviteLink = GroupInviteLink.ADAPTER.decode(bytes);
54
55 if (groupInviteLink.v1Contents != null) {
56 var groupInviteLinkContentsV1 = groupInviteLink.v1Contents;
57 var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.groupMasterKey.toByteArray());
58 var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.inviteLinkPassword.toByteArray());
59
60 return new GroupInviteLinkUrl(groupMasterKey, password);
61 } else {
62 throw new UnknownGroupLinkVersionException("Url contains no known group link content");
63 }
64 } catch (InvalidInputException | IOException e) {
65 throw new InvalidGroupLinkException(e);
66 }
67 }
68
69 /**
70 * @return {@link URI} if the host name matches.
71 */
72 private static URI getGroupUrl(String urlString) {
73 try {
74 var url = new URI(urlString);
75
76 if (!"https".equalsIgnoreCase(url.getScheme()) && !"sgnl".equalsIgnoreCase(url.getScheme())) {
77 return null;
78 }
79
80 return GROUP_URL_HOST.equalsIgnoreCase(url.getHost()) ? url : null;
81 } catch (URISyntaxException e) {
82 return null;
83 }
84 }
85
86 private GroupInviteLinkUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) {
87 this.groupMasterKey = groupMasterKey;
88 this.password = password;
89 this.url = createUrl(groupMasterKey, password);
90 }
91
92 private static String createUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) {
93 var groupInviteLink = new GroupInviteLink.Builder().v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder().groupMasterKey(
94 ByteString.of(groupMasterKey.serialize()))
95 .inviteLinkPassword(ByteString.of(password.serialize()))
96 .build()).build();
97
98 var encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.encode());
99
100 return GROUP_URL_PREFIX + encoding;
101 }
102
103 public String getUrl() {
104 return url;
105 }
106
107 public GroupMasterKey getGroupMasterKey() {
108 return groupMasterKey;
109 }
110
111 public GroupLinkPassword getPassword() {
112 return password;
113 }
114
115 public final static class InvalidGroupLinkException extends Exception {
116
117 public InvalidGroupLinkException(String message) {
118 super(message);
119 }
120
121 public InvalidGroupLinkException(Throwable cause) {
122 super(cause);
123 }
124 }
125
126 public final static class UnknownGroupLinkVersionException extends Exception {
127
128 public UnknownGroupLinkVersionException(String message) {
129 super(message);
130 }
131 }
132 }