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