Add BackupCredentialType to BackupAuthCredential

Rename BackupLevel::{Messages, Media} to {Free, Paid}, then add 
BackupCredentialType::{Messages, Media}.

This is a breaking change for apps and the server, both because of the
new names, and because the credential attributes have changed (both
what's in a serialized credential, and what's included in the
signature).
This commit is contained in:
Jordan Rose
2024-10-23 13:03:53 -07:00
committed by GitHub
parent 8b93f45806
commit 4e22da3293
23 changed files with 297 additions and 80 deletions

View File

@@ -152,6 +152,7 @@ public final class Native {
public static native void BackupAuthCredentialPresentation_CheckValidContents(byte[] presentationBytes) throws Exception;
public static native byte[] BackupAuthCredentialPresentation_GetBackupId(byte[] presentationBytes);
public static native int BackupAuthCredentialPresentation_GetBackupLevel(byte[] presentationBytes);
public static native int BackupAuthCredentialPresentation_GetType(byte[] presentationBytes);
public static native void BackupAuthCredentialPresentation_Verify(byte[] presentationBytes, long now, byte[] serverParamsBytes) throws Exception;
public static native void BackupAuthCredentialRequestContext_CheckValidContents(byte[] contextBytes) throws Exception;
@@ -160,13 +161,14 @@ public final class Native {
public static native byte[] BackupAuthCredentialRequestContext_ReceiveResponse(byte[] contextBytes, byte[] responseBytes, long expectedRedemptionTime, byte[] paramsBytes) throws Exception;
public static native void BackupAuthCredentialRequest_CheckValidContents(byte[] requestBytes) throws Exception;
public static native byte[] BackupAuthCredentialRequest_IssueDeterministic(byte[] requestBytes, long redemptionTime, int backupLevel, byte[] paramsBytes, byte[] randomness);
public static native byte[] BackupAuthCredentialRequest_IssueDeterministic(byte[] requestBytes, long redemptionTime, int backupLevel, int credentialType, byte[] paramsBytes, byte[] randomness);
public static native void BackupAuthCredentialResponse_CheckValidContents(byte[] responseBytes) throws Exception;
public static native void BackupAuthCredential_CheckValidContents(byte[] paramsBytes) throws Exception;
public static native byte[] BackupAuthCredential_GetBackupId(byte[] credentialBytes);
public static native int BackupAuthCredential_GetBackupLevel(byte[] credentialBytes);
public static native int BackupAuthCredential_GetType(byte[] credentialBytes);
public static native byte[] BackupAuthCredential_PresentDeterministic(byte[] credentialBytes, byte[] serverParamsBytes, byte[] randomness) throws Exception;
public static native void CallLinkAuthCredentialPresentation_CheckValidContents(byte[] presentationBytes) throws Exception;

View File

@@ -53,4 +53,9 @@ public final class BackupAuthCredential extends ByteArray {
return BackupLevel.fromValue(
Native.BackupAuthCredential_GetBackupLevel(getInternalContentsForJNI()));
}
public BackupCredentialType getType() {
return BackupCredentialType.fromValue(
Native.BackupAuthCredential_GetType(getInternalContentsForJNI()));
}
}

View File

@@ -46,4 +46,9 @@ public final class BackupAuthCredentialPresentation extends ByteArray {
return BackupLevel.fromValue(
Native.BackupAuthCredentialPresentation_GetBackupLevel(getInternalContentsForJNI()));
}
public BackupCredentialType getType() {
return BackupCredentialType.fromValue(
Native.BackupAuthCredentialPresentation_GetType(getInternalContentsForJNI()));
}
}

View File

@@ -30,11 +30,15 @@ public final class BackupAuthCredentialRequest extends ByteArray {
* @param timestamp Must be a round number of days. Use {@link Instant#truncatedTo} to ensure
* this.
* @param backupLevel The {@link BackupLevel} that this credential is authorized for
* @param type The type of upload the credential will be used for
* @param params The params that will be used by the verifying server to verify this credential.
*/
public BackupAuthCredentialResponse issueCredential(
Instant timestamp, BackupLevel backupLevel, GenericServerSecretParams params) {
return issueCredential(timestamp, backupLevel, params, new SecureRandom());
Instant timestamp,
BackupLevel backupLevel,
BackupCredentialType type,
GenericServerSecretParams params) {
return issueCredential(timestamp, backupLevel, type, params, new SecureRandom());
}
/**
@@ -46,12 +50,14 @@ public final class BackupAuthCredentialRequest extends ByteArray {
* @param timestamp Must be a round number of days. Use {@link Instant#truncatedTo} to ensure
* this.
* @param backupLevel The {@link BackupLevel} that this credential is authorized for
* @param type The type of upload the credential will be used for
* @param params The params that will be used by the verifying server to verify this credential.
* @param secureRandom Used to hide the server's secrets and make the issued credential unique.
*/
public BackupAuthCredentialResponse issueCredential(
Instant timestamp,
BackupLevel backupLevel,
BackupCredentialType type,
GenericServerSecretParams params,
SecureRandom secureRandom) {
byte[] random = new byte[RANDOM_LENGTH];
@@ -62,6 +68,7 @@ public final class BackupAuthCredentialRequest extends ByteArray {
getInternalContentsForJNI(),
timestamp.getEpochSecond(),
backupLevel.getValue(),
type.getValue(),
params.getInternalContentsForJNI(),
random);

View File

@@ -0,0 +1,32 @@
//
// Copyright 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
package org.signal.libsignal.zkgroup.backups;
public enum BackupCredentialType {
// This must match the Rust version of the enum.
MESSAGES(1),
MEDIA(2);
private final int value;
BackupCredentialType(int value) {
this.value = value;
}
int getValue() {
return this.value;
}
public static BackupCredentialType fromValue(int value) {
// A linear scan is simpler than a hash lookup for a set of values this small.
for (final var credentialType : BackupCredentialType.values()) {
if (credentialType.getValue() == value) {
return credentialType;
}
}
throw new IllegalArgumentException("Invalid backup credential type: " + value);
}
}

View File

@@ -5,19 +5,10 @@
package org.signal.libsignal.zkgroup.backups;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public enum BackupLevel {
// This must match the Rust version of the enum.
MESSAGES(200),
MEDIA(201);
private static final Map<Integer, BackupLevel> LOOKUP =
Arrays.stream(BackupLevel.values())
.collect(Collectors.toMap(BackupLevel::getValue, Function.identity()));
FREE(200),
PAID(201);
private final int value;
@@ -30,10 +21,12 @@ public enum BackupLevel {
}
public static BackupLevel fromValue(int value) {
BackupLevel backupLevel = LOOKUP.get(value);
if (backupLevel == null) {
throw new IllegalArgumentException("Invalid backup level: " + value);
// A linear scan is simpler than a hash lookup for a set of values this small.
for (final var backupLevel : BackupLevel.values()) {
if (backupLevel.getValue() == value) {
return backupLevel;
}
}
return backupLevel;
throw new IllegalArgumentException("Invalid backup level: " + value);
}
}