X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/eabd361405a54a5b7122bf537cb299306f098e45..5845dad7690da09c696649544a1f396b0a01f080:/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java diff --git a/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java index c1ef428b..d71e3581 100644 --- a/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java @@ -7,15 +7,15 @@ import com.fasterxml.jackson.databind.*; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.IdentityKeyStore; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.*; class JsonIdentityKeyStore implements IdentityKeyStore { - private final Map trustedKeys = new HashMap<>(); + private final Map> trustedKeys = new HashMap<>(); private final IdentityKeyPair identityKeyPair; private final int localRegistrationId; @@ -26,10 +26,6 @@ class JsonIdentityKeyStore implements IdentityKeyStore { this.localRegistrationId = localRegistrationId; } - public void addTrustedKeys(Map keyMap) { - trustedKeys.putAll(keyMap); - } - @Override public IdentityKeyPair getIdentityKeyPair() { return identityKeyPair; @@ -41,14 +37,65 @@ class JsonIdentityKeyStore implements IdentityKeyStore { } @Override - public void saveIdentity(String name, IdentityKey identityKey) { - trustedKeys.put(name, identityKey); + public void saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + saveIdentity(address.getName(), identityKey, TrustLevel.TRUSTED_UNVERIFIED, null); + } + + /** + * Adds or updates the given identityKey for the user name and sets the trustLevel and added timestamp. + * + * @param name User name, i.e. phone number + * @param identityKey The user's public key + * @param trustLevel + * @param added Added timestamp, if null and the key is newly added, the current time is used. + */ + public void saveIdentity(String name, IdentityKey identityKey, TrustLevel trustLevel, Date added) { + List identities = trustedKeys.get(name); + if (identities == null) { + identities = new ArrayList<>(); + trustedKeys.put(name, identities); + } else { + for (Identity id : identities) { + if (!id.identityKey.equals(identityKey)) + continue; + + if (id.trustLevel.compareTo(trustLevel) < 0) { + id.trustLevel = trustLevel; + } + if (added != null) { + id.added = added; + } + return; + } + } + identities.add(new Identity(identityKey, trustLevel, added != null ? added : new Date())); } @Override - public boolean isTrustedIdentity(String name, IdentityKey identityKey) { - IdentityKey trusted = trustedKeys.get(name); - return (trusted == null || trusted.equals(identityKey)); + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + List identities = trustedKeys.get(address.getName()); + if (identities == null) { + // Trust on first use + return true; + } + + for (Identity id : identities) { + if (id.identityKey.equals(identityKey)) { + return id.isTrusted(); + } + } + + return false; + } + + public Map> getIdentities() { + // TODO deep copy + return trustedKeys; + } + + public List getIdentities(String name) { + // TODO deep copy + return trustedKeys.get(name); } public static class JsonIdentityKeyStoreDeserializer extends JsonDeserializer { @@ -62,24 +109,24 @@ class JsonIdentityKeyStore implements IdentityKeyStore { IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.decode(node.get("identityKey").asText())); - Map trustedKeyMap = new HashMap<>(); + JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); + JsonNode trustedKeysNode = node.get("trustedKeys"); if (trustedKeysNode.isArray()) { for (JsonNode trustedKey : trustedKeysNode) { String trustedKeyName = trustedKey.get("name").asText(); try { - trustedKeyMap.put(trustedKeyName, new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0)); + IdentityKey id = new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0); + TrustLevel trustLevel = trustedKey.has("trustLevel") ? TrustLevel.fromInt(trustedKey.get("trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; + Date added = trustedKey.has("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp").asLong()) : new Date(); + keyStore.saveIdentity(trustedKeyName, id, trustLevel, added); } catch (InvalidKeyException | IOException e) { System.out.println(String.format("Error while decoding key for: %s", trustedKeyName)); } } } - JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); - keyStore.addTrustedKeys(trustedKeyMap); - return keyStore; - } catch (InvalidKeyException e) { throw new IOException(e); } @@ -94,14 +141,45 @@ class JsonIdentityKeyStore implements IdentityKeyStore { json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId()); json.writeStringField("identityKey", Base64.encodeBytes(jsonIdentityKeyStore.getIdentityKeyPair().serialize())); json.writeArrayFieldStart("trustedKeys"); - for (Map.Entry trustedKey : jsonIdentityKeyStore.trustedKeys.entrySet()) { - json.writeStartObject(); - json.writeStringField("name", trustedKey.getKey()); - json.writeStringField("identityKey", Base64.encodeBytes(trustedKey.getValue().serialize())); - json.writeEndObject(); + for (Map.Entry> trustedKey : jsonIdentityKeyStore.trustedKeys.entrySet()) { + for (Identity id : trustedKey.getValue()) { + json.writeStartObject(); + json.writeStringField("name", trustedKey.getKey()); + json.writeStringField("identityKey", Base64.encodeBytes(id.identityKey.serialize())); + json.writeNumberField("trustLevel", id.trustLevel.ordinal()); + json.writeNumberField("addedTimestamp", id.added.getTime()); + json.writeEndObject(); + } } json.writeEndArray(); json.writeEndObject(); } } + + public class Identity { + IdentityKey identityKey; + TrustLevel trustLevel; + Date added; + + public Identity(IdentityKey identityKey, TrustLevel trustLevel) { + this.identityKey = identityKey; + this.trustLevel = trustLevel; + this.added = new Date(); + } + + public Identity(IdentityKey identityKey, TrustLevel trustLevel, Date added) { + this.identityKey = identityKey; + this.trustLevel = trustLevel; + this.added = added; + } + + public boolean isTrusted() { + return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || + trustLevel == TrustLevel.TRUSTED_VERIFIED; + } + + public byte[] getFingerprint() { + return identityKey.getPublicKey().serialize(); + } + } }