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 static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.crypto.tink.InsecureSecretKeyAccess; 23 import com.google.crypto.tink.internal.KeyTester; 24 import com.google.crypto.tink.subtle.Hex; 25 import com.google.crypto.tink.util.Bytes; 26 import com.google.crypto.tink.util.SecretBigInteger; 27 import java.math.BigInteger; 28 import java.security.GeneralSecurityException; 29 import java.security.KeyFactory; 30 import java.security.KeyPair; 31 import java.security.KeyPairGenerator; 32 import java.security.interfaces.ECPrivateKey; 33 import java.security.interfaces.ECPublicKey; 34 import java.security.spec.ECPoint; 35 import java.security.spec.ECPrivateKeySpec; 36 import java.security.spec.ECPublicKeySpec; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 import org.junit.runners.JUnit4; 40 41 @RunWith(JUnit4.class) 42 public final class EcdsaPrivateKeyTest { 43 44 // Test case from https://www.ietf.org/rfc/rfc6979.txt, A.2.5 45 private static final ECPoint P256_PUBLIC_POINT = 46 new ECPoint( 47 new BigInteger("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6", 16), 48 new BigInteger("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299", 16)); 49 private static final BigInteger P256_PRIVATE_VALUE = 50 new BigInteger("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721", 16); 51 52 // Test case from https://www.ietf.org/rfc/rfc6979.txt, A.2.5 53 private static final ECPoint P521_PUBLIC_POINT = 54 new ECPoint( 55 new BigInteger( 56 "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD3" 57 + "71123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F502" 58 + "3A4", 59 16), 60 new BigInteger( 61 "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A2" 62 + "8A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDF" 63 + "CF5", 64 16)); 65 private static final BigInteger P521_PRIVATE_VALUE = 66 new BigInteger( 67 "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C" 68 + "AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83" 69 + "538", 70 16); 71 72 @Test buildNoPrefixVariantAndGetProperties()73 public void buildNoPrefixVariantAndGetProperties() throws Exception { 74 EcdsaParameters parameters = 75 EcdsaParameters.builder() 76 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 77 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 78 .setHashType(EcdsaParameters.HashType.SHA256) 79 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 80 .build(); 81 assertThat(parameters.hasIdRequirement()).isFalse(); 82 EcdsaPublicKey publicKey = 83 EcdsaPublicKey.builder() 84 .setParameters(parameters) 85 .setPublicPoint(P256_PUBLIC_POINT) 86 .build(); 87 EcdsaPrivateKey privateKey = 88 EcdsaPrivateKey.builder() 89 .setPublicKey(publicKey) 90 .setPrivateValue( 91 SecretBigInteger.fromBigInteger(P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 92 .build(); 93 assertThat(privateKey.getParameters()).isEqualTo(parameters); 94 assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); 95 assertThat(privateKey.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get())) 96 .isEqualTo(P256_PRIVATE_VALUE); 97 assertThat(privateKey.getOutputPrefix()).isEqualTo(Bytes.copyFrom(new byte[] {})); 98 assertThat(privateKey.getIdRequirementOrNull()).isNull(); 99 } 100 101 @Test buildTinkVariantAndGetProperties()102 public void buildTinkVariantAndGetProperties() throws Exception { 103 EcdsaParameters parameters = 104 EcdsaParameters.builder() 105 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 106 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 107 .setHashType(EcdsaParameters.HashType.SHA256) 108 .setVariant(EcdsaParameters.Variant.TINK) 109 .build(); 110 assertThat(parameters.hasIdRequirement()).isTrue(); 111 EcdsaPublicKey publicKey = 112 EcdsaPublicKey.builder() 113 .setParameters(parameters) 114 .setPublicPoint(P256_PUBLIC_POINT) 115 .setIdRequirement(0x66AABBCC) 116 .build(); 117 EcdsaPrivateKey privateKey = 118 EcdsaPrivateKey.builder() 119 .setPublicKey(publicKey) 120 .setPrivateValue( 121 SecretBigInteger.fromBigInteger(P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 122 .build(); 123 assertThat(privateKey.getParameters()).isEqualTo(parameters); 124 assertThat(privateKey.getPublicKey()).isEqualTo(publicKey); 125 assertThat(privateKey.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get())) 126 .isEqualTo(P256_PRIVATE_VALUE); 127 assertThat(privateKey.getOutputPrefix()).isEqualTo(Bytes.copyFrom(Hex.decode("0166AABBCC"))); 128 assertThat(privateKey.getIdRequirementOrNull()).isEqualTo(0x66AABBCC); 129 } 130 131 @Test convertToAndFromJavaECKeys()132 public void convertToAndFromJavaECKeys() throws Exception { 133 // Create an elliptic curve key pair using Java's KeyPairGenerator. 134 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); 135 keyGen.initialize(EcdsaParameters.CurveType.NIST_P384.toParameterSpec()); 136 KeyPair keyPair = keyGen.generateKeyPair(); 137 ECPrivateKey ecPrivateKey = (ECPrivateKey) keyPair.getPrivate(); 138 ECPublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic(); 139 140 // Before conversion, always check that the specs of ecPrivateKey and ecPublicKey are what 141 // we expect. 142 assertThat(EcdsaParameters.CurveType.fromParameterSpec(ecPrivateKey.getParams())) 143 .isEqualTo(EcdsaParameters.CurveType.NIST_P384); 144 assertThat(EcdsaParameters.CurveType.fromParameterSpec(ecPublicKey.getParams())) 145 .isEqualTo(EcdsaParameters.CurveType.NIST_P384); 146 147 // Create EcdsaParameters that match the curve type. 148 EcdsaParameters parameters = 149 EcdsaParameters.builder() 150 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 151 .setCurveType(EcdsaParameters.CurveType.NIST_P384) 152 .setHashType(EcdsaParameters.HashType.SHA384) 153 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 154 .build(); 155 156 // Create EcdsaPublicKey and EcdsaPrivateKey using ecPublicKey and ecPrivateKey. 157 EcdsaPublicKey publicKey = 158 EcdsaPublicKey.builder() 159 .setParameters(parameters) 160 .setPublicPoint(ecPublicKey.getW()) 161 .build(); 162 EcdsaPrivateKey privateKey = 163 EcdsaPrivateKey.builder() 164 .setPublicKey(publicKey) 165 .setPrivateValue( 166 SecretBigInteger.fromBigInteger(ecPrivateKey.getS(), InsecureSecretKeyAccess.get())) 167 .build(); 168 169 // Convert EcdsaPublicKey and back into a ECPublicKey. 170 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 171 ECPublicKey ecPublicKey2 = 172 (ECPublicKey) 173 keyFactory.generatePublic( 174 new ECPublicKeySpec( 175 publicKey.getPublicPoint(), 176 publicKey.getParameters().getCurveType().toParameterSpec())); 177 assertThat(ecPublicKey2.getW()).isEqualTo(ecPublicKey.getW()); 178 assertThat(EcdsaParameters.CurveType.fromParameterSpec(ecPublicKey2.getParams())) 179 .isEqualTo(EcdsaParameters.CurveType.NIST_P384); 180 181 // Convert EcdsaPrivateKey back into a ECPrivateKey. 182 ECPrivateKey ecPrivateKey2 = 183 (ECPrivateKey) 184 keyFactory.generatePrivate( 185 new ECPrivateKeySpec( 186 privateKey.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get()), 187 privateKey.getParameters().getCurveType().toParameterSpec())); 188 assertThat(ecPrivateKey2.getS()).isEqualTo(ecPrivateKey.getS()); 189 assertThat(EcdsaParameters.CurveType.fromParameterSpec(ecPrivateKey2.getParams())) 190 .isEqualTo(EcdsaParameters.CurveType.NIST_P384); 191 } 192 193 @Test emptyBuild_fails()194 public void emptyBuild_fails() throws Exception { 195 assertThrows(GeneralSecurityException.class, () -> EcdsaPublicKey.builder().build()); 196 } 197 198 @Test buildWithoutPublicKey_fails()199 public void buildWithoutPublicKey_fails() throws Exception { 200 assertThrows( 201 GeneralSecurityException.class, 202 () -> 203 EcdsaPrivateKey.builder() 204 .setPrivateValue( 205 SecretBigInteger.fromBigInteger( 206 P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 207 .build()); 208 } 209 210 @Test build_validatesPrivateValue()211 public void build_validatesPrivateValue() throws Exception { 212 EcdsaParameters parameters = 213 EcdsaParameters.builder() 214 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 215 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 216 .setHashType(EcdsaParameters.HashType.SHA256) 217 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 218 .build(); 219 EcdsaPublicKey publicKey = 220 EcdsaPublicKey.builder() 221 .setParameters(parameters) 222 .setPublicPoint(P256_PUBLIC_POINT) 223 .build(); 224 EcdsaPrivateKey valid = 225 EcdsaPrivateKey.builder() 226 .setPublicKey(publicKey) 227 .setPrivateValue( 228 SecretBigInteger.fromBigInteger(P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 229 .build(); 230 assertThat(valid.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get())) 231 .isEqualTo(P256_PRIVATE_VALUE); 232 BigInteger invalidPrivateValue = P256_PRIVATE_VALUE.add(BigInteger.ONE); 233 assertThrows( 234 GeneralSecurityException.class, 235 () -> 236 EcdsaPrivateKey.builder() 237 .setPublicKey(publicKey) 238 .setPrivateValue( 239 SecretBigInteger.fromBigInteger( 240 invalidPrivateValue, InsecureSecretKeyAccess.get())) 241 .build()); 242 assertThrows( 243 GeneralSecurityException.class, 244 () -> 245 EcdsaPrivateKey.builder() 246 .setPublicKey(publicKey) 247 .setPrivateValue( 248 SecretBigInteger.fromBigInteger(BigInteger.ZERO, InsecureSecretKeyAccess.get())) 249 .build()); 250 assertThrows( 251 GeneralSecurityException.class, 252 () -> 253 EcdsaPrivateKey.builder() 254 .setPublicKey(publicKey) 255 .setPrivateValue( 256 SecretBigInteger.fromBigInteger( 257 new BigInteger("-1"), InsecureSecretKeyAccess.get())) 258 .build()); 259 } 260 261 @Test build_rejectsPrivateValueThatIsLargerThanOrder()262 public void build_rejectsPrivateValueThatIsLargerThanOrder() throws Exception { 263 EcdsaParameters parameters = 264 EcdsaParameters.builder() 265 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 266 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 267 .setHashType(EcdsaParameters.HashType.SHA256) 268 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 269 .build(); 270 EcdsaPublicKey publicKey = 271 EcdsaPublicKey.builder() 272 .setParameters(parameters) 273 .setPublicPoint(P256_PUBLIC_POINT) 274 .build(); 275 EcdsaPrivateKey valid = 276 EcdsaPrivateKey.builder() 277 .setPublicKey(publicKey) 278 .setPrivateValue( 279 SecretBigInteger.fromBigInteger(P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 280 .build(); 281 assertThat(valid.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get())) 282 .isEqualTo(P256_PRIVATE_VALUE); 283 // Add the order of the generator to the private value. 284 BigInteger tooLargePrivateValue = 285 P256_PRIVATE_VALUE.add(EcdsaParameters.CurveType.NIST_P256.toParameterSpec().getOrder()); 286 assertThrows( 287 GeneralSecurityException.class, 288 () -> 289 EcdsaPrivateKey.builder() 290 .setPublicKey(publicKey) 291 .setPrivateValue( 292 SecretBigInteger.fromBigInteger( 293 tooLargePrivateValue, InsecureSecretKeyAccess.get())) 294 .build()); 295 } 296 297 @Test testEqualities()298 public void testEqualities() throws Exception { 299 EcdsaPublicKey noPrefixPublicKey = 300 EcdsaPublicKey.builder() 301 .setParameters( 302 EcdsaParameters.builder() 303 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 304 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 305 .setHashType(EcdsaParameters.HashType.SHA256) 306 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 307 .build()) 308 .setPublicPoint(P256_PUBLIC_POINT) 309 .build(); 310 311 // Uses generator as public key, and private key as ONE 312 EcdsaPublicKey noPrefixPublicKeyOne = 313 EcdsaPublicKey.builder() 314 .setParameters( 315 EcdsaParameters.builder() 316 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 317 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 318 .setHashType(EcdsaParameters.HashType.SHA256) 319 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 320 .build()) 321 .setPublicPoint(EcdsaParameters.CurveType.NIST_P256.toParameterSpec().getGenerator()) 322 .build(); 323 324 EcdsaPublicKey tinkPrefixPublicKey = 325 EcdsaPublicKey.builder() 326 .setParameters( 327 EcdsaParameters.builder() 328 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 329 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 330 .setHashType(EcdsaParameters.HashType.SHA256) 331 .setVariant(EcdsaParameters.Variant.TINK) 332 .build()) 333 .setPublicPoint(P256_PUBLIC_POINT) 334 .setIdRequirement(1907) 335 .build(); 336 337 EcdsaPublicKey noPrefixPublicKeyP521 = 338 EcdsaPublicKey.builder() 339 .setParameters( 340 EcdsaParameters.builder() 341 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 342 .setCurveType(EcdsaParameters.CurveType.NIST_P521) 343 .setHashType(EcdsaParameters.HashType.SHA512) 344 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 345 .build()) 346 .setPublicPoint(P521_PUBLIC_POINT) 347 .build(); 348 349 new KeyTester() 350 .addEqualityGroup( 351 "No prefix", 352 EcdsaPrivateKey.builder() 353 .setPublicKey(noPrefixPublicKey) 354 .setPrivateValue( 355 SecretBigInteger.fromBigInteger( 356 P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 357 .build(), 358 // the same key built twice must be equal 359 EcdsaPrivateKey.builder() 360 .setPublicKey( 361 EcdsaPublicKey.builder() 362 .setParameters( 363 EcdsaParameters.builder() 364 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 365 .setCurveType(EcdsaParameters.CurveType.NIST_P256) 366 .setHashType(EcdsaParameters.HashType.SHA256) 367 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 368 .build()) 369 .setPublicPoint(P256_PUBLIC_POINT) 370 .build()) 371 .setPrivateValue( 372 SecretBigInteger.fromBigInteger( 373 P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 374 .build()) 375 // This group checks that keys with different key bytes are not equal 376 .addEqualityGroup( 377 "No prefix, ONE", 378 EcdsaPrivateKey.builder() 379 .setPublicKey(noPrefixPublicKeyOne) 380 .setPrivateValue( 381 SecretBigInteger.fromBigInteger(BigInteger.ONE, InsecureSecretKeyAccess.get())) 382 .build()) 383 // This group checks that keys with different parameters are not equal 384 .addEqualityGroup( 385 "No prefix, P521", 386 EcdsaPrivateKey.builder() 387 .setPublicKey(noPrefixPublicKeyP521) 388 .setPrivateValue( 389 SecretBigInteger.fromBigInteger( 390 P521_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 391 .build()) 392 .addEqualityGroup( 393 "Tink with key id 1907", 394 EcdsaPrivateKey.builder() 395 .setPublicKey(tinkPrefixPublicKey) 396 .setPrivateValue( 397 SecretBigInteger.fromBigInteger( 398 P256_PRIVATE_VALUE, InsecureSecretKeyAccess.get())) 399 .build()) 400 .doTests(); 401 } 402 } 403