]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java
ed04e5562bf48c8b12db881da303c941726af300
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / protocol / JsonIdentityKeyStore.java
1 package org.asamk.signal.manager.storage.protocol;
2
3 import com.fasterxml.jackson.core.JsonGenerator;
4 import com.fasterxml.jackson.core.JsonParser;
5 import com.fasterxml.jackson.databind.DeserializationContext;
6 import com.fasterxml.jackson.databind.JsonDeserializer;
7 import com.fasterxml.jackson.databind.JsonNode;
8 import com.fasterxml.jackson.databind.JsonSerializer;
9 import com.fasterxml.jackson.databind.SerializerProvider;
10
11 import org.asamk.signal.manager.TrustLevel;
12 import org.asamk.signal.manager.util.Utils;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15 import org.whispersystems.libsignal.IdentityKey;
16 import org.whispersystems.libsignal.IdentityKeyPair;
17 import org.whispersystems.libsignal.InvalidKeyException;
18 import org.whispersystems.libsignal.SignalProtocolAddress;
19 import org.whispersystems.libsignal.state.IdentityKeyStore;
20 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
21 import org.whispersystems.signalservice.api.util.UuidUtil;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Base64;
26 import java.util.Date;
27 import java.util.List;
28
29 public class JsonIdentityKeyStore implements IdentityKeyStore {
30
31 private final static Logger logger = LoggerFactory.getLogger(JsonIdentityKeyStore.class);
32
33 private final List<IdentityInfo> identities = new ArrayList<>();
34
35 private final IdentityKeyPair identityKeyPair;
36 private final int localRegistrationId;
37
38 private SignalServiceAddressResolver resolver;
39
40 public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) {
41 this.identityKeyPair = identityKeyPair;
42 this.localRegistrationId = localRegistrationId;
43 }
44
45 public void setResolver(final SignalServiceAddressResolver resolver) {
46 this.resolver = resolver;
47 }
48
49 private SignalServiceAddress resolveSignalServiceAddress(String identifier) {
50 if (resolver != null) {
51 return resolver.resolveSignalServiceAddress(identifier);
52 } else {
53 return Utils.getSignalServiceAddressFromIdentifier(identifier);
54 }
55 }
56
57 @Override
58 public IdentityKeyPair getIdentityKeyPair() {
59 return identityKeyPair;
60 }
61
62 @Override
63 public int getLocalRegistrationId() {
64 return localRegistrationId;
65 }
66
67 @Override
68 public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
69 return saveIdentity(resolveSignalServiceAddress(address.getName()),
70 identityKey,
71 TrustLevel.TRUSTED_UNVERIFIED,
72 null);
73 }
74
75 /**
76 * Adds the given identityKey for the user name and sets the trustLevel and added timestamp.
77 * If the identityKey already exists, the trustLevel and added timestamp are NOT updated.
78 *
79 * @param serviceAddress User address, i.e. phone number and/or uuid
80 * @param identityKey The user's public key
81 * @param trustLevel Level of trust: untrusted, trusted, trusted and verified
82 * @param added Added timestamp, if null and the key is newly added, the current time is used.
83 */
84 public boolean saveIdentity(
85 SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel, Date added
86 ) {
87 for (var id : identities) {
88 if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) {
89 continue;
90 }
91
92 if (!id.address.getUuid().isPresent() || !id.address.getNumber().isPresent()) {
93 id.address = serviceAddress;
94 }
95 // Identity already exists, not updating the trust level
96 return true;
97 }
98
99 identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, added != null ? added : new Date()));
100 return false;
101 }
102
103 /**
104 * Update trustLevel for the given identityKey for the user name.
105 *
106 * @param serviceAddress User address, i.e. phone number and/or uuid
107 * @param identityKey The user's public key
108 * @param trustLevel Level of trust: untrusted, trusted, trusted and verified
109 */
110 public void setIdentityTrustLevel(
111 SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel
112 ) {
113 for (var id : identities) {
114 if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) {
115 continue;
116 }
117
118 if (!id.address.getUuid().isPresent() || !id.address.getNumber().isPresent()) {
119 id.address = serviceAddress;
120 }
121 id.trustLevel = trustLevel;
122 return;
123 }
124
125 identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, new Date()));
126 }
127
128 @Override
129 public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
130 // TODO implement possibility for different handling of incoming/outgoing trust decisions
131 var serviceAddress = resolveSignalServiceAddress(address.getName());
132 var trustOnFirstUse = true;
133
134 for (var id : identities) {
135 if (!id.address.matches(serviceAddress)) {
136 continue;
137 }
138
139 if (id.identityKey.equals(identityKey)) {
140 return id.isTrusted();
141 } else {
142 trustOnFirstUse = false;
143 }
144 }
145
146 return trustOnFirstUse;
147 }
148
149 @Override
150 public IdentityKey getIdentity(SignalProtocolAddress address) {
151 var serviceAddress = resolveSignalServiceAddress(address.getName());
152 var identity = getIdentity(serviceAddress);
153 return identity == null ? null : identity.getIdentityKey();
154 }
155
156 public IdentityInfo getIdentity(SignalServiceAddress serviceAddress) {
157 long maxDate = 0;
158 IdentityInfo maxIdentity = null;
159 for (var id : this.identities) {
160 if (!id.address.matches(serviceAddress)) {
161 continue;
162 }
163
164 final var time = id.getDateAdded().getTime();
165 if (maxIdentity == null || maxDate <= time) {
166 maxDate = time;
167 maxIdentity = id;
168 }
169 }
170 return maxIdentity;
171 }
172
173 public List<IdentityInfo> getIdentities() {
174 // TODO deep copy
175 return identities;
176 }
177
178 public List<IdentityInfo> getIdentities(SignalServiceAddress serviceAddress) {
179 var identities = new ArrayList<IdentityInfo>();
180 for (var identity : this.identities) {
181 if (identity.address.matches(serviceAddress)) {
182 identities.add(identity);
183 }
184 }
185 return identities;
186 }
187
188 public static class JsonIdentityKeyStoreDeserializer extends JsonDeserializer<JsonIdentityKeyStore> {
189
190 @Override
191 public JsonIdentityKeyStore deserialize(
192 JsonParser jsonParser, DeserializationContext deserializationContext
193 ) throws IOException {
194 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
195
196 var localRegistrationId = node.get("registrationId").asInt();
197 var identityKeyPair = new IdentityKeyPair(Base64.getDecoder().decode(node.get("identityKey").asText()));
198
199 var keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
200
201 var trustedKeysNode = node.get("trustedKeys");
202 if (trustedKeysNode.isArray()) {
203 for (var trustedKey : trustedKeysNode) {
204 var trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null;
205
206 if (UuidUtil.isUuid(trustedKeyName)) {
207 // Ignore identities that were incorrectly created with UUIDs as name
208 continue;
209 }
210
211 var uuid = trustedKey.hasNonNull("uuid")
212 ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText())
213 : null;
214 final var serviceAddress = uuid == null
215 ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName)
216 : new SignalServiceAddress(uuid, trustedKeyName);
217 try {
218 var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0);
219 var trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get(
220 "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED;
221 var added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp")
222 .asLong()) : new Date();
223 keyStore.saveIdentity(serviceAddress, id, trustLevel, added);
224 } catch (InvalidKeyException e) {
225 logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage());
226 }
227 }
228 }
229
230 return keyStore;
231 }
232 }
233
234 public static class JsonIdentityKeyStoreSerializer extends JsonSerializer<JsonIdentityKeyStore> {
235
236 @Override
237 public void serialize(
238 JsonIdentityKeyStore jsonIdentityKeyStore, JsonGenerator json, SerializerProvider serializerProvider
239 ) throws IOException {
240 json.writeStartObject();
241 json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId());
242 json.writeStringField("identityKey",
243 Base64.getEncoder().encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().serialize()));
244 json.writeStringField("identityPrivateKey",
245 Base64.getEncoder()
246 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPrivateKey().serialize()));
247 json.writeStringField("identityPublicKey",
248 Base64.getEncoder()
249 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPublicKey().serialize()));
250 json.writeArrayFieldStart("trustedKeys");
251 for (var trustedKey : jsonIdentityKeyStore.identities) {
252 json.writeStartObject();
253 if (trustedKey.getAddress().getNumber().isPresent()) {
254 json.writeStringField("name", trustedKey.getAddress().getNumber().get());
255 }
256 if (trustedKey.getAddress().getUuid().isPresent()) {
257 json.writeStringField("uuid", trustedKey.getAddress().getUuid().get().toString());
258 }
259 json.writeStringField("identityKey",
260 Base64.getEncoder().encodeToString(trustedKey.identityKey.serialize()));
261 json.writeNumberField("trustLevel", trustedKey.trustLevel.ordinal());
262 json.writeNumberField("addedTimestamp", trustedKey.added.getTime());
263 json.writeEndObject();
264 }
265 json.writeEndArray();
266 json.writeEndObject();
267 }
268 }
269 }