]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java
Update libsignal-service-java
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / util / ProfileUtils.java
1 package org.asamk.signal.manager.util;
2
3 import org.asamk.signal.manager.api.Pair;
4 import org.asamk.signal.manager.storage.recipients.Profile;
5 import org.signal.zkgroup.profiles.ProfileKey;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
9 import org.whispersystems.signalservice.api.crypto.ProfileCipher;
10 import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
11
12 import java.util.Base64;
13 import java.util.HashSet;
14
15 public class ProfileUtils {
16
17 private final static Logger logger = LoggerFactory.getLogger(ProfileUtils.class);
18
19 public static Profile decryptProfile(
20 final ProfileKey profileKey, final SignalServiceProfile encryptedProfile
21 ) {
22 var profileCipher = new ProfileCipher(profileKey);
23 try {
24 var name = decrypt(encryptedProfile.getName(), profileCipher);
25 var about = trimZeros(decrypt(encryptedProfile.getAbout(), profileCipher));
26 var aboutEmoji = trimZeros(decrypt(encryptedProfile.getAboutEmoji(), profileCipher));
27
28 final var nameParts = splitName(name);
29 return new Profile(System.currentTimeMillis(),
30 nameParts.first(),
31 nameParts.second(),
32 about,
33 aboutEmoji,
34 encryptedProfile.getAvatar(),
35 getUnidentifiedAccessMode(encryptedProfile, profileCipher),
36 getCapabilities(encryptedProfile));
37 } catch (InvalidCiphertextException e) {
38 logger.debug("Failed to decrypt profile for {}", encryptedProfile.getServiceId(), e);
39 return null;
40 }
41 }
42
43 public static Profile.UnidentifiedAccessMode getUnidentifiedAccessMode(
44 final SignalServiceProfile encryptedProfile, final ProfileCipher profileCipher
45 ) {
46 if (encryptedProfile.isUnrestrictedUnidentifiedAccess()) {
47 return Profile.UnidentifiedAccessMode.UNRESTRICTED;
48 }
49
50 if (encryptedProfile.getUnidentifiedAccess() != null && profileCipher != null) {
51 final var unidentifiedAccessVerifier = Base64.getDecoder().decode(encryptedProfile.getUnidentifiedAccess());
52 if (profileCipher.verifyUnidentifiedAccess(unidentifiedAccessVerifier)) {
53 return Profile.UnidentifiedAccessMode.ENABLED;
54 }
55 }
56
57 return Profile.UnidentifiedAccessMode.DISABLED;
58 }
59
60 public static HashSet<Profile.Capability> getCapabilities(final SignalServiceProfile encryptedProfile) {
61 final var capabilities = new HashSet<Profile.Capability>();
62 if (encryptedProfile.getCapabilities().isGv1Migration()) {
63 capabilities.add(Profile.Capability.gv1Migration);
64 }
65 if (encryptedProfile.getCapabilities().isGv2()) {
66 capabilities.add(Profile.Capability.gv2);
67 }
68 if (encryptedProfile.getCapabilities().isStorage()) {
69 capabilities.add(Profile.Capability.storage);
70 }
71 if (encryptedProfile.getCapabilities().isSenderKey()) {
72 capabilities.add(Profile.Capability.senderKey);
73 }
74 if (encryptedProfile.getCapabilities().isAnnouncementGroup()) {
75 capabilities.add(Profile.Capability.announcementGroup);
76 }
77
78 return capabilities;
79 }
80
81 private static String decrypt(
82 final String encryptedName, final ProfileCipher profileCipher
83 ) throws InvalidCiphertextException {
84 try {
85 return encryptedName == null
86 ? null
87 : new String(profileCipher.decrypt(Base64.getDecoder().decode(encryptedName)));
88 } catch (IllegalArgumentException e) {
89 return null;
90 }
91 }
92
93 private static Pair<String, String> splitName(String name) {
94 if (name == null) {
95 return new Pair<>(null, null);
96 }
97 String[] parts = name.split("\0");
98
99 return switch (parts.length) {
100 case 0 -> new Pair<>(null, null);
101 case 1 -> new Pair<>(parts[0], null);
102 default -> new Pair<>(parts[0], parts[1]);
103 };
104 }
105
106 static String trimZeros(String str) {
107 if (str == null) {
108 return null;
109 }
110
111 int pos = str.indexOf(0);
112 return pos == -1 ? str : str.substring(0, pos);
113 }
114 }