From: AsamK Date: Sun, 22 Aug 2021 17:23:49 +0000 (+0200) Subject: Trust an identity with its scannable safety numbers from the other device X-Git-Tag: v0.9.0~53 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/4f67ac674b464b07a9ce022a6b19229d511384e2?hp=0a5e836ab69d52c262ea792ed4b84a82dd8a34ca Trust an identity with its scannable safety numbers from the other device Attention, the scannable fingerprints are asymetric, so the scannable fingerprints from the local listIdentities command can't be used to trust an identity. The scannable fingerprint must come from the other device. --- diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index cde2714f..cc57e061 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -81,6 +81,9 @@ import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.fingerprint.Fingerprint; +import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; +import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Pair; @@ -2668,6 +2671,25 @@ public class Manager implements Closeable { TrustLevel.TRUSTED_VERIFIED); } + /** + * Trust this the identity with this scannable safety number + * + * @param name username of the identity + * @param safetyNumber Scannable safety number + */ + public boolean trustIdentityVerifiedSafetyNumber(String name, byte[] safetyNumber) throws InvalidNumberException { + var recipientId = canonicalizeAndResolveRecipient(name); + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + return trustIdentity(recipientId, identityKey -> { + final var fingerprint = computeSafetyNumberFingerprint(address, identityKey); + try { + return fingerprint != null && fingerprint.getScannableFingerprint().compareTo(safetyNumber); + } catch (FingerprintVersionMismatchException | FingerprintParsingException e) { + return false; + } + }, TrustLevel.TRUSTED_VERIFIED); + } + /** * Trust all keys of this identity without verification * @@ -2717,21 +2739,23 @@ public class Manager implements Closeable { } public String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) { - final var fingerprint = Utils.computeSafetyNumber(capabilities.isUuid(), - account.getSelfAddress(), - getIdentityKeyPair().getPublicKey(), - theirAddress, - theirIdentityKey); + final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey); return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText(); } public byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) { - final var fingerprint = Utils.computeSafetyNumber(capabilities.isUuid(), + final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey); + return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized(); + } + + private Fingerprint computeSafetyNumberFingerprint( + final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey + ) { + return Utils.computeSafetyNumber(capabilities.isUuid(), account.getSelfAddress(), getIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); - return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized(); } @Deprecated diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 78f22c19..65487ba7 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -11,6 +11,7 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.util.Hex; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import java.util.Base64; import java.util.Locale; public class TrustCommand implements JsonRpcLocalCommand { @@ -49,44 +50,59 @@ public class TrustCommand implements JsonRpcLocalCommand { } } else { var safetyNumber = ns.getString("verified-safety-number"); - if (safetyNumber != null) { - safetyNumber = safetyNumber.replaceAll(" ", ""); - if (safetyNumber.length() == 66) { - byte[] fingerprintBytes; - try { - fingerprintBytes = Hex.toByteArray(safetyNumber.toLowerCase(Locale.ROOT)); - } catch (Exception e) { - throw new UserErrorException( - "Failed to parse the fingerprint, make sure the fingerprint is a correctly encoded hex string without additional characters."); - } - boolean res; - try { - res = m.trustIdentityVerified(number, fingerprintBytes); - } catch (InvalidNumberException e) { - throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); - } - if (!res) { - throw new UserErrorException( - "Failed to set the trust for the fingerprint of this number, make sure the number and the fingerprint are correct."); - } - } else if (safetyNumber.length() == 60) { - boolean res; - try { - res = m.trustIdentityVerifiedSafetyNumber(number, safetyNumber); - } catch (InvalidNumberException e) { - throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); - } - if (!res) { - throw new UserErrorException( - "Failed to set the trust for the safety number of this phone number, make sure the phone number and the safety number are correct."); - } - } else { + if (safetyNumber == null) { + throw new UserErrorException( + "You need to specify the fingerprint/safety number you have verified with -v SAFETY_NUMBER"); + } + + safetyNumber = safetyNumber.replaceAll(" ", ""); + if (safetyNumber.length() == 66) { + byte[] fingerprintBytes; + try { + fingerprintBytes = Hex.toByteArray(safetyNumber.toLowerCase(Locale.ROOT)); + } catch (Exception e) { throw new UserErrorException( - "Safety number has invalid format, either specify the old hex fingerprint or the new safety number"); + "Failed to parse the fingerprint, make sure the fingerprint is a correctly encoded hex string without additional characters."); + } + boolean res; + try { + res = m.trustIdentityVerified(number, fingerprintBytes); + } catch (InvalidNumberException e) { + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); + } + if (!res) { + throw new UserErrorException( + "Failed to set the trust for the fingerprint of this number, make sure the number and the fingerprint are correct."); + } + } else if (safetyNumber.length() == 60) { + boolean res; + try { + res = m.trustIdentityVerifiedSafetyNumber(number, safetyNumber); + } catch (InvalidNumberException e) { + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); + } + if (!res) { + throw new UserErrorException( + "Failed to set the trust for the safety number of this phone number, make sure the phone number and the safety number are correct."); } } else { - throw new UserErrorException( - "You need to specify the fingerprint/safety number you have verified with -v SAFETY_NUMBER"); + final byte[] scannableSafetyNumber; + try { + scannableSafetyNumber = Base64.getDecoder().decode(safetyNumber); + } catch (IllegalArgumentException e) { + throw new UserErrorException( + "Safety number has invalid format, either specify the old hex fingerprint or the new safety number"); + } + boolean res; + try { + res = m.trustIdentityVerifiedSafetyNumber(number, scannableSafetyNumber); + } catch (InvalidNumberException e) { + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); + } + if (!res) { + throw new UserErrorException( + "Failed to set the trust for the safety number of this phone number, make sure the phone number and the safety number are correct."); + } } } }