From: AsamK Date: Thu, 14 Jul 2016 13:35:59 +0000 (+0200) Subject: Implement listIdentities and trust commands X-Git-Tag: v0.5.0~12 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/f095d947f892830e1ced4af450df77a8f7ae6d5d Implement listIdentities and trust commands Print the fingerprints of all known phone numbers and can set their trust --- diff --git a/src/main/java/org/asamk/signal/Hex.java b/src/main/java/org/asamk/signal/Hex.java index 43d77cc5..696ca62b 100644 --- a/src/main/java/org/asamk/signal/Hex.java +++ b/src/main/java/org/asamk/signal/Hex.java @@ -19,4 +19,13 @@ public class Hex { buf.append(HEX_DIGITS[b & 0xf]); buf.append(" "); } + + public static byte[] toByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } } diff --git a/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java index d4d0ea3e..7cde350c 100644 --- a/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/JsonIdentityKeyStore.java @@ -85,6 +85,16 @@ class JsonIdentityKeyStore implements IdentityKeyStore { 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 { @Override @@ -165,8 +175,8 @@ class JsonIdentityKeyStore implements IdentityKeyStore { trustLevel == TrustLevel.TRUSTED_VERIFIED; } - public String getFingerprint() { - return Hex.toStringCondensed(identityKey.getPublicKey().serialize()); + public byte[] getFingerprint() { + return identityKey.getPublicKey().serialize(); } } } diff --git a/src/main/java/org/asamk/signal/JsonSignalProtocolStore.java b/src/main/java/org/asamk/signal/JsonSignalProtocolStore.java index 015707ae..a3159e48 100644 --- a/src/main/java/org/asamk/signal/JsonSignalProtocolStore.java +++ b/src/main/java/org/asamk/signal/JsonSignalProtocolStore.java @@ -13,6 +13,7 @@ import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import java.util.List; +import java.util.Map; class JsonSignalProtocolStore implements SignalProtocolStore { @@ -72,6 +73,14 @@ class JsonSignalProtocolStore implements SignalProtocolStore { identityKeyStore.saveIdentity(name, identityKey, trustLevel, null); } + public Map> getIdentities() { + return identityKeyStore.getIdentities(); + } + + public List getIdentities(String name) { + return identityKeyStore.getIdentities(name); + } + @Override public boolean isTrustedIdentity(String name, IdentityKey identityKey) { return identityKeyStore.isTrustedIdentity(name, identityKey); diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 3b76a2b8..25807a6b 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -48,6 +48,8 @@ import java.nio.charset.Charset; import java.security.Security; import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.concurrent.TimeoutException; public class Main { @@ -436,6 +438,65 @@ public class Main { return 3; } + break; + case "listIdentities": + if (dBusConn != null) { + System.err.println("listIdentities is not yet implemented via dbus"); + return 1; + } + if (!m.isRegistered()) { + System.err.println("User is not registered."); + return 1; + } + if (ns.get("number") == null) { + for (Map.Entry> keys : m.getIdentities().entrySet()) { + for (JsonIdentityKeyStore.Identity id : keys.getValue()) { + System.out.println(String.format("%s: %s Added: %s Fingerprint: %s", keys.getKey(), id.trustLevel, id.added, Hex.toStringCondensed(id.getFingerprint()))); + } + } + } else { + String number = ns.getString("number"); + for (JsonIdentityKeyStore.Identity id : m.getIdentities(number)) { + System.out.println(String.format("%s: %s Added: %s Fingerprint: %s", number, id.trustLevel, id.added, Hex.toStringCondensed(id.getFingerprint()))); + } + } + break; + case "trust": + if (dBusConn != null) { + System.err.println("trust is not yet implemented via dbus"); + return 1; + } + if (!m.isRegistered()) { + System.err.println("User is not registered."); + return 1; + } + String number = ns.getString("number"); + if (ns.getBoolean("trust_all_known_keys")) { + boolean res = m.trustIdentityAllKeys(number); + if (!res) { + System.err.println("Failed to set the trust for this number, make sure the number is correct."); + return 1; + } + } else { + String fingerprint = ns.getString("verified_fingerprint"); + if (fingerprint != null) { + byte[] fingerprintBytes; + try { + fingerprintBytes = Hex.toByteArray(fingerprint.replaceAll(" ", "").toLowerCase(Locale.ROOT)); + } catch (Exception e) { + System.err.println("Failed to parse the fingerprint, make sure the fingerprint is a correctly encoded hex string without additional characters."); + return 1; + } + boolean res = m.trustIdentityVerified(number, fingerprintBytes); + if (!res) { + System.err.println("Failed to set the trust for the fingerprint of this number, make sure the number and the fingerprint are correct."); + return 1; + } + } else { + System.err.println("You need to specify the fingerprint you have verified with -v FINGERPRINT"); + return 1; + } + } break; case "daemon": if (dBusConn != null) { @@ -593,6 +654,21 @@ public class Main { .nargs("*") .help("Specify one or more members to add to the group"); + Subparser parserListIdentities = subparsers.addParser("listIdentities"); + parserListIdentities.addArgument("-n", "--number") + .help("Only show identity keys for the given phone number."); + + Subparser parserTrust = subparsers.addParser("trust"); + parserTrust.addArgument("number") + .help("Specify the phone number, for which to set the trust.") + .required(true); + MutuallyExclusiveGroup mutTrust = parserTrust.addMutuallyExclusiveGroup(); + mutTrust.addArgument("-a", "--trust-all-known-keys") + .help("Trust all known keys of this user, only use this for testing.") + .action(Arguments.storeTrue()); + mutTrust.addArgument("-v", "--verified-fingerprint") + .help("Specify the fingerprint of the key, only use this option if you have verified the fingerprint."); + Subparser parserReceive = subparsers.addParser("receive"); parserReceive.addArgument("-t", "--timeout") .type(int.class) diff --git a/src/main/java/org/asamk/signal/Manager.java b/src/main/java/org/asamk/signal/Manager.java index 3ce9dfbe..3e36e319 100644 --- a/src/main/java/org/asamk/signal/Manager.java +++ b/src/main/java/org/asamk/signal/Manager.java @@ -1100,4 +1100,54 @@ class Manager implements Signal { public GroupInfo getGroup(byte[] groupId) { return groupStore.getGroup(groupId); } + + public Map> getIdentities() { + return signalProtocolStore.getIdentities(); + } + + public List getIdentities(String number) { + return signalProtocolStore.getIdentities(number); + } + + /** + * Trust this the identity with this fingerprint + * + * @param name username of the identity + * @param fingerprint Fingerprint + */ + public boolean trustIdentityVerified(String name, byte[] fingerprint) { + List ids = signalProtocolStore.getIdentities(name); + if (ids == null) { + return false; + } + for (JsonIdentityKeyStore.Identity id : ids) { + if (!Arrays.equals(id.identityKey.serialize(), fingerprint)) { + continue; + } + + signalProtocolStore.saveIdentity(name, id.identityKey, TrustLevel.TRUSTED_VERIFIED); + save(); + return true; + } + return false; + } + + /** + * Trust all keys of this identity without verification + * + * @param name username of the identity + */ + public boolean trustIdentityAllKeys(String name) { + List ids = signalProtocolStore.getIdentities(name); + if (ids == null) { + return false; + } + for (JsonIdentityKeyStore.Identity id : ids) { + if (id.trustLevel == TrustLevel.UNTRUSTED) { + signalProtocolStore.saveIdentity(name, id.identityKey, TrustLevel.TRUSTED_UNVERIFIED); + } + } + save(); + return true; + } }