1 package org
.asamk
.signal
.manager
.api
;
3 import org
.signal
.core
.util
.Base64
;
4 import org
.whispersystems
.signalservice
.api
.push
.UsernameLinkComponents
;
5 import org
.whispersystems
.signalservice
.api
.util
.UuidUtil
;
7 import java
.io
.IOException
;
8 import java
.util
.Arrays
;
9 import java
.util
.regex
.Pattern
;
11 public final class UsernameLinkUrl
{
13 private static final Pattern URL_REGEX
= Pattern
.compile("(https://)?signal.me/?#eu/([a-zA-Z0-9+\\-_/]+)");
15 private static final String BASE_URL
= "https://signal.me/#eu/";
17 private final String url
;
18 private final UsernameLinkComponents usernameLinkComponents
;
20 public static UsernameLinkUrl
fromUri(String url
) throws InvalidUsernameLinkException
{
21 final var matcher
= URL_REGEX
.matcher(url
);
22 if (!matcher
.matches()) {
23 throw new InvalidUsernameLinkException("Invalid username link");
25 final var path
= matcher
.group(2);
26 final byte[] allBytes
;
28 allBytes
= Base64
.decode(path
);
29 } catch (IOException e
) {
30 throw new InvalidUsernameLinkException("Invalid base64 encoding");
33 if (allBytes
.length
!= 48) {
34 throw new InvalidUsernameLinkException("Invalid username link");
37 final var entropy
= Arrays
.copyOfRange(allBytes
, 0, 32);
38 final var serverId
= Arrays
.copyOfRange(allBytes
, 32, allBytes
.length
);
39 final var serverIdUuid
= UuidUtil
.parseOrNull(serverId
);
40 if (serverIdUuid
== null) {
41 throw new InvalidUsernameLinkException("Invalid serverId");
44 return new UsernameLinkUrl(new UsernameLinkComponents(entropy
, serverIdUuid
));
47 public UsernameLinkUrl(UsernameLinkComponents usernameLinkComponents
) {
48 this.usernameLinkComponents
= usernameLinkComponents
;
49 this.url
= createUrl(usernameLinkComponents
);
52 private static String
createUrl(UsernameLinkComponents usernameLinkComponents
) {
53 final var entropy
= usernameLinkComponents
.getEntropy();
54 final var serverId
= UuidUtil
.toByteArray(usernameLinkComponents
.getServerId());
56 final var combined
= new byte[entropy
.length
+ serverId
.length
];
57 System
.arraycopy(entropy
, 0, combined
, 0, entropy
.length
);
58 System
.arraycopy(serverId
, 0, combined
, entropy
.length
, serverId
.length
);
60 final var base64
= Base64
.encodeUrlSafeWithoutPadding(combined
);
61 return BASE_URL
+ base64
;
64 public String
getUrl() {
68 public UsernameLinkComponents
getComponents() {
69 return usernameLinkComponents
;
72 public static final class InvalidUsernameLinkException
extends Exception
{
74 public InvalidUsernameLinkException(String message
) {
78 public InvalidUsernameLinkException(Throwable cause
) {