1 /** 2 * ECDSA 3 * 4 * Copyright: 5 * (C) 2007 Falko Strenzke, FlexSecure GmbH 6 * Manuel Hartl, FlexSecure GmbH 7 * (C) 2008-2010 Jack Lloyd 8 * (C) 2014-2015 Etienne Cimon 9 * 10 * License: 11 * Botan is released under the Simplified BSD License (see LICENSE.md) 12 */ 13 module botan.pubkey.algo.ecdsa; 14 15 import botan.constants; 16 17 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_ECDSA): 18 19 public import botan.pubkey.pubkey; 20 import botan.pubkey.algo.ecc_key; 21 import botan.math.numbertheory.reducer; 22 import botan.pubkey.pk_ops; 23 import botan.pubkey.algo.keypair; 24 import botan.math.ec_gfp.point_gfp; 25 import botan.rng.rng; 26 import botan.utils.types; 27 import memutils.helpers : Embed; 28 29 struct ECDSAOptions { 30 enum algoName = "ECDSA"; 31 enum msgParts = 2; 32 33 static bool checkKey(in ECPrivateKey privkey, RandomNumberGenerator rng, bool strong) 34 { 35 if (!privkey.publicPoint().onTheCurve()) 36 return false; 37 38 if (!strong) 39 return true; 40 41 return signatureConsistencyCheck(rng, privkey, "EMSA1(SHA-1)"); 42 } 43 } 44 45 /** 46 * This class represents ECDSA Public Keys. 47 */ 48 struct ECDSAPublicKey 49 { 50 public: 51 alias Options = ECDSAOptions; 52 __gshared immutable string algoName = Options.algoName; 53 /** 54 * Construct a public key from a given public point. 55 * 56 * Params: 57 * dom_par = the domain parameters associated with this key 58 * public_point = the public point defining this key 59 */ 60 this(in ECGroup dom_par, in PointGFp public_point) 61 { 62 m_owned = true; 63 m_pub = new ECPublicKey(Options(), dom_par, public_point); 64 } 65 66 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 67 { 68 m_owned = true; 69 m_pub = new ECPublicKey(Options(), alg_id, key_bits); 70 } 71 72 this(in PublicKey pkey) { 73 m_pub = cast(ECPublicKey) pkey; 74 } 75 76 this(in PrivateKey pkey) { 77 m_pub = cast(ECPublicKey) pkey; 78 } 79 80 mixin Embed!(m_pub, m_owned); 81 82 bool m_owned; 83 ECPublicKey m_pub; 84 } 85 86 /** 87 * This class represents ECDSA Private Keys 88 */ 89 struct ECDSAPrivateKey 90 { 91 public: 92 alias Options = ECDSAOptions; 93 __gshared immutable string algoName = Options.algoName; 94 95 /** 96 * Load a private key 97 * Params: 98 * alg_id = the X.509 algorithm identifier 99 * key_bits = PKCS #8 structure 100 */ 101 this(const ref AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 102 { 103 m_owned = true; 104 m_priv = new ECPrivateKey(Options(), alg_id, key_bits); 105 } 106 107 /** 108 * Generate a new private key 109 * Params: 110 * rng = a random number generator 111 * domain = parameters to used for this key 112 * x = the private key (if zero, generate a ney random key) 113 */ 114 this()(RandomNumberGenerator rng, auto const ref ECGroup domain, BigInt x = BigInt(0)) 115 { 116 m_owned = true; 117 m_priv = new ECPrivateKey(Options(), rng, domain, x); 118 } 119 120 this(in PrivateKey pkey) { 121 m_priv = cast(ECPrivateKey)pkey; 122 } 123 124 mixin Embed!(m_priv, m_owned); 125 126 bool m_owned; 127 ECPrivateKey m_priv; 128 } 129 130 /** 131 * ECDSA signature operation 132 */ 133 final class ECDSASignatureOperation : Signature 134 { 135 public: 136 this(in PrivateKey pkey) { 137 this(cast(ECPrivateKey) pkey); 138 } 139 140 this(in ECDSAPrivateKey pkey) { 141 this(pkey.m_priv); 142 } 143 144 this(in ECPrivateKey ecdsa) 145 { 146 assert(ecdsa.algoName == ECDSAPublicKey.algoName); 147 m_ecdsa = ecdsa; 148 m_base_point = &m_ecdsa.domain().getBasePoint(); 149 m_order = &m_ecdsa.domain().getOrder(); 150 m_x = &m_ecdsa.privateValue(); 151 m_mod_order = ModularReducer(*m_order); 152 } 153 154 override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng) 155 { 156 rng.addEntropy(msg, msg_len); 157 158 BigInt m = BigInt(msg, msg_len); 159 160 BigInt r = BigInt(0), s = BigInt(0); 161 162 while (r == 0 || s == 0) 163 { 164 // This contortion is necessary for the tests 165 BigInt k; 166 k.randomize(rng, m_order.bits()); 167 168 while (k >= *m_order) 169 k.randomize(rng, m_order.bits() - 1); 170 PointGFp k_times_P = *m_base_point * &k; 171 assert(k_times_P.onTheCurve()); 172 r = m_mod_order.reduce(k_times_P.getAffineX()); 173 auto s_0 = inverseMod(&k, m_order); 174 auto s_1 = mulAdd(m_x, &r, &m); 175 s = m_mod_order.multiply(&s_0, &s_1); 176 177 } 178 179 SecureVector!ubyte output = SecureVector!ubyte(2*m_order.bytes()); 180 r.binaryEncode(&output[output.length / 2 - r.bytes()]); 181 s.binaryEncode(&output[output.length - s.bytes()]); 182 return output.move(); 183 } 184 185 override size_t messageParts() const { return 2; } 186 override size_t messagePartSize() const { return m_order.bytes(); } 187 override size_t maxInputBits() const { return m_order.bits(); } 188 189 private: 190 const ECPrivateKey m_ecdsa; 191 const PointGFp* m_base_point; 192 const BigInt* m_order; 193 const BigInt* m_x; 194 ModularReducer m_mod_order; 195 } 196 197 /** 198 * ECDSA verification operation 199 */ 200 final class ECDSAVerificationOperation : Verification 201 { 202 public: 203 this(in PublicKey pkey) { 204 this(cast(ECPublicKey) pkey); 205 } 206 207 this(in ECDSAPublicKey pkey) { 208 this(pkey.m_pub); 209 } 210 211 this(in ECPublicKey ecdsa) 212 { 213 assert(ecdsa.algoName == ECDSAPublicKey.algoName); 214 m_pubkey = ecdsa; 215 m_base_point = &m_pubkey.domain().getBasePoint(); 216 m_public_point = &m_pubkey.publicPoint(); 217 m_order = &m_pubkey.domain().getOrder(); 218 m_mod_order = *m_order; 219 } 220 221 override size_t messageParts() const { return 2; } 222 override size_t messagePartSize() const { return m_order.bytes(); } 223 override size_t maxInputBits() const { return m_order.bits(); } 224 225 override bool withRecovery() const { return false; } 226 227 override SecureVector!ubyte verifyMr(const(ubyte)*, size_t) { throw new InvalidState("Message recovery not supported"); } 228 override bool verify(const(ubyte)* msg, size_t msg_len, 229 const(ubyte)* sig, size_t sig_len) 230 { 231 if (sig_len != m_order.bytes()*2) { 232 return false; 233 } 234 235 BigInt e = BigInt(msg, msg_len); 236 237 BigInt r = BigInt(sig, sig_len / 2); 238 BigInt s = BigInt(sig + sig_len / 2, sig_len / 2); 239 240 if (r <= 0 || r >= *m_order || s <= 0 || s >= *m_order) { 241 //logError("arg error"); 242 return false; 243 } 244 245 BigInt w = inverseMod(&s, m_order); 246 BigInt u1 = m_mod_order.reduce(e * w); 247 BigInt u2 = m_mod_order.reduce(r * w); 248 PointGFp R = PointGFp.multiExponentiate(*m_base_point, &u1, *m_public_point, &u2); 249 if (R.isZero()) 250 return false; 251 BigInt v = m_mod_order.reduce(R.getAffineX()); 252 return (v == r); 253 } 254 255 private: 256 const ECPublicKey m_pubkey; 257 const PointGFp* m_base_point; 258 const PointGFp* m_public_point; 259 const BigInt* m_order; 260 ModularReducer m_mod_order; 261 } 262 263 static if (BOTAN_TEST): 264 265 /****************************************************** 266 * ECDSA tests * 267 * * 268 * (C) 2007 Falko Strenzke * 269 * Manuel Hartl * 270 * 2008 Jack Lloyd * 271 ******************************************************/ 272 273 import botan.test; 274 import botan.pubkey.test; 275 import botan.rng.auto_rng; 276 import botan.pubkey.pubkey; 277 static if (BOTAN_HAS_RSA) import botan.pubkey.algo.rsa; 278 import botan.cert.x509.x509cert; 279 import botan.pubkey.pkcs8; 280 import botan.asn1.oids; 281 import botan.codec.hex; 282 import core.atomic; 283 import memutils.hashmap; 284 private shared size_t total_tests; 285 286 string toHex(const Vector!ubyte bin) 287 { 288 return hexEncode(bin.ptr, bin.length); 289 } 290 291 /** 292 293 * Tests whether the the signing routine will work correctly input case 294 * the integer e that is constructed from the message (thus the hash 295 * value) is larger than n, the order of the base point. Tests the 296 * signing function of the pk signer object */ 297 298 size_t testHashLargerThanN(RandomNumberGenerator rng) 299 { 300 atomicOp!"+="(total_tests, 1); 301 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); // secp160r1 302 // n = 0x0100000000000000000001f4c8f927aed3ca752257 (21 bytes) 303 // . shouldn't work with SHA224 which outputs 28 bytes 304 305 size_t fails = 0; 306 auto priv_key = ECDSAPrivateKey(rng, dom_pars); 307 308 Vector!ubyte message = Vector!ubyte(20); 309 for(size_t i = 0; i != message.length; ++i) 310 message[i] = i; 311 312 PKSigner pk_signer_160 = PKSigner(priv_key, "EMSA1_BSI(SHA-1)"); 313 PKVerifier PKVerifier_160 = PKVerifier(priv_key, "EMSA1_BSI(SHA-1)"); 314 315 PKSigner pk_signer_224 = PKSigner(priv_key, "EMSA1_BSI(SHA-224)"); 316 317 // Verify we can sign and verify with SHA-160 318 Vector!ubyte signature_160 = pk_signer_160.signMessage(message, rng); 319 320 mixin( CHECK(` PKVerifier_160.verifyMessage(message, signature_160) `) ); 321 322 bool signature_failed = false; 323 try 324 { 325 Vector!ubyte signature_224 = pk_signer_224.signMessage(message, rng); 326 } 327 catch(EncodingError) 328 { 329 signature_failed = true; 330 } 331 332 mixin( CHECK(` signature_failed `) ); 333 334 // now check that verification alone fails 335 336 // sign it with the normal EMSA1 337 PKSigner pk_signer = PKSigner(priv_key, "EMSA1(SHA-224)"); 338 Vector!ubyte signature = pk_signer.signMessage(message, rng); 339 340 PKVerifier PKVerifier = PKVerifier(priv_key, "EMSA1_BSI(SHA-224)"); 341 342 // verify against EMSA1_BSI 343 if (PKVerifier.verifyMessage(message, signature)) 344 { 345 logTrace("Corrupt ECDSA signature verified, should not have"); 346 ++fails; 347 } 348 return fails; 349 } 350 351 static if (BOTAN_HAS_X509_CERTIFICATES) 352 size_t testDecodeEcdsaX509() 353 { 354 X509Certificate cert = X509Certificate("test_data/ecc/CSCA.CSCA.csca-germany.1.crt"); 355 //logDebug(cert.toString()); 356 size_t fails = 0; 357 358 mixin( CHECK_MESSAGE( `OIDS.lookup(cert.signatureAlgorithm().oid) == "ECDSA/EMSA1(SHA-224)"`, "error reading signature algorithm from x509 ecdsa certificate" ) ); 359 360 mixin( CHECK_MESSAGE( `toHex(cert.serialNumber()) == "01"`, "error reading serial from x509 ecdsa certificate" ) ); 361 mixin( CHECK_MESSAGE( `toHex(cert.authorityKeyId()) == "0096452DE588F966C4CCDF161DD1F3F5341B71E7"`, "error reading authority key id from x509 ecdsa certificate" ) ); 362 mixin( CHECK_MESSAGE( `toHex(cert.subjectKeyId()) == "0096452DE588F966C4CCDF161DD1F3F5341B71E7"`, "error reading Subject key id from x509 ecdsa certificate" ) ); 363 364 Unique!X509PublicKey pubkey = cert.subjectPublicKey(); 365 bool ver_ec = cert.checkSignature(*pubkey); 366 mixin( CHECK_MESSAGE( `ver_ec`, "could not positively verify correct selfsigned x509-ecdsa certificate" ) ); 367 assert(!fails); 368 return fails; 369 } 370 371 static if (BOTAN_HAS_X509_CERTIFICATES) 372 size_t testDecodeVerLinkSHA256() 373 { 374 X509Certificate root_cert = X509Certificate("test_data/ecc/root2_SHA256.cer"); 375 X509Certificate link_cert = X509Certificate("test_data/ecc/link_SHA256.cer"); 376 377 size_t fails = 0; 378 Unique!X509PublicKey pubkey = root_cert.subjectPublicKey(); 379 bool ver_ec = link_cert.checkSignature(*pubkey); 380 mixin( CHECK_MESSAGE( `ver_ec`, "could not positively verify correct SHA256 link x509-ecdsa certificate" ) ); 381 return fails; 382 } 383 384 static if (BOTAN_HAS_X509_CERTIFICATES) 385 size_t testDecodeVerLinkSHA1() 386 { 387 atomicOp!"+="(total_tests, 1); 388 X509Certificate root_cert = X509Certificate("test_data/ecc/root_SHA1.163.crt"); 389 X509Certificate link_cert = X509Certificate("test_data/ecc/link_SHA1.166.crt"); 390 391 size_t fails = 0; 392 Unique!X509PublicKey pubkey = root_cert.subjectPublicKey(); 393 bool ver_ec = link_cert.checkSignature(*pubkey); 394 mixin( CHECK_MESSAGE( `ver_ec`, "could not positively verify correct SHA1 link x509-ecdsa certificate" ) ); 395 return fails; 396 } 397 398 size_t testSignThenVer(RandomNumberGenerator rng) 399 { 400 atomicOp!"+="(total_tests, 2); 401 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); 402 auto ecdsa = ECDSAPrivateKey(rng, dom_pars); 403 404 size_t fails = 0; 405 PKSigner signer = PKSigner(ecdsa, "EMSA1(SHA-1)"); 406 407 auto msg = hexDecode("12345678901234567890abcdef12"); 408 Vector!ubyte sig = signer.signMessage(msg, rng); 409 410 PKVerifier verifier = PKVerifier(ecdsa, "EMSA1(SHA-1)"); 411 412 bool ok = verifier.verifyMessage(msg, sig); 413 414 if (!ok) 415 { 416 logTrace("ERROR: Could not verify ECDSA signature"); 417 fails++; 418 } 419 420 sig[0]++; 421 ok = verifier.verifyMessage(msg, sig); 422 423 if (ok) 424 { 425 logTrace("ERROR: Bogus ECDSA signature verified anyway"); 426 fails++; 427 } 428 429 return fails; 430 } 431 432 size_t testEcSign(RandomNumberGenerator rng) 433 { 434 atomicOp!"+="(total_tests, 4); 435 size_t fails = 0; 436 437 try 438 { 439 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); 440 auto priv_key = ECDSAPrivateKey(rng, dom_pars); 441 string pem_encoded_key = pkcs8.PEM_encode(priv_key); 442 443 PKSigner signer = PKSigner(priv_key, "EMSA1(SHA-224)"); 444 PKVerifier verifier = PKVerifier(priv_key, "EMSA1(SHA-224)"); 445 446 for(size_t i = 0; i != 256; ++i) 447 signer.update(cast(ubyte)(i)); 448 Vector!ubyte sig = signer.signature(rng); 449 450 for(uint i = 0; i != 256; ++i) 451 verifier.update(cast(ubyte)(i)); 452 if (!verifier.checkSignature(sig)) 453 { 454 logTrace("ECDSA self-test failed!"); 455 ++fails; 456 } 457 458 // now check valid signature, different input 459 for(uint i = 1; i != 256; ++i) //starting from 1 460 verifier.update(cast(ubyte)(i)); 461 462 if (verifier.checkSignature(sig)) 463 { 464 logTrace("ECDSA with bad input passed validation"); 465 ++fails; 466 } 467 468 // now check with original in, modified signature 469 sig[sig.length/2]++; 470 for(uint i = 0; i != 256; ++i) 471 verifier.update(cast(ubyte)(i)); 472 473 if (verifier.checkSignature(sig)) 474 { 475 logTrace("ECDSA with bad signature passed validation"); 476 ++fails; 477 } 478 } 479 catch (Exception e) 480 { 481 logTrace("Exception in test_ec_sign - " ~ e.msg); 482 ++fails; 483 } 484 return fails; 485 } 486 487 static if (BOTAN_HAS_RSA) 488 size_t testCreatePkcs8(RandomNumberGenerator rng) 489 { 490 atomicOp!"+="(total_tests, 1); 491 size_t fails = 0; 492 493 try 494 { 495 auto rsa_key = RSAPrivateKey(rng, 1024); 496 497 //RSAPrivateKey rsa_key2(1024); 498 //cout " ~\nequal: " ~ (rsa_key == rsa_key2)); 499 //DSAPrivateKey key(DLGroup("dsa/jce/1024")); 500 501 File rsa_priv_key = File("test_data/ecc/rsa_private.pkcs8.pem", "wb+"); 502 rsa_priv_key.write(pkcs8.PEM_encode(rsa_key)); 503 504 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); 505 auto key = ECDSAPrivateKey(rng, dom_pars); 506 507 // later used by other tests :( 508 File priv_key = File("test_data/ecc/wo_dompar_private.pkcs8.pem", "wb+"); 509 priv_key.write( pkcs8.PEM_encode(key) ); 510 } 511 catch (Exception e) 512 { 513 logTrace("Exception: " ~ e.msg); 514 ++fails; 515 } 516 517 return fails; 518 } 519 520 static if (BOTAN_HAS_RSA) 521 size_t testCreateAndVerify(RandomNumberGenerator rng) 522 { 523 atomicOp!"+="(total_tests, 1); 524 size_t fails = 0; 525 526 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); 527 auto key = ECDSAPrivateKey(rng, dom_pars); 528 File priv_key = File("test_data/ecc/dompar_private.pkcs8.pem", "w+"); 529 priv_key.write( pkcs8.PEM_encode(key) ); 530 531 Unique!PKCS8PrivateKey loaded_key = pkcs8.loadKey("test_data/ecc/wo_dompar_private.pkcs8.pem", rng); 532 auto loaded_ec_key = ECDSAPrivateKey(*loaded_key); 533 mixin( CHECK_MESSAGE( `loaded_ec_key`, "the loaded key could not be converted into an ECDSAPrivateKey" ) ); 534 Unique!PKCS8PrivateKey loaded_key_1 = pkcs8.loadKey("test_data/ecc/rsa_private.pkcs8.pem", rng); 535 auto loaded_rsa_key = ECDSAPrivateKey(*loaded_key_1); 536 mixin( CHECK_MESSAGE( `!loaded_rsa_key`, "the loaded key is ECDSAPrivateKey -> shouldn't be, is a RSA-Key" ) ); 537 538 //calc a curve which is not in the registry 539 // string p_secp = "2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"; 540 string a_secp = "0a377dede6b523333d36c78e9b0eaa3bf48ce93041f6d4fc34014d08f6833807498deedd4290101c5866e8dfb589485d13357b9e78c2d7fbe9fe"; 541 string b_secp = "0a9acf8c8ba617777e248509bcb4717d4db346202bf9e352cd5633731dd92a51b72a4dc3b3d17c823fcc8fbda4da08f25dea89046087342595a7"; 542 string G_secp_comp = "04081523d03d4f12cd02879dea4bf6a4f3a7df26ed888f10c5b2235a1274c386a2f218300dee6ed217841164533bcdc903f07a096f9fbf4ee95bac098a111f296f5830fe5c35b3e344d5df3a2256985f64fbe6d0edcc4c61d18bef681dd399df3d0194c5a4315e012e0245ecea56365baa9e8be1f7"; 543 string order_g = "0e1a16196e6000000000bc7f1618d867b15bb86474418f"; 544 545 // ::Vector!ubyte sv_p_secp = hexDecode( p_secp ); 546 auto sv_a_secp = hexDecode( a_secp ); 547 auto sv_b_secp = hexDecode( b_secp ); 548 auto sv_G_secp_comp = hexDecode( G_secp_comp ); 549 auto sv_order_g = hexDecode( order_g ); 550 551 // BigInt bi_p_secp = BigInt.decode( sv_p_secp.ptr, sv_p_secp.length ); 552 BigInt bi_p_secp = BigInt("2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"); 553 BigInt bi_a_secp = BigInt.decode( sv_a_secp.ptr, sv_a_secp.length ); 554 BigInt bi_b_secp = BigInt.decode( sv_b_secp.ptr, sv_b_secp.length ); 555 BigInt bi_order_g = BigInt.decode( sv_order_g.ptr, sv_order_g.length ); 556 CurveGFp curve = CurveGFp(&bi_p_secp, &bi_a_secp, &bi_b_secp); 557 PointGFp p_G = OS2ECP( sv_G_secp_comp, curve ); 558 auto bi = BigInt(1); 559 ECGroup dom_params = ECGroup(curve, p_G, bi_order_g, bi); 560 if (!p_G.onTheCurve()) 561 throw new InternalError("Point not on the curve"); 562 563 auto key_odd_oid = ECDSAPrivateKey(rng, dom_params); 564 string key_odd_oid_str = pkcs8.PEM_encode(key_odd_oid); 565 auto key_data_src = DataSourceMemory(key_odd_oid_str); 566 Unique!PKCS8PrivateKey loaded_key2 = pkcs8.loadKey(cast(DataSource)key_data_src, rng); 567 568 if (!*ECDSAPrivateKey(*loaded_key)) 569 { 570 logError("Failed to reload an ECDSA key with unusual parameter set"); 571 ++fails; 572 } 573 574 return fails; 575 } 576 577 size_t testCurveRegistry(RandomNumberGenerator rng) 578 { 579 Vector!string oids; 580 oids.pushBack("1.3.132.0.8"); 581 oids.pushBack("1.2.840.10045.3.1.1"); 582 oids.pushBack("1.2.840.10045.3.1.2"); 583 oids.pushBack("1.2.840.10045.3.1.3"); 584 oids.pushBack("1.2.840.10045.3.1.4"); 585 oids.pushBack("1.2.840.10045.3.1.5"); 586 oids.pushBack("1.2.840.10045.3.1.6"); 587 oids.pushBack("1.2.840.10045.3.1.7"); 588 oids.pushBack("1.3.132.0.6"); 589 oids.pushBack("1.3.132.0.7"); 590 oids.pushBack("1.3.132.0.28"); 591 oids.pushBack("1.3.132.0.29"); 592 oids.pushBack("1.3.132.0.9"); 593 oids.pushBack("1.3.132.0.30"); 594 oids.pushBack("1.3.132.0.31"); 595 oids.pushBack("1.3.132.0.32"); 596 oids.pushBack("1.3.132.0.33"); 597 oids.pushBack("1.3.132.0.10"); 598 oids.pushBack("1.3.132.0.34"); 599 oids.pushBack("1.3.132.0.35"); 600 //oids.pushBack("1.3.6.1.4.1.8301.3.1.2.9.0.38"); 601 oids.pushBack("1.3.36.3.3.2.8.1.1.1"); 602 oids.pushBack("1.3.36.3.3.2.8.1.1.3"); 603 oids.pushBack("1.3.36.3.3.2.8.1.1.5"); 604 oids.pushBack("1.3.36.3.3.2.8.1.1.7"); 605 oids.pushBack("1.3.36.3.3.2.8.1.1.9"); 606 oids.pushBack("1.3.36.3.3.2.8.1.1.11"); 607 oids.pushBack("1.3.36.3.3.2.8.1.1.13"); 608 609 size_t fails = 0; 610 611 uint i; 612 foreach (oid_str; oids[]) 613 { 614 atomicOp!"+="(total_tests, 1); 615 try 616 { 617 OID oid = OID(oid_str); 618 ECGroup dom_pars = ECGroup(oid); 619 auto ecdsa = ECDSAPrivateKey(rng, dom_pars); 620 621 PKSigner signer = PKSigner(ecdsa, "EMSA1(SHA-1)"); 622 PKVerifier verifier = PKVerifier(ecdsa, "EMSA1(SHA-1)"); 623 624 auto msg = hexDecode("12345678901234567890abcdef12"); 625 Vector!ubyte sig = signer.signMessage(msg, rng); 626 627 if (!verifier.verifyMessage(msg, sig)) 628 { 629 logError("Failed testing ECDSA sig for curve " ~ oid_str); 630 ++fails; 631 } 632 } 633 catch(InvalidArgument e) 634 { 635 logError("Error testing curve " ~ oid_str ~ " - " ~ e.msg); 636 ++fails; 637 } 638 } 639 return fails; 640 } 641 642 size_t testReadPkcs8(RandomNumberGenerator rng) 643 { 644 atomicOp!"+="(total_tests, 2); 645 auto msg = hexDecode("12345678901234567890abcdef12"); 646 size_t fails = 0; 647 648 try 649 { 650 Unique!PKCS8PrivateKey loaded_key = pkcs8.loadKey("test_data/ecc/wo_dompar_private.pkcs8.pem", rng); 651 auto ecdsa = ECDSAPrivateKey(*loaded_key); 652 mixin( CHECK_MESSAGE( `ecdsa`, "the loaded key could not be converted into an ECDSAPrivateKey" ) ); 653 654 PKSigner signer = PKSigner(ecdsa, "EMSA1(SHA-1)"); 655 656 Vector!ubyte sig = signer.signMessage(msg, rng); 657 658 PKVerifier verifier = PKVerifier(ecdsa, "EMSA1(SHA-1)"); 659 660 mixin( CHECK_MESSAGE(`verifier.verifyMessage(msg, sig)`, "generated sig could not be verified positively")); 661 } 662 catch (Exception e) 663 { 664 ++fails; 665 logError("Exception in test_read_pkcs8 - " ~ e.msg); 666 } 667 668 try 669 { 670 Unique!PKCS8PrivateKey loaded_key_nodp = pkcs8.loadKey("test_data/ecc/nodompar_private.pkcs8.pem", rng); 671 // anew in each test with unregistered domain-parameters 672 auto ecdsa_nodp = ECDSAPrivateKey(*loaded_key_nodp); 673 mixin( CHECK_MESSAGE( `ecdsa_nodp`, "the loaded key could not be converted into an ECDSAPrivateKey" ) ); 674 675 PKSigner signer = PKSigner(ecdsa_nodp, "EMSA1(SHA-1)"); 676 PKVerifier verifier = PKVerifier(ecdsa_nodp, "EMSA1(SHA-1)"); 677 678 Vector!ubyte signature_nodp = signer.signMessage(msg, rng); 679 680 mixin( CHECK_MESSAGE(`verifier.verifyMessage(msg, signature_nodp)`, 681 "generated signature could not be verified positively (no_dom)")); 682 683 try 684 { 685 Unique!PKCS8PrivateKey loaded_key_withdp = pkcs8.loadKey("test_data/ecc/withdompar_private.pkcs8.pem", rng); 686 687 logError("Unexpected success: loaded key with unknown OID"); 688 ++fails; 689 } 690 catch (Exception) { /* OK */ } 691 } 692 catch (Exception e) 693 { 694 logError("Exception in test_read_pkcs8 - " ~ e.msg); 695 ++fails; 696 } 697 698 return fails; 699 } 700 701 size_t testEccKeyWithRfc5915Extensions(RandomNumberGenerator rng) 702 { 703 atomicOp!"+="(total_tests, 1); 704 size_t fails = 0; 705 706 try 707 { 708 Unique!PKCS8PrivateKey pkcs8 = pkcs8.loadKey("test_data/ecc/ecc_private_with_rfc5915_ext.pem", rng); 709 710 if (!*ECDSAPrivateKey(*pkcs8)) 711 { 712 logError("Loaded RFC 5915 key, but got something other than an ECDSA key"); 713 ++fails; 714 } 715 } 716 catch(Exception e) 717 { 718 logError("Exception in " ~ __PRETTY_FUNCTION__ ~ " - " ~ e.msg); 719 ++fails; 720 } 721 722 return fails; 723 } 724 725 size_t testPkKeygen(RandomNumberGenerator rng) { 726 size_t fails = 0; 727 728 string[] ecdsa_list = ["secp112r1", "secp128r1", "secp160r1", "secp192r1", 729 "secp224r1", "secp256r1", "secp384r1", "secp521r1"]; 730 731 foreach (ecdsa; ecdsa_list) { 732 atomicOp!"+="(total_tests, 1); 733 auto key = ECDSAPrivateKey(rng, ECGroup(OIDS.lookup(ecdsa))); 734 key.checkKey(rng, true); 735 fails += validateSaveAndLoad(key, rng); 736 } 737 738 return fails; 739 } 740 741 size_t ecdsaSigKat(string group_id, 742 string x, 743 string hash, 744 string msg, 745 string nonce, 746 string signature) 747 { 748 atomicOp!"+="(total_tests, 1); 749 Unique!AutoSeededRNG rng = new AutoSeededRNG; 750 751 ECGroup group = ECGroup(OIDS.lookup(group_id)); 752 auto bx = BigInt(x); 753 auto ecdsa = ECDSAPrivateKey(*rng, group, bx.move()); 754 755 const string padding = "EMSA1(" ~ hash ~ ")"; 756 PKVerifier verify = PKVerifier(*ecdsa, padding); 757 PKSigner sign = PKSigner(*ecdsa, padding); 758 return validateSignature(verify, sign, "ECDSA/" ~ group_id ~ "/" ~ hash, msg, *rng, nonce, signature); 759 } 760 761 size_t eccPointMul(in string group_id, 762 in string m_s, 763 in string X_s, 764 in string Y_s) 765 { 766 atomicOp!"+="(total_tests, 2); 767 ECGroup group = OIDS.lookup(group_id); 768 769 const BigInt m = BigInt(m_s); 770 const BigInt X = BigInt(X_s); 771 const BigInt Y = BigInt(Y_s); 772 773 PointGFp p = group.getBasePoint() * &m; 774 775 size_t fails = 0; 776 777 if (p.getAffineX() != X) 778 { 779 logError( p.getAffineY().toString() ~ " != " ~ X.toString() ~ "\n"); 780 ++fails; 781 } 782 783 if (p.getAffineY() != Y) 784 { 785 logError( p.getAffineY().toString() ~ " != " ~ Y.toString() ~ "\n"); 786 ++fails; 787 } 788 789 return fails; 790 } 791 792 static if (BOTAN_HAS_TESTS && !SKIP_ECDSA_TEST) unittest 793 { 794 logDebug("Testing ecdsa.d ..."); 795 size_t fails = 0; 796 797 Unique!AutoSeededRNG rng = new AutoSeededRNG; 798 799 static if (BOTAN_HAS_X509_CERTIFICATES) { 800 fails += testDecodeEcdsaX509(); 801 fails += testDecodeVerLinkSHA256(); 802 fails += testDecodeVerLinkSHA1(); 803 } 804 805 fails += testCurveRegistry(*rng); 806 fails += testHashLargerThanN(*rng); 807 fails += testSignThenVer(*rng); 808 fails += testEcSign(*rng); 809 810 static if (BOTAN_HAS_RSA) { 811 fails += testCreatePkcs8(*rng); 812 fails += testCreateAndVerify(*rng); 813 } 814 815 fails += testReadPkcs8(*rng); 816 fails += testEccKeyWithRfc5915Extensions(*rng); 817 818 fails += testPkKeygen(*rng); 819 820 File ecdsa_sig = File("test_data/pubkey/ecdsa.vec", "r"); 821 822 fails += runTestsBb(ecdsa_sig, "ECDSA Signature", "Signature", true, 823 (ref HashMap!(string, string) m) { 824 return ecdsaSigKat(m.get("Group"), m.get("X"), m.get("Hash"), m.get("Msg"), m.get("Nonce"), m.get("Signature")); 825 }); 826 827 File ecc_mul = File("test_data/pubkey/ecc.vec", "r"); 828 829 fails += runTestsBb(ecc_mul, "ECC Point Mult", "Y", false, 830 (ref HashMap!(string, string) m) 831 { 832 return eccPointMul(m["Group"], m["m"], m["X"], m["Y"]); 833 }); 834 835 testReport("ECDSA", total_tests, fails); 836 837 }