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