aboutsummaryrefslogtreecommitdiff
path: root/java_src
diff options
context:
space:
mode:
authortholenst <tholenst@google.com>2023-07-21 07:10:17 -0700
committerCopybara-Service <copybara-worker@google.com>2023-07-21 07:11:31 -0700
commit0b93273056d1d2e82d652d9871125add33623e52 (patch)
treee63c4e458498fab2e1b9f7027d83f3dcc7fef615 /java_src
parent7eab20215cd36782545257b9683d4d66d14f58bd (diff)
downloadtink-0b93273056d1d2e82d652d9871125add33623e52.tar.gz
Add serialization for Ecies Public Keys.
PiperOrigin-RevId: 549938077
Diffstat (limited to 'java_src')
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/hybrid/BUILD.bazel12
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/hybrid/EciesProtoSerialization.java122
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/hybrid/BUILD.bazel8
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/hybrid/EciesProtoSerializationTest.java425
4 files changed, 561 insertions, 6 deletions
diff --git a/java_src/src/main/java/com/google/crypto/tink/hybrid/BUILD.bazel b/java_src/src/main/java/com/google/crypto/tink/hybrid/BUILD.bazel
index 12d9f317f..90ff37cfa 100644
--- a/java_src/src/main/java/com/google/crypto/tink/hybrid/BUILD.bazel
+++ b/java_src/src/main/java/com/google/crypto/tink/hybrid/BUILD.bazel
@@ -330,15 +330,21 @@ java_library(
srcs = ["EciesProtoSerialization.java"],
deps = [
":ecies_parameters",
+ ":ecies_public_key",
"//proto:common_java_proto",
"//proto:ecies_aead_hkdf_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:tink_proto_parameters_format",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding",
"//src/main/java/com/google/crypto/tink/internal:enum_type_proto_converter",
+ "//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",
@@ -659,15 +665,21 @@ android_library(
srcs = ["EciesProtoSerialization.java"],
deps = [
":ecies_parameters-android",
+ ":ecies_public_key-android",
"//proto:common_java_proto_lite",
"//proto:ecies_aead_hkdf_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:tink_proto_parameters_format-android",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding-android",
"//src/main/java/com/google/crypto/tink/internal:enum_type_proto_converter-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",
diff --git a/java_src/src/main/java/com/google/crypto/tink/hybrid/EciesProtoSerialization.java b/java_src/src/main/java/com/google/crypto/tink/hybrid/EciesProtoSerialization.java
index 604ad11d9..b318063cb 100644
--- a/java_src/src/main/java/com/google/crypto/tink/hybrid/EciesProtoSerialization.java
+++ b/java_src/src/main/java/com/google/crypto/tink/hybrid/EciesProtoSerialization.java
@@ -19,23 +19,30 @@ package com.google.crypto.tink.hybrid;
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.TinkProtoParametersFormat;
+import com.google.crypto.tink.internal.BigIntegerEncoding;
import com.google.crypto.tink.internal.EnumTypeProtoConverter;
+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.EcPointFormat;
import com.google.crypto.tink.proto.EciesAeadHkdfKeyFormat;
import com.google.crypto.tink.proto.EciesAeadHkdfParams;
import com.google.crypto.tink.proto.EllipticCurveType;
import com.google.crypto.tink.proto.HashType;
+import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
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 EciesParameters} objects. */
@@ -46,6 +53,10 @@ final class EciesProtoSerialization {
"type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey";
private static final Bytes PRIVATE_TYPE_URL_BYTES = toBytesFromPrintableAscii(PRIVATE_TYPE_URL);
+ private static final String PUBLIC_TYPE_URL =
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey";
+ private static final Bytes PUBLIC_TYPE_URL_BYTES = toBytesFromPrintableAscii(PUBLIC_TYPE_URL);
+
private static final ParametersSerializer<EciesParameters, ProtoParametersSerialization>
PARAMETERS_SERIALIZER =
ParametersSerializer.create(
@@ -59,6 +70,18 @@ final class EciesProtoSerialization {
PRIVATE_TYPE_URL_BYTES,
ProtoParametersSerialization.class);
+ private static final KeySerializer<EciesPublicKey, ProtoKeySerialization> PUBLIC_KEY_SERIALIZER =
+ KeySerializer.create(
+ EciesProtoSerialization::serializePublicKey,
+ EciesPublicKey.class,
+ ProtoKeySerialization.class);
+
+ private static final KeyParser<ProtoKeySerialization> PUBLIC_KEY_PARSER =
+ KeyParser.create(
+ EciesProtoSerialization::parsePublicKey,
+ PUBLIC_TYPE_URL_BYTES,
+ ProtoKeySerialization.class);
+
private static final EnumTypeProtoConverter<OutputPrefixType, EciesParameters.Variant>
VARIANT_CONVERTER =
EnumTypeProtoConverter.<OutputPrefixType, EciesParameters.Variant>builder()
@@ -85,7 +108,7 @@ final class EciesProtoSerialization {
.add(EllipticCurveType.NIST_P521, EciesParameters.CurveType.NIST_P521)
.add(EllipticCurveType.CURVE25519, EciesParameters.CurveType.X25519)
.build();
-
+
private static final EnumTypeProtoConverter<EcPointFormat, EciesParameters.PointFormat>
POINT_FORMAT_CONVERTER =
EnumTypeProtoConverter.<EcPointFormat, EciesParameters.PointFormat>builder()
@@ -109,6 +132,8 @@ final class EciesProtoSerialization {
throws GeneralSecurityException {
registry.registerParametersSerializer(PARAMETERS_SERIALIZER);
registry.registerParametersParser(PARAMETERS_PARSER);
+ registry.registerKeySerializer(PUBLIC_KEY_SERIALIZER);
+ registry.registerKeyParser(PUBLIC_KEY_PARSER);
}
private static com.google.crypto.tink.proto.EciesAeadHkdfParams toProtoParameters(
@@ -205,5 +230,100 @@ final class EciesProtoSerialization {
serialization.getKeyTemplate().getOutputPrefixType(), format.getParams());
}
+ private static int getEncodingLength(EciesParameters.CurveType curveType)
+ 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.
+ // See also b/264525021.
+ if (EciesParameters.CurveType.NIST_P256.equals(curveType)) {
+ return 33;
+ }
+ if (EciesParameters.CurveType.NIST_P384.equals(curveType)) {
+ return 49;
+ }
+ if (EciesParameters.CurveType.NIST_P521.equals(curveType)) {
+ return 67;
+ }
+ throw new GeneralSecurityException("Unable to serialize CurveType " + curveType);
+ }
+
+ private static com.google.crypto.tink.proto.EciesAeadHkdfPublicKey getProtoPublicKey(
+ EciesPublicKey key) throws GeneralSecurityException {
+ if (key.getParameters().getCurveType().equals(EciesParameters.CurveType.X25519)) {
+ return com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(toProtoParameters(key.getParameters()))
+ .setX(ByteString.copyFrom(key.getX25519CurvePointBytes().toByteArray()))
+ .setY(ByteString.EMPTY)
+ .build();
+ }
+
+ int encLength = getEncodingLength(key.getParameters().getCurveType());
+ ECPoint publicPoint = key.getNistCurvePoint();
+ if (publicPoint == null) {
+ throw new GeneralSecurityException("NistCurvePoint was null for NIST curve");
+ }
+ return com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(toProtoParameters(key.getParameters()))
+ .setX(
+ ByteString.copyFrom(
+ BigIntegerEncoding.toBigEndianBytesOfFixedLength(
+ publicPoint.getAffineX(), encLength)))
+ .setY(
+ ByteString.copyFrom(
+ BigIntegerEncoding.toBigEndianBytesOfFixedLength(
+ publicPoint.getAffineY(), encLength)))
+ .build();
+ }
+
+ @SuppressWarnings("UnusedException")
+ private static EciesPublicKey parsePublicKey(
+ ProtoKeySerialization serialization, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ if (!serialization.getTypeUrl().equals(PUBLIC_TYPE_URL)) {
+ throw new IllegalArgumentException(
+ "Wrong type URL in call to EciesPublicKey.parsePublicKey: " + serialization.getTypeUrl());
+ }
+ try {
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey protoKey =
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parseFrom(
+ serialization.getValue(), ExtensionRegistryLite.getEmptyRegistry());
+ if (protoKey.getVersion() != 0) {
+ throw new GeneralSecurityException("Only version 0 keys are accepted");
+ }
+ EciesParameters parameters =
+ fromProtoParameters(serialization.getOutputPrefixType(), protoKey.getParams());
+ if (parameters.getCurveType().equals(EciesParameters.CurveType.X25519)) {
+ if (!protoKey.getY().isEmpty()) {
+ throw new GeneralSecurityException("Y must be empty for X25519 points");
+ }
+ return EciesPublicKey.createForCurveX25519(
+ parameters,
+ Bytes.copyFrom(protoKey.getX().toByteArray()),
+ serialization.getIdRequirementOrNull());
+ }
+ ECPoint point =
+ new ECPoint(
+ BigIntegerEncoding.fromUnsignedBigEndianBytes(protoKey.getX().toByteArray()),
+ BigIntegerEncoding.fromUnsignedBigEndianBytes(protoKey.getY().toByteArray()));
+
+ return EciesPublicKey.createForNistCurve(
+ parameters, point, serialization.getIdRequirementOrNull());
+ } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
+ throw new GeneralSecurityException("Parsing EcdsaPublicKey failed");
+ }
+ }
+
+ private static ProtoKeySerialization serializePublicKey(
+ EciesPublicKey key, @Nullable SecretKeyAccess access) throws GeneralSecurityException {
+ return ProtoKeySerialization.create(
+ PUBLIC_TYPE_URL,
+ getProtoPublicKey(key).toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ VARIANT_CONVERTER.toProtoEnum(key.getParameters().getVariant()),
+ key.getIdRequirementOrNull());
+ }
+
private EciesProtoSerialization() {}
}
diff --git a/java_src/src/test/java/com/google/crypto/tink/hybrid/BUILD.bazel b/java_src/src/test/java/com/google/crypto/tink/hybrid/BUILD.bazel
index b88aced42..c8e4506e5 100644
--- a/java_src/src/test/java/com/google/crypto/tink/hybrid/BUILD.bazel
+++ b/java_src/src/test/java/com/google/crypto/tink/hybrid/BUILD.bazel
@@ -385,16 +385,24 @@ java_test(
"//proto:ecies_aead_hkdf_java_proto",
"//proto:tink_java_proto",
"//proto:xchacha20_poly1305_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:key_templates",
"//src/main/java/com/google/crypto/tink:parameters",
"//src/main/java/com/google/crypto/tink/aead:aead_config",
"//src/main/java/com/google/crypto/tink/aead:x_cha_cha20_poly1305_parameters",
"//src/main/java/com/google/crypto/tink/hybrid:ecies_parameters",
"//src/main/java/com/google/crypto/tink/hybrid:ecies_proto_serialization",
+ "//src/main/java/com/google/crypto/tink/hybrid:ecies_public_key",
+ "//src/main/java/com/google/crypto/tink/internal:key_template_proto_converter",
"//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:tink_bug_exception",
"//src/main/java/com/google/crypto/tink/internal/testing:asserts",
"//src/main/java/com/google/crypto/tink/subtle:hex",
+ "//src/main/java/com/google/crypto/tink/subtle:random",
+ "//src/main/java/com/google/crypto/tink/subtle:x25519",
"//src/main/java/com/google/crypto/tink/util:bytes",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_protobuf_protobuf_java",
diff --git a/java_src/src/test/java/com/google/crypto/tink/hybrid/EciesProtoSerializationTest.java b/java_src/src/test/java/com/google/crypto/tink/hybrid/EciesProtoSerializationTest.java
index c63747de5..0028cc4af 100644
--- a/java_src/src/test/java/com/google/crypto/tink/hybrid/EciesProtoSerializationTest.java
+++ b/java_src/src/test/java/com/google/crypto/tink/hybrid/EciesProtoSerializationTest.java
@@ -21,27 +21,37 @@ import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug;
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.KeyTemplates;
import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.aead.XChaCha20Poly1305Parameters;
+import com.google.crypto.tink.internal.KeyTemplateProtoConverter;
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.EcPointFormat;
import com.google.crypto.tink.proto.EciesAeadDemParams;
import com.google.crypto.tink.proto.EciesAeadHkdfKeyFormat;
import com.google.crypto.tink.proto.EciesAeadHkdfParams;
+import com.google.crypto.tink.proto.EciesAeadHkdfPublicKey;
import com.google.crypto.tink.proto.EciesHkdfKemParams;
import com.google.crypto.tink.proto.EllipticCurveType;
import com.google.crypto.tink.proto.HashType;
+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.proto.XChaCha20Poly1305KeyFormat;
import com.google.crypto.tink.subtle.Hex;
+import com.google.crypto.tink.subtle.Random;
+import com.google.crypto.tink.subtle.X25519;
import com.google.crypto.tink.util.Bytes;
import com.google.protobuf.ByteString;
+import java.math.BigInteger;
import java.security.GeneralSecurityException;
+import java.security.spec.ECPoint;
import javax.annotation.Nullable;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.FromDataPoints;
@@ -118,10 +128,13 @@ public final class EciesProtoSerializationTest {
.build();
}
- @BeforeClass
- public static void setUp() throws Exception {
- AeadConfig.register();
- EciesProtoSerialization.register(registry);
+ static {
+ try {
+ AeadConfig.register();
+ EciesProtoSerialization.register(registry);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Test
@@ -408,4 +421,406 @@ public final class EciesProtoSerializationTest {
assertThrows(
GeneralSecurityException.class, () -> registry.parseParameters(serializedParameters));
}
+
+ // PUBLIC KEY SERIALIZATION ======================================================================
+ private static EciesAeadHkdfParams validParamsForCurve(EllipticCurveType curveType)
+ throws GeneralSecurityException {
+ EciesHkdfKemParams kemParams =
+ EciesHkdfKemParams.newBuilder()
+ .setCurveType(curveType)
+ .setHkdfHashType(HashType.SHA256)
+ .setHkdfSalt(ByteString.copyFrom(SALT.toByteArray()))
+ .build();
+ KeyTemplate demKeyTemplate =
+ KeyTemplateProtoConverter.toProto(KeyTemplates.get("XCHACHA20_POLY1305_RAW"));
+ EciesAeadDemParams demParams =
+ EciesAeadDemParams.newBuilder().setAeadDem(demKeyTemplate).build();
+ return EciesAeadHkdfParams.newBuilder()
+ .setKemParams(kemParams)
+ .setDemParams(demParams)
+ .setEcPointFormat(com.google.crypto.tink.proto.EcPointFormat.COMPRESSED)
+ .build();
+ }
+
+ @Test
+ public void serializeParsePublicKey_p256_tink_equal() throws Exception {
+ String pointXHex = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String pointYHex = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+ ECPoint someP256PublicPoint =
+ new ECPoint(new BigInteger(pointXHex, 16), new BigInteger(pointYHex, 16));
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.NIST_P256)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
+ .setVariant(EciesParameters.Variant.TINK)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForNistCurve(parameters, someP256PublicPoint, 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(
+ validParamsForCurve(com.google.crypto.tink.proto.EllipticCurveType.NIST_P256))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(publicKey, ProtoKeySerialization.class, /* access= */ null);
+
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_p256_crunchy_equal() throws Exception {
+ String pointXHex = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String pointYHex = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+ ECPoint someP256PublicPoint =
+ new ECPoint(new BigInteger(pointXHex, 16), new BigInteger(pointYHex, 16));
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.NIST_P256)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
+ .setVariant(EciesParameters.Variant.CRUNCHY)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForNistCurve(parameters, someP256PublicPoint, 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(validParamsForCurve(EllipticCurveType.NIST_P256))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.CRUNCHY,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(publicKey, ProtoKeySerialization.class, /* access= */ null);
+
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_p384_tink_equal() throws Exception {
+ String pointXHex =
+ "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e764592efda27fe7513272"
+ + "734466b400091adbf2d68c58e0c50066";
+ String pointYHex =
+ "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b661efedf243451915e"
+ + "d0905a32b060992b468c64766fc8437a";
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.NIST_P384)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
+ .setVariant(EciesParameters.Variant.TINK)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForNistCurve(
+ parameters,
+ new ECPoint(new BigInteger(pointXHex, 16), new BigInteger(pointYHex, 16)),
+ 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(
+ validParamsForCurve(com.google.crypto.tink.proto.EllipticCurveType.NIST_P384))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(publicKey, ProtoKeySerialization.class, /* access= */ null);
+
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_p521_tink_equal() throws Exception {
+ String pointXHex =
+ "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a9490340"
+ + "854334b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2"
+ + "046d";
+ String pointYHex =
+ "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b7398"
+ + "84a83bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302"
+ + "f676";
+ ECPoint someP521PublicPoint =
+ new ECPoint(new BigInteger(pointXHex, 16), new BigInteger(pointYHex, 16));
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.NIST_P521)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
+ .setVariant(EciesParameters.Variant.TINK)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForNistCurve(parameters, someP521PublicPoint, 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(
+ validParamsForCurve(com.google.crypto.tink.proto.EllipticCurveType.NIST_P521))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(publicKey, ProtoKeySerialization.class, /* access= */ null);
+
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void parsePublicKey_parsingIgnoresZeroes_works() throws Exception {
+ String pointXHex =
+ "0000000000685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a9490340"
+ + "854334b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2"
+ + "046d";
+ String pointYHex =
+ "0000000001ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b7398"
+ + "84a83bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302"
+ + "f676";
+ ECPoint someP521PublicPoint =
+ new ECPoint(new BigInteger(pointXHex, 16), new BigInteger(pointYHex, 16));
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.NIST_P521)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
+ .setVariant(EciesParameters.Variant.TINK)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForNistCurve(parameters, someP521PublicPoint, 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(
+ validParamsForCurve(com.google.crypto.tink.proto.EllipticCurveType.NIST_P521))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+ }
+
+ @Test
+ public void serializeParsePublicKey_x25519_equal() throws Exception {
+ Bytes publicPointBytes = Bytes.copyFrom(X25519.publicFromPrivate(X25519.generatePrivateKey()));
+
+ // Java object
+ EciesParameters parameters =
+ EciesParameters.builder()
+ .setCurveType(EciesParameters.CurveType.X25519)
+ .setHashType(EciesParameters.HashType.SHA256)
+ .setVariant(EciesParameters.Variant.TINK)
+ .setDemParameters(DEM_PARAMETERS)
+ .setSalt(SALT)
+ .build();
+ EciesPublicKey publicKey =
+ EciesPublicKey.createForCurveX25519(parameters, publicPointBytes, 101);
+
+ // Proto object
+ EciesAeadHkdfPublicKey protoPublicKey =
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(validParamsForCurve(EllipticCurveType.CURVE25519))
+ .setX(ByteString.copyFrom(publicPointBytes.toByteArray()))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101);
+
+ // Comparison
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.getParameters()).isEqualTo(publicKey.getParameters());
+ assertThat(parsed.equalsKey(publicKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(publicKey, ProtoKeySerialization.class, /* access= */ null);
+
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.EciesAeadHkdfPublicKey.parser(), serialized, serialization);
+ }
+
+ private static ProtoKeySerialization[] createInvalidPublicKeySerializations() {
+ try {
+ String pointXHex = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
+ String pointYHex = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
+
+ return new ProtoKeySerialization[] {
+ // Point not on curve.
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(validParamsForCurve(EllipticCurveType.NIST_P256))
+ // pointXHex + 1
+ .setX(
+ ByteString.copyFrom(
+ Hex.decode(
+ "00700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d288")))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build()
+ .toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101),
+ // Bad version
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(1)
+ .setParams(validParamsForCurve(EllipticCurveType.NIST_P256))
+ .setX(ByteString.copyFrom(Hex.decode("00" + pointXHex)))
+ .setY(ByteString.copyFrom(Hex.decode("00" + pointYHex)))
+ .build()
+ .toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101),
+ // X25519 Curve with Y set
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(validParamsForCurve(EllipticCurveType.CURVE25519))
+ .setX(ByteString.copyFrom(Random.randBytes(32)))
+ .setY(ByteString.copyFrom(Random.randBytes(32)))
+ .build()
+ .toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101),
+ // X25519 Curve with EC Point Format uncompressed
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
+ EciesAeadHkdfPublicKey.newBuilder()
+ .setVersion(0)
+ .setParams(
+ validParamsForCurve(EllipticCurveType.CURVE25519).toBuilder()
+ .setEcPointFormat(EcPointFormat.UNCOMPRESSED)
+ .build())
+ .setX(ByteString.copyFrom(Random.randBytes(32)))
+ .build()
+ .toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 101),
+ };
+
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @DataPoints("invalidPublicKeySerializations")
+ public static final ProtoKeySerialization[] INVALID_PUBLIC_KEY_SERIALIZATIONS =
+ createInvalidPublicKeySerializations();
+
+ @Theory
+ public void testParseInvalidPublicKeys_throws(
+ @FromDataPoints("invalidPublicKeySerializations") ProtoKeySerialization serialization)
+ throws Exception {
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
}