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