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