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 }