1 package org
.asamk
.signal
.storage
.protocol
;
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
;
11 import org
.asamk
.signal
.manager
.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
;
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
;
28 public class JsonIdentityKeyStore
implements IdentityKeyStore
{
30 private final List
<Identity
> identities
= new ArrayList
<>();
32 private final IdentityKeyPair identityKeyPair
;
33 private final int localRegistrationId
;
35 private SignalServiceAddressResolver resolver
;
37 public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair
, int localRegistrationId
) {
38 this.identityKeyPair
= identityKeyPair
;
39 this.localRegistrationId
= localRegistrationId
;
42 public void setResolver(final SignalServiceAddressResolver resolver
) {
43 this.resolver
= resolver
;
46 private SignalServiceAddress
resolveSignalServiceAddress(String identifier
) {
47 if (resolver
!= null) {
48 return resolver
.resolveSignalServiceAddress(identifier
);
50 return Util
.getSignalServiceAddressFromIdentifier(identifier
);
55 public IdentityKeyPair
getIdentityKeyPair() {
56 return identityKeyPair
;
60 public int getLocalRegistrationId() {
61 return localRegistrationId
;
65 public boolean saveIdentity(SignalProtocolAddress address
, IdentityKey identityKey
) {
66 return saveIdentity(resolveSignalServiceAddress(address
.getName()),
68 TrustLevel
.TRUSTED_UNVERIFIED
,
73 * Adds the given identityKey for the user name and sets the trustLevel and added timestamp.
74 * If the identityKey already exists, the trustLevel and added timestamp are NOT updated.
76 * @param serviceAddress User address, i.e. phone number and/or uuid
77 * @param identityKey The user's public key
78 * @param trustLevel Level of trust: untrusted, trusted, trusted and verified
79 * @param added Added timestamp, if null and the key is newly added, the current time is used.
81 public boolean saveIdentity(
82 SignalServiceAddress serviceAddress
, IdentityKey identityKey
, TrustLevel trustLevel
, Date added
84 for (Identity id
: identities
) {
85 if (!id
.address
.matches(serviceAddress
) || !id
.identityKey
.equals(identityKey
)) {
89 if (!id
.address
.getUuid().isPresent() || !id
.address
.getNumber().isPresent()) {
90 id
.address
= serviceAddress
;
92 // Identity already exists, not updating the trust level
96 identities
.add(new Identity(serviceAddress
, identityKey
, trustLevel
, added
!= null ? added
: new Date()));
101 * Update trustLevel for the given identityKey for the user name.
103 * @param serviceAddress User address, i.e. phone number and/or uuid
104 * @param identityKey The user's public key
105 * @param trustLevel Level of trust: untrusted, trusted, trusted and verified
107 public void setIdentityTrustLevel(
108 SignalServiceAddress serviceAddress
, IdentityKey identityKey
, TrustLevel trustLevel
110 for (Identity id
: identities
) {
111 if (!id
.address
.matches(serviceAddress
) || !id
.identityKey
.equals(identityKey
)) {
115 if (!id
.address
.getUuid().isPresent() || !id
.address
.getNumber().isPresent()) {
116 id
.address
= serviceAddress
;
118 id
.trustLevel
= trustLevel
;
122 identities
.add(new Identity(serviceAddress
, identityKey
, trustLevel
, new Date()));
126 public boolean isTrustedIdentity(SignalProtocolAddress address
, IdentityKey identityKey
, Direction direction
) {
127 // TODO implement possibility for different handling of incoming/outgoing trust decisions
128 SignalServiceAddress serviceAddress
= resolveSignalServiceAddress(address
.getName());
129 boolean trustOnFirstUse
= true;
131 for (Identity id
: identities
) {
132 if (!id
.address
.matches(serviceAddress
)) {
136 if (id
.identityKey
.equals(identityKey
)) {
137 return id
.isTrusted();
139 trustOnFirstUse
= false;
143 return trustOnFirstUse
;
147 public IdentityKey
getIdentity(SignalProtocolAddress address
) {
148 SignalServiceAddress serviceAddress
= resolveSignalServiceAddress(address
.getName());
149 Identity identity
= getIdentity(serviceAddress
);
150 return identity
== null ?
null : identity
.getIdentityKey();
153 public Identity
getIdentity(SignalServiceAddress serviceAddress
) {
155 Identity maxIdentity
= null;
156 for (Identity id
: this.identities
) {
157 if (!id
.address
.matches(serviceAddress
)) {
161 final long time
= id
.getDateAdded().getTime();
162 if (maxIdentity
== null || maxDate
<= time
) {
170 public List
<Identity
> getIdentities() {
175 public List
<Identity
> getIdentities(SignalServiceAddress serviceAddress
) {
176 List
<Identity
> identities
= new ArrayList
<>();
177 for (Identity identity
: this.identities
) {
178 if (identity
.address
.matches(serviceAddress
)) {
179 identities
.add(identity
);
185 public static class JsonIdentityKeyStoreDeserializer
extends JsonDeserializer
<JsonIdentityKeyStore
> {
188 public JsonIdentityKeyStore
deserialize(
189 JsonParser jsonParser
, DeserializationContext deserializationContext
190 ) throws IOException
{
191 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
194 int localRegistrationId
= node
.get("registrationId").asInt();
195 IdentityKeyPair identityKeyPair
= new IdentityKeyPair(Base64
.decode(node
.get("identityKey").asText()));
197 JsonIdentityKeyStore keyStore
= new JsonIdentityKeyStore(identityKeyPair
, localRegistrationId
);
199 JsonNode trustedKeysNode
= node
.get("trustedKeys");
200 if (trustedKeysNode
.isArray()) {
201 for (JsonNode trustedKey
: trustedKeysNode
) {
202 String trustedKeyName
= trustedKey
.hasNonNull("name") ? trustedKey
.get("name").asText() : null;
204 if (UuidUtil
.isUuid(trustedKeyName
)) {
205 // Ignore identities that were incorrectly created with UUIDs as name
209 UUID uuid
= trustedKey
.hasNonNull("uuid") ? UuidUtil
.parseOrNull(trustedKey
.get("uuid")
211 final SignalServiceAddress serviceAddress
= uuid
== null
212 ? Util
.getSignalServiceAddressFromIdentifier(trustedKeyName
)
213 : new SignalServiceAddress(uuid
, trustedKeyName
);
215 IdentityKey id
= new IdentityKey(Base64
.decode(trustedKey
.get("identityKey").asText()), 0);
216 TrustLevel trustLevel
= trustedKey
.has("trustLevel") ? TrustLevel
.fromInt(trustedKey
.get(
217 "trustLevel").asInt()) : TrustLevel
.TRUSTED_UNVERIFIED
;
218 Date added
= trustedKey
.has("addedTimestamp") ?
new Date(trustedKey
.get("addedTimestamp")
219 .asLong()) : new Date();
220 keyStore
.saveIdentity(serviceAddress
, id
, trustLevel
, added
);
221 } catch (InvalidKeyException
| IOException e
) {
222 System
.out
.println(String
.format("Error while decoding key for: %s", trustedKeyName
));
228 } catch (InvalidKeyException e
) {
229 throw new IOException(e
);
234 public static class JsonIdentityKeyStoreSerializer
extends JsonSerializer
<JsonIdentityKeyStore
> {
237 public void serialize(
238 JsonIdentityKeyStore jsonIdentityKeyStore
, JsonGenerator json
, SerializerProvider serializerProvider
239 ) throws IOException
{
240 json
.writeStartObject();
241 json
.writeNumberField("registrationId", jsonIdentityKeyStore
.getLocalRegistrationId());
242 json
.writeStringField("identityKey",
243 Base64
.encodeBytes(jsonIdentityKeyStore
.getIdentityKeyPair().serialize()));
244 json
.writeArrayFieldStart("trustedKeys");
245 for (Identity trustedKey
: jsonIdentityKeyStore
.identities
) {
246 json
.writeStartObject();
247 if (trustedKey
.getAddress().getNumber().isPresent()) {
248 json
.writeStringField("name", trustedKey
.getAddress().getNumber().get());
250 if (trustedKey
.getAddress().getUuid().isPresent()) {
251 json
.writeStringField("uuid", trustedKey
.getAddress().getUuid().get().toString());
253 json
.writeStringField("identityKey", Base64
.encodeBytes(trustedKey
.identityKey
.serialize()));
254 json
.writeNumberField("trustLevel", trustedKey
.trustLevel
.ordinal());
255 json
.writeNumberField("addedTimestamp", trustedKey
.added
.getTime());
256 json
.writeEndObject();
258 json
.writeEndArray();
259 json
.writeEndObject();
263 public static class Identity
{
265 SignalServiceAddress address
;
266 IdentityKey identityKey
;
267 TrustLevel trustLevel
;
270 public Identity(SignalServiceAddress address
, IdentityKey identityKey
, TrustLevel trustLevel
) {
271 this.address
= address
;
272 this.identityKey
= identityKey
;
273 this.trustLevel
= trustLevel
;
274 this.added
= new Date();
277 Identity(SignalServiceAddress address
, IdentityKey identityKey
, TrustLevel trustLevel
, Date added
) {
278 this.address
= address
;
279 this.identityKey
= identityKey
;
280 this.trustLevel
= trustLevel
;
284 public SignalServiceAddress
getAddress() {
288 public void setAddress(final SignalServiceAddress address
) {
289 this.address
= address
;
292 boolean isTrusted() {
293 return trustLevel
== TrustLevel
.TRUSTED_UNVERIFIED
|| trustLevel
== TrustLevel
.TRUSTED_VERIFIED
;
296 public IdentityKey
getIdentityKey() {
297 return this.identityKey
;
300 public TrustLevel
getTrustLevel() {
301 return this.trustLevel
;
304 public Date
getDateAdded() {
308 public byte[] getFingerprint() {
309 return identityKey
.getPublicKey().serialize();