]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java
9f01b719f8f72ef74df86eb6973249e4d7f9c5e8
[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 int localRegistrationId = node.get("registrationId").asInt();
198 IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.getDecoder()
199 .decode(node.get("identityKey").asText()));
200
201 JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
202
203 JsonNode trustedKeysNode = node.get("trustedKeys");
204 if (trustedKeysNode.isArray()) {
205 for (JsonNode trustedKey : trustedKeysNode) {
206 String trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null;
207
208 if (UuidUtil.isUuid(trustedKeyName)) {
209 // Ignore identities that were incorrectly created with UUIDs as name
210 continue;
211 }
212
213 UUID uuid = trustedKey.hasNonNull("uuid")
214 ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText())
215 : 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.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get(
223 "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED;
224 Date added = trustedKey.hasNonNull("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 }
235 }
236
237 public static class JsonIdentityKeyStoreSerializer extends JsonSerializer<JsonIdentityKeyStore> {
238
239 @Override
240 public void serialize(
241 JsonIdentityKeyStore jsonIdentityKeyStore, JsonGenerator json, SerializerProvider serializerProvider
242 ) throws IOException {
243 json.writeStartObject();
244 json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId());
245 json.writeStringField("identityKey",
246 Base64.getEncoder().encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().serialize()));
247 json.writeStringField("identityPrivateKey",
248 Base64.getEncoder()
249 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPrivateKey().serialize()));
250 json.writeStringField("identityPublicKey",
251 Base64.getEncoder()
252 .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPublicKey().serialize()));
253 json.writeArrayFieldStart("trustedKeys");
254 for (IdentityInfo trustedKey : jsonIdentityKeyStore.identities) {
255 json.writeStartObject();
256 if (trustedKey.getAddress().getNumber().isPresent()) {
257 json.writeStringField("name", trustedKey.getAddress().getNumber().get());
258 }
259 if (trustedKey.getAddress().getUuid().isPresent()) {
260 json.writeStringField("uuid", trustedKey.getAddress().getUuid().get().toString());
261 }
262 json.writeStringField("identityKey",
263 Base64.getEncoder().encodeToString(trustedKey.identityKey.serialize()));
264 json.writeNumberField("trustLevel", trustedKey.trustLevel.ordinal());
265 json.writeNumberField("addedTimestamp", trustedKey.added.getTime());
266 json.writeEndObject();
267 }
268 json.writeEndArray();
269 json.writeEndObject();
270 }
271 }
272 }