mirror of
https://github.com/signalapp/libsignal.git
synced 2026-05-14 19:07:09 +02:00
zkgroup: Add AuthCredentialWithPni
This is a variant of AuthCredential that carries two UUIDs, intended to be a user's ACI and PNI. Why? Because when you've been invited to a group, you may have been invited by your ACI or by your PNI, or by both, and it's easier for clients to treat all those states the same by having a credential that covers both identities. The downside is that it's larger (both the data, obviously, but also the zkgroup proof of validity, unsurprisingly). AnyAuthCredentialPresentation gains a 'get_pni_ciphertext' method, which will return `None` for the existing presentations and `Some(encrypted_pni)` for the new credential. Having a separate credential type but a common presentation type makes it easier for the server to handle all possible credentials uniformly.
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
package org.signal.libsignal.zkgroup.integrationtests;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.Test;
|
||||
import org.signal.libsignal.protocol.util.Hex;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
@@ -19,6 +18,8 @@ import org.signal.libsignal.zkgroup.InvalidRedemptionTimeException;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredential;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredentialPresentation;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPni;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse;
|
||||
import org.signal.libsignal.zkgroup.auth.ClientZkAuthOperations;
|
||||
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
|
||||
import org.signal.libsignal.zkgroup.groups.ClientZkGroupCipher;
|
||||
@@ -51,6 +52,7 @@ import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public final class ZkGroupTest extends SecureRandomTest {
|
||||
|
||||
@@ -127,20 +129,28 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[32]; // wrong length
|
||||
new ServerSecretParams(temp);
|
||||
throw new AssertionError("Failed to catch invalid ServerSecretParams deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new ServerSecretParams(makeBadArray(serverSecretParams.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid ServerSecretParams deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
byte[] temp = new byte[32]; // wrong length
|
||||
new ServerPublicParams(temp);
|
||||
throw new AssertionError("Failed to catch invalid ServerPublicParams deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new ServerPublicParams(makeBadArray(serverPublicParams.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid ServerPublicParams deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
ServerZkAuthOperations serverZkAuth = new ServerZkAuthOperations(serverSecretParams);
|
||||
@@ -161,25 +171,35 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[10]; // wrong length
|
||||
new GroupMasterKey(temp);
|
||||
throw new AssertionError("Failed to catch invalid GroupMasterKey deserialize");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
byte[] temp = new byte[10]; // wrong length
|
||||
new GroupSecretParams(temp);
|
||||
throw new AssertionError("Failed to catch invalid GroupSecretParams deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new GroupSecretParams(makeBadArray(groupSecretParams.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid GroupSecretParams deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
byte[] temp = new byte[10]; // wrong length
|
||||
new GroupPublicParams(temp);
|
||||
throw new AssertionError("Failed to catch invalid GroupPublicParams deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new GroupPublicParams(makeBadArray(groupPublicParams.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid GroupPublicParams deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// SERVER
|
||||
@@ -199,11 +219,15 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[10];
|
||||
new AuthCredentialResponse(temp);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialResponse deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new AuthCredentialResponse(makeBadArray(authCredentialResponse.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialResponse deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT - verify test
|
||||
@@ -212,7 +236,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
clientZkAuthCipher.receiveAuthCredential(badUuid, redemptionTime, authCredentialResponse);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredential 1");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
byte[] temp = authCredentialResponse.serialize();
|
||||
temp[1]++;
|
||||
@@ -220,7 +246,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
clientZkAuthCipher.receiveAuthCredential(uuid, redemptionTime, badResponse);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredential 2");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// Create and decrypt user entry
|
||||
@@ -235,12 +263,16 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[10];
|
||||
new UuidCiphertext(temp);
|
||||
throw new AssertionError("Failed to catch invalid UuidCiphertext deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
new UuidCiphertext(makeBadArray(uuidCiphertext.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid UuidCiphertext deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT - verify test
|
||||
@@ -250,7 +282,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
clientZkGroupCipher.decryptUuid(new UuidCiphertext(temp));
|
||||
throw new AssertionError("Failed to catch invalid UuidCiphertext decrypt");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT - Create presentation
|
||||
@@ -266,32 +300,38 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
new AuthCredentialPresentation(temp);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialPresentation deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new AuthCredentialPresentation(makeBadArray(presentation.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialPresentation deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// SERVER - Verify presentation, using times at the edge of the acceptable window
|
||||
Instant redemptionInstant = Instant.ofEpochSecond(86400L * redemptionTime);
|
||||
UuidCiphertext uuidCiphertextRecv = presentation.getUuidCiphertext();
|
||||
assertArrayEquals(uuidCiphertext.serialize(), uuidCiphertextRecv.serialize());
|
||||
assertEquals(presentation.getRedemptionTime(), redemptionTime);
|
||||
assertNull(presentation.getPniCiphertext());
|
||||
assertEquals(presentation.getRedemptionTime(), redemptionInstant);
|
||||
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(123458L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.minus(1, ChronoUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.plus(2, ChronoUnit.DAYS));
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS) - 1L);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.minus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #1!");
|
||||
} catch(InvalidRedemptionTimeException e) {
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(123458L, TimeUnit.DAYS) + 1L);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.plus(2, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #2!");
|
||||
} catch(InvalidRedemptionTimeException e) {
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
@@ -299,32 +339,38 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[3]++; // We need a bad presentation that passes deserialization, this seems to work
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionInstant);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #3!");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[0] = 0; // This interprets a V2 as V1, so should fail
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionInstant);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #4");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[0] = 2; // This interprets a V2 as a non-existent version, so should fail
|
||||
temp[0] = 40; // This interprets a V2 as a non-existent version, so should fail
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionInstant);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #5");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
{
|
||||
// Test that V1 presentation verifies successfully
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(authPresentationResultV1);
|
||||
assertEquals(presentationTemp.serialize()[0], 0); // Check V1
|
||||
assertEquals(presentationTemp.getVersion(), AuthCredentialPresentation.Version.V1);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, TimeUnit.MILLISECONDS.convert(123455L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionInstant);
|
||||
assertArrayEquals(presentationTemp.serialize(), authPresentationResultV1);
|
||||
}
|
||||
|
||||
@@ -338,7 +384,7 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
// redemption date to compare against test vectors, it uses the current time
|
||||
|
||||
UUID uuid = TEST_UUID;
|
||||
int redemptionTime = (int)TimeUnit.DAYS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
||||
int redemptionTime = (int)(Instant.now().truncatedTo(ChronoUnit.DAYS).getEpochSecond() / 86400);
|
||||
|
||||
// Generate keys (client's are per-group, server's are not)
|
||||
// ---
|
||||
@@ -375,33 +421,190 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
AuthCredentialPresentation presentation = clientZkAuthCipher.createAuthCredentialPresentation(createSecureRandom(TEST_ARRAY_32_5), groupSecretParams, authCredential);
|
||||
|
||||
// Verify presentation, using times at the edge of the acceptable window
|
||||
Instant redemptionInstant = Instant.ofEpochSecond(86400L * redemptionTime);
|
||||
UuidCiphertext uuidCiphertextRecv = presentation.getUuidCiphertext();
|
||||
assertArrayEquals(uuidCiphertext.serialize(), uuidCiphertextRecv.serialize());
|
||||
assertEquals(presentation.getRedemptionTime(), redemptionTime);
|
||||
assertEquals(presentation.getRedemptionTime(), redemptionInstant);
|
||||
|
||||
// By default the library uses the current time
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation);
|
||||
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(redemptionTime - 1L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(redemptionTime + 2L, TimeUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.minus(1, ChronoUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.plus(2, ChronoUnit.DAYS));
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(redemptionTime - 1L, TimeUnit.DAYS) - 1L);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation (current time) should fail #1!");
|
||||
} catch(InvalidRedemptionTimeException e) {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.minus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #1!");
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, TimeUnit.MILLISECONDS.convert(redemptionTime + 2L, TimeUnit.DAYS) + 1L);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation (current time) should fail #2!");
|
||||
} catch(InvalidRedemptionTimeException e) {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionInstant.plus(2, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #2!");
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthWithPniIntegration() throws VerificationFailedException, InvalidInputException, InvalidRedemptionTimeException {
|
||||
|
||||
UUID aci = TEST_UUID;
|
||||
UUID pni = TEST_UUID_1;
|
||||
Instant redemptionTime = Instant.now().truncatedTo(ChronoUnit.DAYS);
|
||||
|
||||
// Generate keys (client's are per-group, server's are not)
|
||||
// ---
|
||||
|
||||
// SERVER
|
||||
ServerSecretParams serverSecretParams = ServerSecretParams.generate(createSecureRandom(TEST_ARRAY_32));
|
||||
ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
|
||||
|
||||
ServerZkAuthOperations serverZkAuth = new ServerZkAuthOperations(serverSecretParams);
|
||||
|
||||
// CLIENT
|
||||
GroupMasterKey masterKey = new GroupMasterKey(TEST_ARRAY_32_1);
|
||||
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(masterKey);
|
||||
|
||||
assertArrayEquals(groupSecretParams.getMasterKey().serialize(), masterKey.serialize());
|
||||
|
||||
GroupPublicParams groupPublicParams = groupSecretParams.getPublicParams();
|
||||
|
||||
// SERVER
|
||||
// Issue credential
|
||||
AuthCredentialWithPniResponse authCredentialResponse = serverZkAuth.issueAuthCredentialWithPni(createSecureRandom(TEST_ARRAY_32_2), aci, pni, redemptionTime);
|
||||
|
||||
// CLIENT
|
||||
// Receive credential
|
||||
ClientZkAuthOperations clientZkAuthCipher = new ClientZkAuthOperations(serverPublicParams);
|
||||
ClientZkGroupCipher clientZkGroupCipher = new ClientZkGroupCipher (groupSecretParams );
|
||||
AuthCredentialWithPni authCredential = clientZkAuthCipher.receiveAuthCredentialWithPni(aci, pni, redemptionTime.getEpochSecond(), authCredentialResponse);
|
||||
|
||||
// CLIENT - deserialize test
|
||||
{
|
||||
new AuthCredentialWithPniResponse(authCredentialResponse.serialize());
|
||||
try {
|
||||
byte[] temp = new byte[10];
|
||||
new AuthCredentialWithPniResponse(temp);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialWithPniResponse deserialize 1");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new AuthCredentialWithPniResponse(makeBadArray(authCredentialResponse.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialWithPniResponse deserialize 2");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT - verify test
|
||||
{
|
||||
try {
|
||||
// Switch ACI and PNI
|
||||
clientZkAuthCipher.receiveAuthCredentialWithPni(pni, aci, redemptionTime.getEpochSecond(), authCredentialResponse);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialWithPni 1");
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
byte[] temp = authCredentialResponse.serialize();
|
||||
temp[1]++;
|
||||
AuthCredentialWithPniResponse badResponse = new AuthCredentialWithPniResponse(temp);
|
||||
try {
|
||||
clientZkAuthCipher.receiveAuthCredentialWithPni(aci, pni, redemptionTime.getEpochSecond(), badResponse);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialWithPni 2");
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// Create and decrypt user entry
|
||||
UuidCiphertext aciCiphertext = clientZkGroupCipher.encryptUuid(aci);
|
||||
UUID aciPlaintext = clientZkGroupCipher.decryptUuid(aciCiphertext);
|
||||
assertEquals(aci, aciPlaintext);
|
||||
UuidCiphertext pniCiphertext = clientZkGroupCipher.encryptUuid(pni);
|
||||
UUID pniPlaintext = clientZkGroupCipher.decryptUuid(pniCiphertext);
|
||||
assertEquals(pni, pniPlaintext);
|
||||
|
||||
// CLIENT - Create presentation
|
||||
AuthCredentialPresentation presentation = clientZkAuthCipher.createAuthCredentialPresentation(createSecureRandom(TEST_ARRAY_32_5), groupSecretParams, authCredential);
|
||||
assertEquals(presentation.serialize()[0], 2); // Check V3
|
||||
assertEquals(presentation.getVersion(), AuthCredentialPresentation.Version.V3);
|
||||
|
||||
// CLIENT - deserialize test
|
||||
{
|
||||
new AuthCredentialPresentation(presentation.serialize());
|
||||
byte[] temp = new byte[10];
|
||||
try {
|
||||
new AuthCredentialPresentation(temp);
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialPresentation deserialize 1");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new AuthCredentialPresentation(makeBadArray(presentation.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid AuthCredentialPresentation deserialize 2");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// SERVER - Verify presentation, using times at the edge of the acceptable window
|
||||
assertArrayEquals(aciCiphertext.serialize(), presentation.getUuidCiphertext().serialize());
|
||||
assertArrayEquals(pniCiphertext.serialize(), presentation.getPniCiphertext().serialize());
|
||||
assertEquals(presentation.getRedemptionTime(), redemptionTime);
|
||||
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionTime.minus(1, ChronoUnit.DAYS));
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionTime.plus(2, ChronoUnit.DAYS));
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionTime.minus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #1!");
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentation, redemptionTime.plus(2, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS));
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #2!");
|
||||
} catch (InvalidRedemptionTimeException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[3] += 5; // We need a bad presentation that passes deserialization, this seems to work
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionTime);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #3!");
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[0] = 0; // This interprets a V3 as V1, so should fail
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionTime);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #4");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
temp[0] = 40; // This interprets a V3 as a non-existent version, so should fail
|
||||
AuthCredentialPresentation presentationTemp = new AuthCredentialPresentation(temp);
|
||||
serverZkAuth.verifyAuthCredentialPresentation(groupPublicParams, presentationTemp, redemptionTime);
|
||||
throw new AssertionError("verifyAuthCredentialPresentation should fail #5");
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testProfileKeyIntegration() throws VerificationFailedException, InvalidInputException, UnsupportedEncodingException {
|
||||
|
||||
@@ -438,11 +641,15 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[10];
|
||||
new ProfileKeyCredentialRequestContext(temp);
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCredentialResponse deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
new ProfileKeyCredentialRequestContext(makeBadArray(context.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCredentialRequestContext deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// SERVER
|
||||
@@ -456,7 +663,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
serverZkProfile.issueProfileKeyCredential(createSecureRandom(TEST_ARRAY_32_4), badRequest, uuid, profileKeyCommitment);
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCredentialRequest");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT
|
||||
@@ -480,12 +689,16 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
byte[] temp = new byte[10];
|
||||
new ProfileKeyCiphertext(temp);
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCiphertext deserialize 1");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
new ProfileKeyCiphertext(makeBadArray(profileKeyCiphertext.serialize()));
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCiphertext deserialize 2");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT - verify test
|
||||
@@ -495,7 +708,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
clientZkGroupCipher.decryptProfileKey(new ProfileKeyCiphertext(temp), uuid);
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCiphertext decrypt");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
ProfileKeyCredentialPresentation presentation = clientZkProfileCipher.createProfileKeyCredentialPresentation(createSecureRandom(TEST_ARRAY_32_5), groupSecretParams, profileKeyCredential);
|
||||
@@ -519,7 +734,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 1");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -527,7 +744,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 2");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -535,7 +754,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 3");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
{
|
||||
// Test that V1 presentation verifies successfully
|
||||
@@ -589,7 +810,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
serverZkProfile.issueExpiringProfileKeyCredential(createSecureRandom(TEST_ARRAY_32_4), badRequest, uuid, profileKeyCommitment, expiration);
|
||||
throw new AssertionError("Failed to catch invalid ProfileKeyCredentialRequest");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT
|
||||
@@ -621,12 +844,16 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
try {
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentation, expiration);
|
||||
throw new AssertionError("credential expired 1");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentation, expiration.plusSeconds(5));
|
||||
throw new AssertionError("credential expired 2");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -634,7 +861,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 1");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -642,7 +871,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 2");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -650,7 +881,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
ProfileKeyCredentialPresentation presentationTemp = new ProfileKeyCredentialPresentation(temp);
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyProfileKeyCredentialPresentation should fail 3");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// Test that we can encode as a V1 presentation, even though it won't verify.
|
||||
ProfileKeyCredentialPresentation v1Presentation = new ProfileKeyCredentialPresentation(presentation.getStructurallyValidV1PresentationBytes());
|
||||
@@ -658,7 +891,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
assertEquals(v1Presentation.getProfileKeyCiphertext(), presentation.getProfileKeyCiphertext());
|
||||
try {
|
||||
serverZkProfile.verifyProfileKeyCredentialPresentation(groupPublicParams, v1Presentation);
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -718,7 +953,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
PniCredentialPresentation presentationTemp = new PniCredentialPresentation(temp);
|
||||
serverZkProfile.verifyPniCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyPniCredentialPresentation should fail 1");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -726,7 +963,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
PniCredentialPresentation presentationTemp = new PniCredentialPresentation(temp);
|
||||
serverZkProfile.verifyPniCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyPniCredentialPresentation should fail 2");
|
||||
} catch (VerificationFailedException e) {}
|
||||
} catch (VerificationFailedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] temp = presentation.serialize();
|
||||
@@ -734,7 +973,9 @@ private static final byte[] pniPresentationResultV2 = Hex.fromStringCondensedAss
|
||||
PniCredentialPresentation presentationTemp = new PniCredentialPresentation(temp);
|
||||
serverZkProfile.verifyPniCredentialPresentation(groupPublicParams, presentationTemp);
|
||||
throw new AssertionError("verifyPniCredentialPresentation should fail 3");
|
||||
} catch (InvalidInputException e) {}
|
||||
} catch (InvalidInputException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
{
|
||||
// Test that V1 presentation verifies successfully
|
||||
|
||||
Reference in New Issue
Block a user