Define identity key check endpoint in keys anonymous service

This commit is contained in:
Katherine
2023-09-11 11:57:00 -07:00
committed by GitHub
parent c11b74e9c0
commit cbc3887226
6 changed files with 172 additions and 31 deletions

View File

@@ -16,8 +16,11 @@ import static org.whispersystems.textsecuregcm.grpc.GrpcTestUtils.assertStatusEx
import com.google.protobuf.ByteString;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -29,11 +32,14 @@ import org.signal.chat.common.EcPreKey;
import org.signal.chat.common.EcSignedPreKey;
import org.signal.chat.common.KemSignedPreKey;
import org.signal.chat.common.ServiceIdentifier;
import org.signal.chat.keys.CheckIdentityKeyRequest;
import org.signal.chat.keys.GetPreKeysAnonymousRequest;
import org.signal.chat.keys.GetPreKeysRequest;
import org.signal.chat.keys.GetPreKeysResponse;
import org.signal.chat.keys.KeysAnonymousGrpc;
import org.signal.chat.keys.ReactorKeysAnonymousGrpc;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.entities.ECPreKey;
@@ -41,12 +47,15 @@ import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.KeysManager;
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
import org.whispersystems.textsecuregcm.util.UUIDUtil;
import org.whispersystems.textsecuregcm.util.Util;
import reactor.core.publisher.Flux;
class KeysAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<KeysAnonymousGrpcService, KeysAnonymousGrpc.KeysAnonymousBlockingStub> {
@@ -56,7 +65,6 @@ class KeysAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<KeysAnonymousGrpcS
@Mock
private KeysManager keysManager;
@Override
protected KeysAnonymousGrpcService createServiceBeforeEachTest() {
return new KeysAnonymousGrpcService(accountsManager, keysManager);
@@ -203,4 +211,81 @@ class KeysAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<KeysAnonymousGrpcS
.build())
.build()));
}
@Test
void checkIdentityKeys() {
final ReactorKeysAnonymousGrpc.ReactorKeysAnonymousStub reactiveKeysAnonymousStub = ReactorKeysAnonymousGrpc.newReactorStub(SimpleBaseGrpcTest.GRPC_SERVER_EXTENSION_UNAUTHENTICATED.getChannel());
when(accountsManager.getByServiceIdentifierAsync(any()))
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
final Account mismatchedAciFingerprintAccount = mock(Account.class);
final UUID mismatchedAciFingerprintAccountIdentifier = UUID.randomUUID();
final IdentityKey mismatchedAciFingerprintAccountIdentityKey = new IdentityKey(Curve.generateKeyPair().getPublicKey());
final Account matchingAciFingerprintAccount = mock(Account.class);
final UUID matchingAciFingerprintAccountIdentifier = UUID.randomUUID();
final IdentityKey matchingAciFingerprintAccountIdentityKey = new IdentityKey(Curve.generateKeyPair().getPublicKey());
final Account mismatchedPniFingerprintAccount = mock(Account.class);
final UUID mismatchedPniFingerprintAccountIdentifier = UUID.randomUUID();
final IdentityKey mismatchedPniFingerpringAccountIdentityKey = new IdentityKey(Curve.generateKeyPair().getPublicKey());
when(mismatchedAciFingerprintAccount.getIdentityKey(IdentityType.ACI)).thenReturn(mismatchedAciFingerprintAccountIdentityKey);
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(mismatchedAciFingerprintAccountIdentifier)))
.thenReturn(CompletableFuture.completedFuture(Optional.of(mismatchedAciFingerprintAccount)));
when(matchingAciFingerprintAccount.getIdentityKey(IdentityType.ACI)).thenReturn(matchingAciFingerprintAccountIdentityKey);
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(matchingAciFingerprintAccountIdentifier)))
.thenReturn(CompletableFuture.completedFuture(Optional.of(matchingAciFingerprintAccount)));
when(mismatchedPniFingerprintAccount.getIdentityKey(IdentityType.PNI)).thenReturn(mismatchedPniFingerpringAccountIdentityKey);
when(accountsManager.getByServiceIdentifierAsync(new PniServiceIdentifier(mismatchedPniFingerprintAccountIdentifier)))
.thenReturn(CompletableFuture.completedFuture(Optional.of(mismatchedPniFingerprintAccount)));
final Flux<CheckIdentityKeyRequest> requests = Flux.just(
buildCheckIdentityKeyRequest(org.signal.chat.common.IdentityType.IDENTITY_TYPE_ACI, mismatchedAciFingerprintAccountIdentifier,
new IdentityKey(Curve.generateKeyPair().getPublicKey())),
buildCheckIdentityKeyRequest(org.signal.chat.common.IdentityType.IDENTITY_TYPE_ACI, matchingAciFingerprintAccountIdentifier,
matchingAciFingerprintAccountIdentityKey),
buildCheckIdentityKeyRequest(org.signal.chat.common.IdentityType.IDENTITY_TYPE_PNI, UUID.randomUUID(),
new IdentityKey(Curve.generateKeyPair().getPublicKey())),
buildCheckIdentityKeyRequest(org.signal.chat.common.IdentityType.IDENTITY_TYPE_PNI, mismatchedPniFingerprintAccountIdentifier,
new IdentityKey(Curve.generateKeyPair().getPublicKey()))
);
final Map<UUID, IdentityKey> expectedResponses = Map.of(
mismatchedAciFingerprintAccountIdentifier, mismatchedAciFingerprintAccountIdentityKey,
mismatchedPniFingerprintAccountIdentifier, mismatchedPniFingerpringAccountIdentityKey);
final Map<UUID, IdentityKey> responses = reactiveKeysAnonymousStub.checkIdentityKeys(requests)
.collectMap(response -> ServiceIdentifierUtil.fromGrpcServiceIdentifier(response.getTargetIdentifier()).uuid(),
response -> {
try {
return new IdentityKey(response.getIdentityKey().toByteArray());
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
})
.block();
assertEquals(expectedResponses, responses);
}
private static CheckIdentityKeyRequest buildCheckIdentityKeyRequest(final org.signal.chat.common.IdentityType identityType,
final UUID uuid, final IdentityKey identityKey) {
return CheckIdentityKeyRequest.newBuilder()
.setTargetIdentifier(ServiceIdentifier.newBuilder()
.setIdentityType(identityType)
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(uuid))))
.setFingerprint(ByteString.copyFrom(getFingerprint(identityKey)))
.build();
}
private static byte[] getFingerprint(final IdentityKey publicKey) {
try {
return Util.truncate(MessageDigest.getInstance("SHA-256").digest(publicKey.serialize()), 4);
} catch (final NoSuchAlgorithmException e) {
throw new AssertionError("All Java implementations must support SHA-256 MessageDigest algorithm", e);
}
}
}