Improve behavior of trust command

- Remove all other identities for the same number

Fixes #540
This commit is contained in:
AsamK 2021-02-25 19:20:02 +01:00
parent 8249f852f9
commit 4860caef63
4 changed files with 70 additions and 55 deletions

View file

@ -156,6 +156,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities; import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
@ -1211,10 +1212,12 @@ public class Manager implements Closeable {
try { try {
messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync());
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
if (e.getIdentityKey() != null) {
account.getSignalProtocolStore() account.getSignalProtocolStore()
.saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()),
e.getIdentityKey(), e.getIdentityKey(),
TrustLevel.UNTRUSTED); TrustLevel.UNTRUSTED);
}
throw e; throw e;
} }
} }
@ -1296,10 +1299,12 @@ public class Manager implements Closeable {
} }
return new Pair<>(timestamp, result); return new Pair<>(timestamp, result);
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
if (e.getIdentityKey() != null) {
account.getSignalProtocolStore() account.getSignalProtocolStore()
.saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()),
e.getIdentityKey(), e.getIdentityKey(),
TrustLevel.UNTRUSTED); TrustLevel.UNTRUSTED);
}
return new Pair<>(timestamp, List.of()); return new Pair<>(timestamp, List.of());
} }
} else { } else {
@ -1369,10 +1374,12 @@ public class Manager implements Closeable {
false, false,
System.currentTimeMillis() - startTime); System.currentTimeMillis() - startTime);
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
if (e.getIdentityKey() != null) {
account.getSignalProtocolStore() account.getSignalProtocolStore()
.saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()),
e.getIdentityKey(), e.getIdentityKey(),
TrustLevel.UNTRUSTED); TrustLevel.UNTRUSTED);
}
return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
} }
} }
@ -1385,10 +1392,12 @@ public class Manager implements Closeable {
try { try {
return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message); return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message);
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
if (e.getIdentityKey() != null) {
account.getSignalProtocolStore() account.getSignalProtocolStore()
.saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()),
e.getIdentityKey(), e.getIdentityKey(),
TrustLevel.UNTRUSTED); TrustLevel.UNTRUSTED);
}
return SendMessageResult.identityFailure(address, e.getIdentityKey()); return SendMessageResult.identityFailure(address, e.getIdentityKey());
} }
} }
@ -2388,26 +2397,7 @@ public class Manager implements Closeable {
*/ */
public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException {
var address = canonicalizeAndResolveSignalServiceAddress(name); var address = canonicalizeAndResolveSignalServiceAddress(name);
var ids = account.getSignalProtocolStore().getIdentities(address); return trustIdentity(address, (identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint));
if (ids == null) {
return false;
}
for (var id : ids) {
if (!Arrays.equals(id.getIdentityKey().serialize(), fingerprint)) {
continue;
}
account.getSignalProtocolStore()
.setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
try {
sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
} catch (IOException | UntrustedIdentityException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage());
}
account.save();
return true;
}
return false;
} }
/** /**
@ -2418,27 +2408,47 @@ public class Manager implements Closeable {
*/ */
public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException {
var address = canonicalizeAndResolveSignalServiceAddress(name); var address = canonicalizeAndResolveSignalServiceAddress(name);
return trustIdentity(address, (identityKey) -> safetyNumber.equals(computeSafetyNumber(address, identityKey)));
}
private boolean trustIdentity(SignalServiceAddress address, Function<IdentityKey, Boolean> verifier) {
var ids = account.getSignalProtocolStore().getIdentities(address); var ids = account.getSignalProtocolStore().getIdentities(address);
if (ids == null) { if (ids == null) {
return false; return false;
} }
IdentityInfo foundIdentity = null;
for (var id : ids) { for (var id : ids) {
if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) { if (verifier.apply(id.getIdentityKey())) {
continue; foundIdentity = id;
break;
}
}
if (foundIdentity == null) {
return false;
} }
account.getSignalProtocolStore() account.getSignalProtocolStore()
.setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); .setIdentityTrustLevel(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
try { try {
sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); sendVerifiedMessage(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
} catch (IOException | UntrustedIdentityException e) { } catch (IOException | UntrustedIdentityException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage()); logger.warn("Failed to send verification sync message: {}", e.getMessage());
} }
// Successfully trusted the new identity, now remove all other identities for that number
for (var id : ids) {
if (id == foundIdentity) {
continue;
}
account.getSignalProtocolStore().removeIdentity(address, id.getIdentityKey());
}
account.save(); account.save();
return true; return true;
} }
return false;
}
/** /**
* Trust all keys of this identity without verification * Trust all keys of this identity without verification

View file

@ -13,13 +13,6 @@ public class IdentityInfo {
TrustLevel trustLevel; TrustLevel trustLevel;
Date added; Date added;
public IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel) {
this.address = address;
this.identityKey = identityKey;
this.trustLevel = trustLevel;
this.added = new Date();
}
IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) {
this.address = address; this.address = address;
this.identityKey = identityKey; this.identityKey = identityKey;

View file

@ -125,6 +125,10 @@ public class JsonIdentityKeyStore implements IdentityKeyStore {
identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, new Date())); identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, new Date()));
} }
public void removeIdentity(SignalServiceAddress serviceAddress, IdentityKey identityKey) {
identities.removeIf(id -> id.address.matches(serviceAddress) && id.identityKey.equals(identityKey));
}
@Override @Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
// TODO implement possibility for different handling of incoming/outgoing trust decisions // TODO implement possibility for different handling of incoming/outgoing trust decisions
@ -143,6 +147,10 @@ public class JsonIdentityKeyStore implements IdentityKeyStore {
} }
} }
if (!trustOnFirstUse) {
saveIdentity(resolveSignalServiceAddress(address.getName()), identityKey, TrustLevel.UNTRUSTED, null);
}
return trustOnFirstUse; return trustOnFirstUse;
} }

View file

@ -91,6 +91,10 @@ public class JsonSignalProtocolStore implements SignalServiceProtocolStore {
identityKeyStore.setIdentityTrustLevel(serviceAddress, identityKey, trustLevel); identityKeyStore.setIdentityTrustLevel(serviceAddress, identityKey, trustLevel);
} }
public void removeIdentity(SignalServiceAddress serviceAddress, IdentityKey identityKey) {
identityKeyStore.removeIdentity(serviceAddress, identityKey);
}
public List<IdentityInfo> getIdentities() { public List<IdentityInfo> getIdentities() {
return identityKeyStore.getIdentities(); return identityKeyStore.getIdentities();
} }