// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package com.google.crypto.tink.signature; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.internal.KeyTester; import com.google.crypto.tink.subtle.Hex; import com.google.crypto.tink.util.Bytes; import com.google.crypto.tink.util.SecretBytes; import java.security.GeneralSecurityException; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class Ed25519PrivateKeyTest { // Test case from https://www.rfc-editor.org/rfc/rfc8032#page-24 private static final byte[] SECRET_KEY = Hex.decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); private static final byte[] PUBLIC_KEY = Hex.decode("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"); private static final SecretBytes PRIVATE_KEY_BYTES = SecretBytes.copyFrom(SECRET_KEY, InsecureSecretKeyAccess.get()); private static final Bytes PUBLIC_KEY_BYTES = Bytes.copyFrom(PUBLIC_KEY); @Test public void createNoPrefixVariantAndGetProperties() throws Exception { Ed25519PublicKey publicKey = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); Ed25519PrivateKey privateKey = Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES); assertThat(privateKey.getParameters()).isEqualTo(Ed25519Parameters.create()); assertThat(privateKey.getPrivateKeyBytes()).isEqualTo(PRIVATE_KEY_BYTES); assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); assertThat(privateKey.getOutputPrefix()).isEqualTo(Bytes.copyFrom(new byte[] {})); assertThat(privateKey.getIdRequirementOrNull()).isNull(); } @Test public void createTinkVariantAndGetProperties() throws Exception { Ed25519PublicKey publicKey = Ed25519PublicKey.create( Ed25519Parameters.Variant.TINK, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); Ed25519PrivateKey privateKey = Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES); assertThat(privateKey.getParameters()) .isEqualTo(Ed25519Parameters.create(Ed25519Parameters.Variant.TINK)); assertThat(privateKey.getPrivateKeyBytes()).isEqualTo(PRIVATE_KEY_BYTES); assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); assertThat(privateKey.getOutputPrefix()) .isEqualTo(Bytes.copyFrom(new byte[] {0x01, 0x07, 0x08, 0x09, 0x0a})); assertThat(privateKey.getIdRequirementOrNull()).isEqualTo(0x708090a); } @Test public void createCrunchyVariantAndGetProperties() throws Exception { Ed25519PublicKey publicKey = Ed25519PublicKey.create( Ed25519Parameters.Variant.CRUNCHY, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); Ed25519PrivateKey privateKey = Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES); assertThat(privateKey.getParameters()) .isEqualTo(Ed25519Parameters.create(Ed25519Parameters.Variant.CRUNCHY)); assertThat(privateKey.getPrivateKeyBytes()).isEqualTo(PRIVATE_KEY_BYTES); assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); assertThat(privateKey.getOutputPrefix()) .isEqualTo(Bytes.copyFrom(new byte[] {0x00, 0x07, 0x08, 0x09, 0x0a})); assertThat(privateKey.getIdRequirementOrNull()).isEqualTo(0x708090a); } @Test public void createLegacyVariantAndGetProperties() throws Exception { Ed25519PublicKey publicKey = Ed25519PublicKey.create( Ed25519Parameters.Variant.LEGACY, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); Ed25519PrivateKey privateKey = Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES); assertThat(privateKey.getParameters()) .isEqualTo(Ed25519Parameters.create(Ed25519Parameters.Variant.LEGACY)); assertThat(privateKey.getPrivateKeyBytes()).isEqualTo(PRIVATE_KEY_BYTES); assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); assertThat(privateKey.getOutputPrefix()) .isEqualTo(Bytes.copyFrom(new byte[] {0x00, 0x07, 0x08, 0x09, 0x0a})); assertThat(privateKey.getIdRequirementOrNull()).isEqualTo(0x708090a); } @Test public void invalidKeySize() throws Exception { SecretBytes invalidSizePrivateKeyBytes = SecretBytes.randomBytes(64); Ed25519PublicKey publicKey = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); assertThrows( GeneralSecurityException.class, () -> Ed25519PrivateKey.create(publicKey, invalidSizePrivateKeyBytes)); } @Test public void keysMismatch_fails() throws Exception { SecretBytes invalidPrivateKeyBytes = SecretBytes.randomBytes(32); Ed25519PublicKey publicKey = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); assertThrows( GeneralSecurityException.class, () -> Ed25519PrivateKey.create(publicKey, invalidPrivateKeyBytes)); } @Test public void nullPublicKey() throws Exception { assertThrows( GeneralSecurityException.class, () -> Ed25519PrivateKey.create(null, PRIVATE_KEY_BYTES)); } @Test public void testEqualities() throws Exception { SecretBytes privateKeyBytesCopy = SecretBytes.copyFrom( PRIVATE_KEY_BYTES.toByteArray(InsecureSecretKeyAccess.get()), InsecureSecretKeyAccess.get()); Ed25519PublicKey publicKey = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); Ed25519PublicKey publicKeyCopy = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); // Test case from https://www.rfc-editor.org/rfc/rfc8032#page-24 Bytes publicKeyBytesDiff = Bytes.copyFrom( Hex.decode("3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c")); Ed25519PublicKey publicKeyDiff = Ed25519PublicKey.create(publicKeyBytesDiff); SecretBytes privateKeyBytesDiff = SecretBytes.copyFrom( Hex.decode("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"), InsecureSecretKeyAccess.get()); Ed25519PublicKey publicKeyTink = Ed25519PublicKey.create( Ed25519Parameters.Variant.TINK, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); Ed25519PublicKey publicKeyCrunchy = Ed25519PublicKey.create( Ed25519Parameters.Variant.CRUNCHY, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); Ed25519PublicKey publicKeyLegacy = Ed25519PublicKey.create( Ed25519Parameters.Variant.LEGACY, PUBLIC_KEY_BYTES, /* idRequirement= */ 0x0708090a); new KeyTester() .addEqualityGroup( "No prefix, keyBytes", Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES), Ed25519PrivateKey.create(publicKey, privateKeyBytesCopy), Ed25519PrivateKey.create(publicKeyCopy, PRIVATE_KEY_BYTES)) .addEqualityGroup( "No prefix, different key bytes", Ed25519PrivateKey.create(publicKeyDiff, privateKeyBytesDiff)) .addEqualityGroup( "Tink public key, keyBytes", Ed25519PrivateKey.create(publicKeyTink, PRIVATE_KEY_BYTES)) .addEqualityGroup( "Crunchy public key, keyBytes", Ed25519PrivateKey.create(publicKeyCrunchy, PRIVATE_KEY_BYTES)) .addEqualityGroup( "Legacy public key, keyBytes", Ed25519PrivateKey.create(publicKeyLegacy, PRIVATE_KEY_BYTES)) .doTests(); } @Test public void testDifferentKeyTypesEquality_fails() throws Exception { Ed25519PublicKey publicKey = Ed25519PublicKey.create(PUBLIC_KEY_BYTES); Ed25519PrivateKey privateKey = Ed25519PrivateKey.create(publicKey, PRIVATE_KEY_BYTES); assertThat(privateKey.equalsKey(publicKey)).isFalse(); } }