1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.signature; 18 19 import com.google.crypto.tink.AccessesPartialKey; 20 import com.google.crypto.tink.InsecureSecretKeyAccess; 21 import com.google.crypto.tink.Key; 22 import com.google.crypto.tink.internal.Ed25519; 23 import com.google.crypto.tink.util.SecretBytes; 24 import com.google.errorprone.annotations.Immutable; 25 import com.google.errorprone.annotations.RestrictedApi; 26 import java.security.GeneralSecurityException; 27 import java.util.Arrays; 28 29 /** The key for computing Ed25519 signatures. */ 30 @Immutable 31 public final class Ed25519PrivateKey extends SignaturePrivateKey { 32 private final Ed25519PublicKey publicKey; 33 private final SecretBytes privateKeyBytes; 34 Ed25519PrivateKey(Ed25519PublicKey publicKey, SecretBytes privateKeyBytes)35 private Ed25519PrivateKey(Ed25519PublicKey publicKey, SecretBytes privateKeyBytes) { 36 this.publicKey = publicKey; 37 this.privateKeyBytes = privateKeyBytes; 38 } 39 40 @AccessesPartialKey 41 @RestrictedApi( 42 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 43 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 44 allowedOnPath = ".*Test\\.java", 45 allowlistAnnotations = {AccessesPartialKey.class}) create(Ed25519PublicKey publicKey, SecretBytes privateKeyBytes)46 public static Ed25519PrivateKey create(Ed25519PublicKey publicKey, SecretBytes privateKeyBytes) 47 throws GeneralSecurityException { 48 if (publicKey == null) { 49 throw new GeneralSecurityException( 50 "Ed25519 key cannot be constructed without an Ed25519 public key"); 51 } 52 if (privateKeyBytes.size() != 32) { 53 throw new GeneralSecurityException( 54 "Ed25519 key must be constructed with key of length 32 bytes, not " 55 + privateKeyBytes.size()); 56 } 57 58 // Validate private key based on the public key bytes. 59 byte[] publicKeyBytes = publicKey.getPublicKeyBytes().toByteArray(); 60 byte[] secretSeed = privateKeyBytes.toByteArray(InsecureSecretKeyAccess.get()); 61 byte[] expectedPublicKeyBytes = 62 Ed25519.scalarMultWithBaseToBytes(Ed25519.getHashedScalar(secretSeed)); 63 64 if (!Arrays.equals(publicKeyBytes, expectedPublicKeyBytes)) { 65 throw new GeneralSecurityException("Ed25519 keys mismatch"); 66 } 67 68 return new Ed25519PrivateKey(publicKey, privateKeyBytes); 69 } 70 71 @Override getParameters()72 public Ed25519Parameters getParameters() { 73 return publicKey.getParameters(); 74 } 75 76 @Override getPublicKey()77 public Ed25519PublicKey getPublicKey() { 78 return publicKey; 79 } 80 81 @RestrictedApi( 82 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 83 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 84 allowedOnPath = ".*Test\\.java", 85 allowlistAnnotations = {AccessesPartialKey.class}) getPrivateKeyBytes()86 public SecretBytes getPrivateKeyBytes() { 87 return privateKeyBytes; 88 } 89 90 @Override equalsKey(Key o)91 public boolean equalsKey(Key o) { 92 if (!(o instanceof Ed25519PrivateKey)) { 93 return false; 94 } 95 Ed25519PrivateKey that = (Ed25519PrivateKey) o; 96 return that.publicKey.equalsKey(publicKey) 97 && privateKeyBytes.equalsSecretBytes(that.privateKeyBytes); 98 } 99 } 100