]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java
561138c6105f5cf6b7599b78627c52c9cc501860
[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 public void removeIdentity(SignalServiceAddress serviceAddress, IdentityKey identityKey) {
129 identities.removeIf(id -> id.address.matches(serviceAddress) && id.identityKey.equals(identityKey));
130 }
131
132 @Override
133 public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
134 // TODO implement possibility for different handling of incoming/outgoing trust decisions
135 var serviceAddress = resolveSignalServiceAddress(address.getName());
136 var trustOnFirstUse = true;
137
138 for (var id : identities) {
139 if (!id.address.matches(serviceAddress)) {
140 continue;
141 }
142
143 if (id.identityKey.equals(identityKey)) {
144 return id.isTrusted();
145 } else {
146 trustOnFirstUse = false;
147 }
148 }
149
150 if (!trustOnFirstUse) {
151 saveIdentity(resolveSignalServiceAddress(address.getName()), identityKey, TrustLevel.UNTRUSTED, null);
152 }
153
154 return trustOnFirstUse;
155 }
156
157 @Override
158 public IdentityKey getIdentity(SignalProtocolAddress address) {
159 var serviceAddress = resolveSignalServiceAddress(address.getName());
160 var identity = getIdentity(serviceAddress);
161 return identity == null ? null : identity.getIdentityKey();
162 }
163
164 public IdentityInfo getIdentity(SignalServiceAddress serviceAddress) {
165 long maxDate = 0;
166 IdentityInfo maxIdentity = null;
167 for (var id : this.identities) {
168 if (!id.address.matches(serviceAddress)) {
169 continue;
170 }
171
172 final var time = id.getDateAdded().getTime();
173 if (maxIdentity == null || maxDate <= time) {
174 maxDate = time;
175 maxIdentity = id;
176 }
177 }
178 return maxIdentity;
179 }
180
181 public List<IdentityInfo> getIdentities() {
182 // TODO deep copy
183 return identities;
184 }
185
186 public List<IdentityInfo> getIdentities(SignalServiceAddress serviceAddress) {
187 var identities = new ArrayList<IdentityInfo>();
188 for (var identity : this.identities) {
189 if (identity.address.matches(serviceAddress)) {
190 identities.add(identity);
191 }
192 }
193 return identities;
194 }
195
196 public static class JsonIdentityKeyStoreDeserializer extends JsonDeserializer<JsonIdentityKeyStore> {
197
198 @Override
199 public JsonIdentityKeyStore deserialize(
200 JsonParser jsonParser, DeserializationContext deserializationContext
201 ) throws IOException {
202 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
203
204 var localRegistrationId = node.get("registrationId").asInt();
205 var identityKeyPair = new IdentityKeyPair(Base64.getDecoder().decode(node.get("identityKey").asText()));
206
207 var keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
208
209 var trustedKeysNode = node.get("trustedKeys");
210 if (trustedKeysNode.isArray()) {
211 for (var trustedKey : trustedKeysNode) {
212 var trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null;
213
214 if (UuidUtil.isUuid(trustedKeyName)) {
215 // Ignore identities that were incorrectly created with UUIDs as name
216 continue;
217 }
218
219 var uuid = trustedKey.hasNonNull("uuid")
220 ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText())
221 : null;
222 final var serviceAddress = uuid == null
223 ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName)
224 : new SignalServiceAddress(uuid, trustedKeyName);
225 try {
226 var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0);
227 var trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get(
228 "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED;
229 var added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp")
230 .asLong()) : new Date();
231 keyStore.saveIdentity(serviceAddress, id, trustLevel, added);
232 } catch (InvalidKeyException e) {
233 logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage());
234 }
235 }
236 }
237
238 return keyStore;
239 }
240 }
241
242 public static class JsonIdentityKeyStoreSerializer extends JsonSerializer<JsonIdentityKeyStore> {
243
244 @Override
245 public void serialize(
246 JsonIdentityKeyStore jsonIdentityKeyStore, JsonGenerator json, SerializerProvider serializerProvider
247 ) throws IOException {
248 json.writeStartObject();
249 json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId());
250 json.writeStringField("identityKey",
251 Base64.getEncoder().encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().serialize()));
252 json.writeStringField("identityPrivateKey",
253 Base64.getEncoder()
254 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPrivateKey().serialize()));
255 json.writeStringField("identityPublicKey",
256 Base64.getEncoder()
257 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPublicKey().serialize()));
258 json.writeArrayFieldStart("trustedKeys");
259 for (var trustedKey : jsonIdentityKeyStore.identities) {
260 json.writeStartObject();
261 if (trustedKey.getAddress().getNumber().isPresent()) {
262 json.writeStringField("name", trustedKey.getAddress().getNumber().get());
263 }
264 if (trustedKey.getAddress().getUuid().isPresent()) {
265 json.writeStringField("uuid", trustedKey.getAddress().getUuid().get().toString());
266 }
267 json.writeStringField("identityKey",
268 Base64.getEncoder().encodeToString(trustedKey.identityKey.serialize()));
269 json.writeNumberField("trustLevel", trustedKey.trustLevel.ordinal());
270 json.writeNumberField("addedTimestamp", trustedKey.added.getTime());
271 json.writeEndObject();
272 }
273 json.writeEndArray();
274 json.writeEndObject();
275 }
276 }
277 }