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 }