]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/storage/protocol/JsonIdentityKeyStore.java
d7049e4a690d4bca56a2c077718987f36bee8e6b
[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.*;
6 import org.asamk.signal.TrustLevel;
7 import org.whispersystems.libsignal.IdentityKey;
8 import org.whispersystems.libsignal.IdentityKeyPair;
9 import org.whispersystems.libsignal.InvalidKeyException;
10 import org.whispersystems.libsignal.SignalProtocolAddress;
11 import org.whispersystems.libsignal.state.IdentityKeyStore;
12 import org.whispersystems.signalservice.internal.util.Base64;
13
14 import java.io.IOException;
15 import java.util.*;
16
17 public class JsonIdentityKeyStore implements IdentityKeyStore {
18
19 private final Map<String, List<Identity>> trustedKeys = new HashMap<>();
20
21 private final IdentityKeyPair identityKeyPair;
22 private final int localRegistrationId;
23
24 public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) {
25 this.identityKeyPair = identityKeyPair;
26 this.localRegistrationId = localRegistrationId;
27 }
28
29 @Override
30 public IdentityKeyPair getIdentityKeyPair() {
31 return identityKeyPair;
32 }
33
34 @Override
35 public int getLocalRegistrationId() {
36 return localRegistrationId;
37 }
38
39 @Override
40 public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
41 return saveIdentity(address.getName(), identityKey, TrustLevel.TRUSTED_UNVERIFIED, null);
42 }
43
44 /**
45 * Adds or updates the given identityKey for the user name and sets the trustLevel and added timestamp.
46 *
47 * @param name User name, i.e. phone number
48 * @param identityKey The user's public key
49 * @param trustLevel
50 * @param added Added timestamp, if null and the key is newly added, the current time is used.
51 */
52 public boolean saveIdentity(String name, IdentityKey identityKey, TrustLevel trustLevel, Date added) {
53 List<Identity> identities = trustedKeys.get(name);
54 if (identities == null) {
55 identities = new ArrayList<>();
56 trustedKeys.put(name, identities);
57 } else {
58 for (Identity id : identities) {
59 if (!id.identityKey.equals(identityKey))
60 continue;
61
62 if (id.trustLevel.compareTo(trustLevel) < 0) {
63 id.trustLevel = trustLevel;
64 }
65 if (added != null) {
66 id.added = added;
67 }
68 return true;
69 }
70 }
71 identities.add(new Identity(identityKey, trustLevel, added != null ? added : new Date()));
72 return false;
73 }
74
75 @Override
76 public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
77 // TODO implement possibility for different handling of incoming/outgoing trust decisions
78 List<Identity> identities = trustedKeys.get(address.getName());
79 if (identities == null) {
80 // Trust on first use
81 return true;
82 }
83
84 for (Identity id : identities) {
85 if (id.identityKey.equals(identityKey)) {
86 return id.isTrusted();
87 }
88 }
89
90 return false;
91 }
92
93 @Override
94 public IdentityKey getIdentity(SignalProtocolAddress address) {
95 List<Identity> identities = trustedKeys.get(address.getName());
96 if (identities == null || identities.size() == 0) {
97 return null;
98 }
99
100 long maxDate = 0;
101 Identity maxIdentity = null;
102 for (Identity id : identities) {
103 final long time = id.getDateAdded().getTime();
104 if (maxIdentity == null || maxDate <= time) {
105 maxDate = time;
106 maxIdentity = id;
107 }
108 }
109 return maxIdentity.getIdentityKey();
110 }
111
112 public Map<String, List<Identity>> getIdentities() {
113 // TODO deep copy
114 return trustedKeys;
115 }
116
117 public List<Identity> getIdentities(String name) {
118 // TODO deep copy
119 return trustedKeys.get(name);
120 }
121
122 public static class JsonIdentityKeyStoreDeserializer extends JsonDeserializer<JsonIdentityKeyStore> {
123
124 @Override
125 public JsonIdentityKeyStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
126 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
127
128 try {
129 int localRegistrationId = node.get("registrationId").asInt();
130 IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.decode(node.get("identityKey").asText()));
131
132 JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
133
134 JsonNode trustedKeysNode = node.get("trustedKeys");
135 if (trustedKeysNode.isArray()) {
136 for (JsonNode trustedKey : trustedKeysNode) {
137 String trustedKeyName = trustedKey.get("name").asText();
138 try {
139 IdentityKey id = new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0);
140 TrustLevel trustLevel = trustedKey.has("trustLevel") ? TrustLevel.fromInt(trustedKey.get("trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED;
141 Date added = trustedKey.has("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp").asLong()) : new Date();
142 keyStore.saveIdentity(trustedKeyName, id, trustLevel, added);
143 } catch (InvalidKeyException | IOException e) {
144 System.out.println(String.format("Error while decoding key for: %s", trustedKeyName));
145 }
146 }
147 }
148
149 return keyStore;
150 } catch (InvalidKeyException e) {
151 throw new IOException(e);
152 }
153 }
154 }
155
156 public static class JsonIdentityKeyStoreSerializer extends JsonSerializer<JsonIdentityKeyStore> {
157
158 @Override
159 public void serialize(JsonIdentityKeyStore jsonIdentityKeyStore, JsonGenerator json, SerializerProvider serializerProvider) throws IOException {
160 json.writeStartObject();
161 json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId());
162 json.writeStringField("identityKey", Base64.encodeBytes(jsonIdentityKeyStore.getIdentityKeyPair().serialize()));
163 json.writeArrayFieldStart("trustedKeys");
164 for (Map.Entry<String, List<Identity>> trustedKey : jsonIdentityKeyStore.trustedKeys.entrySet()) {
165 for (Identity id : trustedKey.getValue()) {
166 json.writeStartObject();
167 json.writeStringField("name", trustedKey.getKey());
168 json.writeStringField("identityKey", Base64.encodeBytes(id.identityKey.serialize()));
169 json.writeNumberField("trustLevel", id.trustLevel.ordinal());
170 json.writeNumberField("addedTimestamp", id.added.getTime());
171 json.writeEndObject();
172 }
173 }
174 json.writeEndArray();
175 json.writeEndObject();
176 }
177 }
178
179 public class Identity {
180
181 IdentityKey identityKey;
182 TrustLevel trustLevel;
183 Date added;
184
185 public Identity(IdentityKey identityKey, TrustLevel trustLevel) {
186 this.identityKey = identityKey;
187 this.trustLevel = trustLevel;
188 this.added = new Date();
189 }
190
191 Identity(IdentityKey identityKey, TrustLevel trustLevel, Date added) {
192 this.identityKey = identityKey;
193 this.trustLevel = trustLevel;
194 this.added = added;
195 }
196
197 boolean isTrusted() {
198 return trustLevel == TrustLevel.TRUSTED_UNVERIFIED ||
199 trustLevel == TrustLevel.TRUSTED_VERIFIED;
200 }
201
202 public IdentityKey getIdentityKey() {
203 return this.identityKey;
204 }
205
206 public TrustLevel getTrustLevel() {
207 return this.trustLevel;
208 }
209
210 public Date getDateAdded() {
211 return this.added;
212 }
213
214 public byte[] getFingerprint() {
215 return identityKey.getPublicKey().serialize();
216 }
217 }
218 }