1 package org
.asamk
.signal
.manager
.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
.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
;
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
;
29 public class JsonIdentityKeyStore
implements IdentityKeyStore
{
31 private final static Logger logger
= LoggerFactory
.getLogger(JsonIdentityKeyStore
.class);
33 private final List
<IdentityInfo
> identities
= new ArrayList
<>();
35 private final IdentityKeyPair identityKeyPair
;
36 private final int localRegistrationId
;
38 private SignalServiceAddressResolver resolver
;
40 public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair
, int localRegistrationId
) {
41 this.identityKeyPair
= identityKeyPair
;
42 this.localRegistrationId
= localRegistrationId
;
45 public void setResolver(final SignalServiceAddressResolver resolver
) {
46 this.resolver
= resolver
;
49 private SignalServiceAddress
resolveSignalServiceAddress(String identifier
) {
50 if (resolver
!= null) {
51 return resolver
.resolveSignalServiceAddress(identifier
);
53 return Utils
.getSignalServiceAddressFromIdentifier(identifier
);
58 public IdentityKeyPair
getIdentityKeyPair() {
59 return identityKeyPair
;
63 public int getLocalRegistrationId() {
64 return localRegistrationId
;
68 public boolean saveIdentity(SignalProtocolAddress address
, IdentityKey identityKey
) {
69 return saveIdentity(resolveSignalServiceAddress(address
.getName()),
71 TrustLevel
.TRUSTED_UNVERIFIED
,
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.
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.
84 public boolean saveIdentity(
85 SignalServiceAddress serviceAddress
, IdentityKey identityKey
, TrustLevel trustLevel
, Date added
87 for (var id
: identities
) {
88 if (!id
.address
.matches(serviceAddress
) || !id
.identityKey
.equals(identityKey
)) {
92 if (!id
.address
.getUuid().isPresent() || !id
.address
.getNumber().isPresent()) {
93 id
.address
= serviceAddress
;
95 // Identity already exists, not updating the trust level
99 identities
.add(new IdentityInfo(serviceAddress
, identityKey
, trustLevel
, added
!= null ? added
: new Date()));
104 * Update trustLevel for the given identityKey for the user name.
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
110 public void setIdentityTrustLevel(
111 SignalServiceAddress serviceAddress
, IdentityKey identityKey
, TrustLevel trustLevel
113 for (var id
: identities
) {
114 if (!id
.address
.matches(serviceAddress
) || !id
.identityKey
.equals(identityKey
)) {
118 if (!id
.address
.getUuid().isPresent() || !id
.address
.getNumber().isPresent()) {
119 id
.address
= serviceAddress
;
121 id
.trustLevel
= trustLevel
;
125 identities
.add(new IdentityInfo(serviceAddress
, identityKey
, trustLevel
, new Date()));
128 public void removeIdentity(SignalServiceAddress serviceAddress
, IdentityKey identityKey
) {
129 identities
.removeIf(id
-> id
.address
.matches(serviceAddress
) && id
.identityKey
.equals(identityKey
));
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;
138 for (var id
: identities
) {
139 if (!id
.address
.matches(serviceAddress
)) {
143 if (id
.identityKey
.equals(identityKey
)) {
144 return id
.isTrusted();
146 trustOnFirstUse
= false;
150 if (!trustOnFirstUse
) {
151 saveIdentity(resolveSignalServiceAddress(address
.getName()), identityKey
, TrustLevel
.UNTRUSTED
, null);
154 return trustOnFirstUse
;
158 public IdentityKey
getIdentity(SignalProtocolAddress address
) {
159 var serviceAddress
= resolveSignalServiceAddress(address
.getName());
160 var identity
= getIdentity(serviceAddress
);
161 return identity
== null ?
null : identity
.getIdentityKey();
164 public IdentityInfo
getIdentity(SignalServiceAddress serviceAddress
) {
166 IdentityInfo maxIdentity
= null;
167 for (var id
: this.identities
) {
168 if (!id
.address
.matches(serviceAddress
)) {
172 final var time
= id
.getDateAdded().getTime();
173 if (maxIdentity
== null || maxDate
<= time
) {
181 public List
<IdentityInfo
> getIdentities() {
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
);
196 public static class JsonIdentityKeyStoreDeserializer
extends JsonDeserializer
<JsonIdentityKeyStore
> {
199 public JsonIdentityKeyStore
deserialize(
200 JsonParser jsonParser
, DeserializationContext deserializationContext
201 ) throws IOException
{
202 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
204 var localRegistrationId
= node
.get("registrationId").asInt();
205 var identityKeyPair
= new IdentityKeyPair(Base64
.getDecoder().decode(node
.get("identityKey").asText()));
207 var keyStore
= new JsonIdentityKeyStore(identityKeyPair
, localRegistrationId
);
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;
214 if (UuidUtil
.isUuid(trustedKeyName
)) {
215 // Ignore identities that were incorrectly created with UUIDs as name
219 var uuid
= trustedKey
.hasNonNull("uuid")
220 ? UuidUtil
.parseOrNull(trustedKey
.get("uuid").asText())
222 final var serviceAddress
= uuid
== null
223 ? Utils
.getSignalServiceAddressFromIdentifier(trustedKeyName
)
224 : new SignalServiceAddress(uuid
, trustedKeyName
);
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());
242 public static class JsonIdentityKeyStoreSerializer
extends JsonSerializer
<JsonIdentityKeyStore
> {
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",
254 .encodeToString(jsonIdentityKeyStore
.getIdentityKeyPair().getPrivateKey().serialize()));
255 json
.writeStringField("identityPublicKey",
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());
264 if (trustedKey
.getAddress().getUuid().isPresent()) {
265 json
.writeStringField("uuid", trustedKey
.getAddress().getUuid().get().toString());
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();
273 json
.writeEndArray();
274 json
.writeEndObject();