1 // Copyright 2022 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.EllipticCurvesUtil; 23 import com.google.crypto.tink.util.SecretBigInteger; 24 import com.google.errorprone.annotations.CanIgnoreReturnValue; 25 import com.google.errorprone.annotations.Immutable; 26 import com.google.errorprone.annotations.RestrictedApi; 27 import java.math.BigInteger; 28 import java.security.GeneralSecurityException; 29 import java.security.spec.ECPoint; 30 31 /** 32 * Represents a key for computing ECDSA signatures. 33 * 34 * <p>ECDSA is defined in http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf, section 6. 35 */ 36 @Immutable 37 public final class EcdsaPrivateKey extends SignaturePrivateKey { 38 private final EcdsaPublicKey publicKey; 39 private final SecretBigInteger privateValue; 40 41 /** Builder for EcdsaPrivateKey. */ 42 public static class Builder { 43 private EcdsaPublicKey publicKey = null; 44 private SecretBigInteger privateValue = null; 45 Builder()46 private Builder() {} 47 48 @CanIgnoreReturnValue setPublicKey(EcdsaPublicKey publicKey)49 public Builder setPublicKey(EcdsaPublicKey publicKey) { 50 this.publicKey = publicKey; 51 return this; 52 } 53 54 @CanIgnoreReturnValue setPrivateValue(SecretBigInteger privateValue)55 public Builder setPrivateValue(SecretBigInteger privateValue) { 56 this.privateValue = privateValue; 57 return this; 58 } 59 validatePrivateValue( BigInteger privateValue, ECPoint publicPoint, EcdsaParameters.CurveType curveType)60 private static void validatePrivateValue( 61 BigInteger privateValue, ECPoint publicPoint, EcdsaParameters.CurveType curveType) 62 throws GeneralSecurityException { 63 BigInteger order = curveType.toParameterSpec().getOrder(); 64 if ((privateValue.signum() <= 0) || (privateValue.compareTo(order) >= 0)) { 65 throw new GeneralSecurityException("Invalid private value"); 66 } 67 ECPoint p = EllipticCurvesUtil.multiplyByGenerator(privateValue, curveType.toParameterSpec()); 68 if (!p.equals(publicPoint)) { 69 throw new GeneralSecurityException("Invalid private value"); 70 } 71 } 72 73 @AccessesPartialKey build()74 public EcdsaPrivateKey build() throws GeneralSecurityException { 75 if (publicKey == null) { 76 throw new GeneralSecurityException("Cannot build without a ecdsa public key"); 77 } 78 if (privateValue == null) { 79 throw new GeneralSecurityException("Cannot build without a private value"); 80 } 81 validatePrivateValue( 82 privateValue.getBigInteger(InsecureSecretKeyAccess.get()), 83 publicKey.getPublicPoint(), 84 publicKey.getParameters().getCurveType()); 85 return new EcdsaPrivateKey(publicKey, privateValue); 86 } 87 } 88 EcdsaPrivateKey(EcdsaPublicKey publicKey, SecretBigInteger privateValue)89 private EcdsaPrivateKey(EcdsaPublicKey publicKey, SecretBigInteger privateValue) { 90 this.publicKey = publicKey; 91 this.privateValue = privateValue; 92 } 93 94 @RestrictedApi( 95 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 96 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 97 allowedOnPath = ".*Test\\.java", 98 allowlistAnnotations = {AccessesPartialKey.class}) builder()99 public static Builder builder() { 100 return new Builder(); 101 } 102 103 @Override getParameters()104 public EcdsaParameters getParameters() { 105 return publicKey.getParameters(); 106 } 107 108 @Override getPublicKey()109 public EcdsaPublicKey getPublicKey() { 110 return publicKey; 111 } 112 113 @RestrictedApi( 114 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 115 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 116 allowedOnPath = ".*Test\\.java", 117 allowlistAnnotations = {AccessesPartialKey.class}) getPrivateValue()118 public SecretBigInteger getPrivateValue() { 119 return privateValue; 120 } 121 122 @Override equalsKey(Key o)123 public boolean equalsKey(Key o) { 124 if (!(o instanceof EcdsaPrivateKey)) { 125 return false; 126 } 127 EcdsaPrivateKey that = (EcdsaPrivateKey) o; 128 return that.publicKey.equalsKey(publicKey) 129 && privateValue.equalsSecretBigInteger(that.privateValue); 130 } 131 } 132