diff options
author | tholenst <tholenst@google.com> | 2023-07-21 07:10:17 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-07-21 07:11:31 -0700 |
commit | 0b93273056d1d2e82d652d9871125add33623e52 (patch) | |
tree | e63c4e458498fab2e1b9f7027d83f3dcc7fef615 /java_src | |
parent | 7eab20215cd36782545257b9683d4d66d14f58bd (diff) | |
download | tink-0b93273056d1d2e82d652d9871125add33623e52.tar.gz |
Add serialization for Ecies Public Keys.
PiperOrigin-RevId: 549938077
Diffstat (limited to 'java_src')
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())); + } } |