Add serialization for JwtEcdsaPublicKey objects.
PiperOrigin-RevId: 530294696
Change-Id: I0e76efbae588240e4c9ea982d52dbe65b0577983
diff --git a/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel b/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel
index 27e63cb..22b7221 100644
--- a/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel
+++ b/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel
@@ -1075,15 +1075,22 @@
srcs = ["JwtEcdsaProtoSerialization.java"],
deps = [
":jwt_ecdsa_parameters-android",
+ ":jwt_ecdsa_public_key-android",
"//proto:jwt_ecdsa_java_proto_lite",
"//proto:tink_java_proto_lite",
"//src/main/java/com/google/crypto/tink:accesses_partial_key-android",
+ "//src/main/java/com/google/crypto/tink:secret_key_access-android",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding-android",
+ "//src/main/java/com/google/crypto/tink/internal:key_parser-android",
+ "//src/main/java/com/google/crypto/tink/internal:key_serializer-android",
"//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry-android",
"//src/main/java/com/google/crypto/tink/internal:parameters_parser-android",
"//src/main/java/com/google/crypto/tink/internal:parameters_serializer-android",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization-android",
"//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization-android",
"//src/main/java/com/google/crypto/tink/internal:util-android",
"//src/main/java/com/google/crypto/tink/util:bytes-android",
+ "@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_protobuf_protobuf_javalite",
],
)
@@ -1093,15 +1100,22 @@
srcs = ["JwtEcdsaProtoSerialization.java"],
deps = [
":jwt_ecdsa_parameters",
+ ":jwt_ecdsa_public_key",
"//proto:jwt_ecdsa_java_proto",
"//proto:tink_java_proto",
"//src/main/java/com/google/crypto/tink:accesses_partial_key",
+ "//src/main/java/com/google/crypto/tink:secret_key_access",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding",
+ "//src/main/java/com/google/crypto/tink/internal:key_parser",
+ "//src/main/java/com/google/crypto/tink/internal:key_serializer",
"//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry",
"//src/main/java/com/google/crypto/tink/internal:parameters_parser",
"//src/main/java/com/google/crypto/tink/internal:parameters_serializer",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization",
"//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization",
"//src/main/java/com/google/crypto/tink/internal:util",
"//src/main/java/com/google/crypto/tink/util:bytes",
+ "@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_protobuf_protobuf_java",
],
)
diff --git a/src/main/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerialization.java b/src/main/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerialization.java
index a168fc3..cf95b77 100644
--- a/src/main/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerialization.java
+++ b/src/main/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerialization.java
@@ -19,17 +19,26 @@
import static com.google.crypto.tink.internal.Util.toBytesFromPrintableAscii;
import com.google.crypto.tink.AccessesPartialKey;
+import com.google.crypto.tink.SecretKeyAccess;
+import com.google.crypto.tink.internal.BigIntegerEncoding;
+import com.google.crypto.tink.internal.KeyParser;
+import com.google.crypto.tink.internal.KeySerializer;
import com.google.crypto.tink.internal.MutableSerializationRegistry;
import com.google.crypto.tink.internal.ParametersParser;
import com.google.crypto.tink.internal.ParametersSerializer;
+import com.google.crypto.tink.internal.ProtoKeySerialization;
import com.google.crypto.tink.internal.ProtoParametersSerialization;
import com.google.crypto.tink.proto.JwtEcdsaAlgorithm;
+import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.KeyTemplate;
import com.google.crypto.tink.proto.OutputPrefixType;
import com.google.crypto.tink.util.Bytes;
+import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import java.security.GeneralSecurityException;
+import java.security.spec.ECPoint;
+import javax.annotation.Nullable;
/**
* Methods to serialize and parse {@link JwtEcdsaPrivateKey}, {@link JwtEcdsaPublicKey}, and {@link
@@ -42,6 +51,10 @@
"type.googleapis.com/google.crypto.tink.JwtEcdsaPrivateKey";
private static final Bytes TYPE_URL_BYTES = toBytesFromPrintableAscii(TYPE_URL);
+ private static final String PUBLIC_TYPE_URL =
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey";
+ private static final Bytes PUBLIC_TYPE_URL_BYTES = toBytesFromPrintableAscii(PUBLIC_TYPE_URL);
+
private static final ParametersSerializer<JwtEcdsaParameters, ProtoParametersSerialization>
PARAMETERS_SERIALIZER =
ParametersSerializer.create(
@@ -55,6 +68,19 @@
TYPE_URL_BYTES,
ProtoParametersSerialization.class);
+ private static final KeySerializer<JwtEcdsaPublicKey, ProtoKeySerialization>
+ PUBLIC_KEY_SERIALIZER =
+ KeySerializer.create(
+ JwtEcdsaProtoSerialization::serializePublicKey,
+ JwtEcdsaPublicKey.class,
+ ProtoKeySerialization.class);
+
+ private static final KeyParser<ProtoKeySerialization> PUBLIC_KEY_PARSER =
+ KeyParser.create(
+ JwtEcdsaProtoSerialization::parsePublicKey,
+ PUBLIC_TYPE_URL_BYTES,
+ ProtoKeySerialization.class);
+
private static JwtEcdsaAlgorithm toProtoAlgorithm(JwtEcdsaParameters.Algorithm algorithm)
throws GeneralSecurityException {
if (JwtEcdsaParameters.Algorithm.ES256.equals(algorithm)) {
@@ -147,6 +173,115 @@
.build();
}
+ private static int getEncodingLength(JwtEcdsaParameters.Algorithm algorithm)
+ throws GeneralSecurityException {
+ // We currently encode with one extra 0 byte at the beginning, to make sure
+ // that parsing is correct even if passing of a two's complement encoding is used.
+ // We want to prevent bugs similar to b/264525021
+ if (algorithm.equals(JwtEcdsaParameters.Algorithm.ES256)) {
+ return 33;
+ }
+ if (algorithm.equals(JwtEcdsaParameters.Algorithm.ES384)) {
+ return 49;
+ }
+ if (algorithm.equals(JwtEcdsaParameters.Algorithm.ES512)) {
+ return 67;
+ }
+ throw new GeneralSecurityException("Unknown algorithm: " + algorithm);
+ }
+
+ private static OutputPrefixType toProtoOutputPrefixType(JwtEcdsaParameters parameters) {
+ if (parameters.getKidStrategy().equals(JwtEcdsaParameters.KidStrategy.BASE64_ENCODED_KEY_ID)) {
+ return OutputPrefixType.TINK;
+ }
+ return OutputPrefixType.RAW;
+ }
+
+ private static com.google.crypto.tink.proto.JwtEcdsaPublicKey serializePublicKey(
+ JwtEcdsaPublicKey key) throws GeneralSecurityException {
+ int encLength = getEncodingLength(key.getParameters().getAlgorithm());
+ ECPoint publicPoint = key.getPublicPoint();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.Builder builder =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(toProtoAlgorithm(key.getParameters().getAlgorithm()))
+ .setX(
+ ByteString.copyFrom(
+ BigIntegerEncoding.toBigEndianBytesOfFixedLength(
+ publicPoint.getAffineX(), encLength)))
+ .setY(
+ ByteString.copyFrom(
+ BigIntegerEncoding.toBigEndianBytesOfFixedLength(
+ publicPoint.getAffineY(), encLength)));
+ if (key.getParameters().getKidStrategy().equals(JwtEcdsaParameters.KidStrategy.CUSTOM)) {
+ builder.setCustomKid(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.CustomKid.newBuilder()
+ .setValue(key.getKid().get())
+ .build());
+ }
+ return builder.build();
+ }
+
+ private static ProtoKeySerialization serializePublicKey(
+ JwtEcdsaPublicKey key, @Nullable SecretKeyAccess access) throws GeneralSecurityException {
+ return ProtoKeySerialization.create(
+ PUBLIC_TYPE_URL,
+ serializePublicKey(key).toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ toProtoOutputPrefixType(key.getParameters()),
+ key.getIdRequirementOrNull());
+ }
+
+ @SuppressWarnings("UnusedException")
+ private static JwtEcdsaPublicKey parsePublicKey(
+ ProtoKeySerialization serialization, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ if (!serialization.getTypeUrl().equals(PUBLIC_TYPE_URL)) {
+ throw new IllegalArgumentException(
+ "Wrong type URL in call to EcdsaProtoSerialization.parsePublicKey: "
+ + serialization.getTypeUrl());
+ }
+ try {
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parseFrom(
+ serialization.getValue(), ExtensionRegistryLite.getEmptyRegistry());
+ if (protoKey.getVersion() != 0) {
+ throw new GeneralSecurityException("Only version 0 keys are accepted");
+ }
+ JwtEcdsaParameters.Builder parametersBuilder = JwtEcdsaParameters.builder();
+ JwtEcdsaPublicKey.Builder keyBuilder = JwtEcdsaPublicKey.builder();
+
+ if (serialization.getOutputPrefixType().equals(OutputPrefixType.TINK)) {
+ if (protoKey.hasCustomKid()) {
+ throw new GeneralSecurityException(
+ "Keys serialized with OutputPrefixType TINK should not have a custom kid");
+ }
+ @Nullable Integer idRequirement = serialization.getIdRequirementOrNull();
+ if (idRequirement == null) {
+ throw new GeneralSecurityException(
+ "Keys serialized with OutputPrefixType TINK need an ID Requirement");
+ }
+ parametersBuilder.setKidStrategy(JwtEcdsaParameters.KidStrategy.BASE64_ENCODED_KEY_ID);
+ keyBuilder.setIdRequirement(idRequirement);
+ } else if (serialization.getOutputPrefixType().equals(OutputPrefixType.RAW)) {
+ if (protoKey.hasCustomKid()) {
+ parametersBuilder.setKidStrategy(JwtEcdsaParameters.KidStrategy.CUSTOM);
+ keyBuilder.setCustomKid(protoKey.getCustomKid().getValue());
+ } else {
+ parametersBuilder.setKidStrategy(JwtEcdsaParameters.KidStrategy.IGNORED);
+ }
+ }
+ parametersBuilder.setAlgorithm(toAlgorithm(protoKey.getAlgorithm()));
+ keyBuilder.setPublicPoint(
+ new ECPoint(
+ BigIntegerEncoding.fromUnsignedBigEndianBytes(protoKey.getX().toByteArray()),
+ BigIntegerEncoding.fromUnsignedBigEndianBytes(protoKey.getY().toByteArray())));
+ return keyBuilder.setParameters(parametersBuilder.build()).build();
+ } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
+ throw new GeneralSecurityException("Parsing EcdsaPublicKey failed");
+ }
+ }
+
public static void register() throws GeneralSecurityException {
register(MutableSerializationRegistry.globalInstance());
}
@@ -155,6 +290,8 @@
throws GeneralSecurityException {
registry.registerParametersSerializer(PARAMETERS_SERIALIZER);
registry.registerParametersParser(PARAMETERS_PARSER);
+ registry.registerKeySerializer(PUBLIC_KEY_SERIALIZER);
+ registry.registerKeyParser(PUBLIC_KEY_PARSER);
}
private JwtEcdsaProtoSerialization() {}
diff --git a/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel b/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel
index 1e953aa..60ca94e 100644
--- a/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel
+++ b/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel
@@ -473,12 +473,18 @@
deps = [
"//proto:jwt_ecdsa_java_proto",
"//proto:tink_java_proto",
+ "//src/main/java/com/google/crypto/tink:insecure_secret_key_access",
+ "//src/main/java/com/google/crypto/tink:key",
"//src/main/java/com/google/crypto/tink:parameters",
"//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization",
"//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization",
"//src/main/java/com/google/crypto/tink/internal/testing:asserts",
"//src/main/java/com/google/crypto/tink/jwt:jwt_ecdsa_parameters",
"//src/main/java/com/google/crypto/tink/jwt:jwt_ecdsa_proto_serialization",
+ "//src/main/java/com/google/crypto/tink/jwt:jwt_ecdsa_public_key",
+ "//src/main/java/com/google/crypto/tink/subtle:hex",
+ "@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_google_truth_truth",
"@maven//:junit_junit",
],
diff --git a/src/test/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerializationTest.java b/src/test/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerializationTest.java
index 0278eaf..5c76034 100644
--- a/src/test/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerializationTest.java
+++ b/src/test/java/com/google/crypto/tink/jwt/JwtEcdsaProtoSerializationTest.java
@@ -20,12 +20,21 @@
import static com.google.crypto.tink.internal.testing.Asserts.assertEqualWhenValueParsed;
import static org.junit.Assert.assertThrows;
+import com.google.crypto.tink.InsecureSecretKeyAccess;
+import com.google.crypto.tink.Key;
import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.internal.MutableSerializationRegistry;
+import com.google.crypto.tink.internal.ProtoKeySerialization;
import com.google.crypto.tink.internal.ProtoParametersSerialization;
import com.google.crypto.tink.proto.JwtEcdsaAlgorithm;
+import com.google.crypto.tink.proto.JwtEcdsaPublicKey.CustomKid;
+import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.OutputPrefixType;
+import com.google.crypto.tink.subtle.Hex;
+import com.google.protobuf.ByteString;
+import java.math.BigInteger;
import java.security.GeneralSecurityException;
+import java.security.spec.ECPoint;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -169,4 +178,348 @@
.build());
assertThrows(GeneralSecurityException.class, () -> registry.parseParameters(serialization));
}
+
+ // PUBLIC KEY PARSING ========================================================= PUBLIC KEY PARSING
+ @Test
+ public void serializeParsePublicKey_es256_kidIgnored_equal() throws Exception {
+ // a valid P256 point. Each coordinate is encoded in 32 bytes.
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ JwtEcdsaPublicKey key =
+ JwtEcdsaPublicKey.builder()
+ .setParameters(
+ JwtEcdsaParameters.builder()
+ .setAlgorithm(JwtEcdsaParameters.Algorithm.ES256)
+ .setKidStrategy(JwtEcdsaParameters.KidStrategy.IGNORED)
+ .build())
+ .setPublicPoint(new ECPoint(new BigInteger(hexX, 16), new BigInteger(hexY, 16)))
+ .build();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_es384_kidIgnored_equal() throws Exception {
+ // a valid P384 point. Each coordinate is encoded in 48 bytes.
+ String hexX =
+ "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64"
+ + "DEF8F0EA9055866064A254515480BC13";
+ String hexY =
+ "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1"
+ + "288B231C3AE0D4FE7344FD2533264720";
+
+ JwtEcdsaPublicKey key =
+ JwtEcdsaPublicKey.builder()
+ .setParameters(
+ JwtEcdsaParameters.builder()
+ .setAlgorithm(JwtEcdsaParameters.Algorithm.ES384)
+ .setKidStrategy(JwtEcdsaParameters.KidStrategy.IGNORED)
+ .build())
+ .setPublicPoint(new ECPoint(new BigInteger(hexX, 16), new BigInteger(hexY, 16)))
+ .build();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES384)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_es521_kidIgnored_equal() throws Exception {
+ // a valid P521 point, but encoded with leading zeros or truncated zeros.
+ String hexXTruncated =
+ "685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433"
+ + "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d";
+ String hexYWithLeadingZeros =
+ "0000000000000001ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83"
+ + "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676";
+
+ JwtEcdsaPublicKey key =
+ JwtEcdsaPublicKey.builder()
+ .setParameters(
+ JwtEcdsaParameters.builder()
+ .setAlgorithm(JwtEcdsaParameters.Algorithm.ES512)
+ .setKidStrategy(JwtEcdsaParameters.KidStrategy.IGNORED)
+ .build())
+ .setPublicPoint(
+ new ECPoint(
+ new BigInteger(hexXTruncated, 16), new BigInteger(hexYWithLeadingZeros, 16)))
+ .build();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ .setX(ByteString.copyFrom(Hex.decode(hexXTruncated)))
+ .setY(ByteString.copyFrom(Hex.decode(hexYWithLeadingZeros)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES512)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ // X and Y are currently serialized with an extra zero at the beginning. So we expect X and Y to
+ // always be encoded in 67 bytes.
+ String expectedHexX =
+ "00"
+ + "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433"
+ + "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d";
+ String expectedHexY =
+ "00"
+ + "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83"
+ + "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676";
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey expectedProtoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ .setX(ByteString.copyFrom(Hex.decode(expectedHexX)))
+ .setY(ByteString.copyFrom(Hex.decode(expectedHexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES512)
+ .build();
+ ProtoKeySerialization expectedSerialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ expectedProtoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parser(), serialized, expectedSerialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_es256_kidCustom_equal() throws Exception {
+ // a valid P256 point. Each coordinate is encoded in 32 bytes.
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ JwtEcdsaPublicKey key =
+ JwtEcdsaPublicKey.builder()
+ .setParameters(
+ JwtEcdsaParameters.builder()
+ .setAlgorithm(JwtEcdsaParameters.Algorithm.ES256)
+ .setKidStrategy(JwtEcdsaParameters.KidStrategy.CUSTOM)
+ .build())
+ .setPublicPoint(new ECPoint(new BigInteger(hexX, 16), new BigInteger(hexY, 16)))
+ .setCustomKid("weirdCustomKid")
+ .build();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .setCustomKid(CustomKid.newBuilder().setValue("weirdCustomKid").build())
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_es256_base64Kid_equal() throws Exception {
+ // a valid P256 point. Each coordinate is encoded in 32 bytes.
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ JwtEcdsaPublicKey key =
+ JwtEcdsaPublicKey.builder()
+ .setParameters(
+ JwtEcdsaParameters.builder()
+ .setAlgorithm(JwtEcdsaParameters.Algorithm.ES256)
+ .setKidStrategy(JwtEcdsaParameters.KidStrategy.BASE64_ENCODED_KEY_ID)
+ .build())
+ .setPublicPoint(new ECPoint(new BigInteger(hexX, 16), new BigInteger(hexY, 16)))
+ .setIdRequirement(12345)
+ .build();
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.parser(), serialized, serialization);
+ }
+
+ // INVALID PUBLIC KEY SERIALIZATIONS =========================== INVALID PUBLIC KEY SERIALIZATIONS
+ @Test
+ public void parsePublicKey_crunchy_cannotBeParsed_throws() throws Exception {
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.CRUNCHY,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
+
+ @Test
+ public void parsePublicKey_tinkAndCustomKeyId_throws() throws Exception {
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .setCustomKid(CustomKid.newBuilder().setValue("WillNotParseWithRAW").build())
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
+
+ @Test
+ public void parsePublicKey_wrongVersion_throws() throws Exception {
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(1)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES256)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
+
+ @Test
+ public void parsePublicKey_unknownAlgorithm_throws() throws Exception {
+ String hexX = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String hexY = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtEcdsaPublicKey.newBuilder()
+ .setVersion(0)
+ // X and Y are currently serialized with an extra zero at the beginning.
+ .setX(ByteString.copyFrom(Hex.decode("00" + hexX)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + hexY)))
+ .setAlgorithm(JwtEcdsaAlgorithm.ES_UNKNOWN)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
}