1 /**
2 * DSA
3 * 
4 * Copyright:
5 * (C) 1999-2010 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.pubkey.algo.dsa;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_DSA):
15 
16 public import botan.pubkey.algo.dl_algo;
17 public import botan.pubkey.pubkey;
18 import botan.pubkey.pk_ops;
19 import botan.math.numbertheory.reducer;
20 import botan.math.numbertheory.pow_mod;
21 import botan.math.numbertheory.numthry;
22 import botan.pubkey.algo.keypair;
23 import std.concurrency;
24 import core.thread;
25 import memutils.helpers : Embed;
26 import std.algorithm : max;
27 
28 struct DSAOptions {
29     enum algoName = "DSA";
30     enum format = DLGroup.ANSI_X9_57;
31     enum msgParts = 2;
32 
33     /*
34     * Check Private DSA Parameters
35     */
36     static bool checkKey(in DLSchemePrivateKey privkey, RandomNumberGenerator rng, bool strong)
37     {
38         if (!privkey.checkKeyImpl(rng, strong) || privkey.m_x >= privkey.groupQ())
39             return false;
40 
41         if (!strong)
42             return true;
43 
44         return signatureConsistencyCheck(rng, privkey, "EMSA1(SHA-1)");
45     }
46 }
47 
48 /**
49 * DSA Public Key
50 */
51 struct DSAPublicKey
52 {
53 public:
54     alias Options = DSAOptions;
55     __gshared immutable string algoName = Options.algoName;
56 
57 
58     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
59     {
60 		m_owned = true;
61         m_pub = new DLSchemePublicKey(Options(), alg_id, key_bits);
62     }
63 
64     /*
65     * DSAPublicKey Constructor
66     */
67     this(DLGroup grp, BigInt y1)
68     {
69 		m_owned = true;
70         m_pub = new DLSchemePublicKey(Options(), grp.move, y1.move);
71     }
72 
73     this(PublicKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
74     this(PrivateKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
75 
76     mixin Embed!(m_pub, m_owned);
77 
78 	bool m_owned;
79     DLSchemePublicKey m_pub;
80 }
81 
82 /**
83 * DSA Private Key
84 */
85 struct DSAPrivateKey
86 {
87 public:
88     alias Options = DSAOptions;
89     __gshared immutable string algoName = Options.algoName;
90     
91     /*
92     * Create a DSA private key
93     */
94     this(RandomNumberGenerator rng, DLGroup dl_group, BigInt x_arg = 0)
95     {
96         bool x_arg_0;
97         if (x_arg == 0) {
98             x_arg_0 = true;
99             auto bi = BigInt(2);
100             x_arg = BigInt.randomInteger(rng, bi, dl_group.getQ() - 1);
101         }
102         BigInt y1 = powerMod(&dl_group.getG(), &x_arg, &dl_group.getP());
103         
104 		m_owned = true;
105         m_priv = new DLSchemePrivateKey(Options(), dl_group.move, y1.move, x_arg.move);
106 
107         if (x_arg_0)
108             m_priv.genCheck(rng);
109         else
110             m_priv.loadCheck(rng);
111     }
112 
113     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits, RandomNumberGenerator rng)
114     {
115 		m_owned = true;
116         m_priv = new DLSchemePrivateKey(Options(), alg_id, key_bits);
117         m_priv.loadCheck(rng);
118     }
119 
120     this(PrivateKey pkey) { m_priv = cast(DLSchemePrivateKey) pkey; }
121 
122     mixin Embed!(m_priv, m_owned);
123 
124 	bool m_owned;
125     DLSchemePrivateKey m_priv;
126 }
127 
128 /**
129 * Object that can create a DSA signature
130 */
131 final class DSASignatureOperation : Signature
132 {
133 public:
134     this(in PrivateKey pkey) {
135         this(cast(DLSchemePrivateKey) pkey);
136     }
137 
138     this(in DSAPrivateKey pkey) {
139         this(pkey.m_priv);
140     }
141 
142     this(in DLSchemePrivateKey dsa)
143     { 
144         assert(dsa.algoName == DSAPublicKey.algoName);
145         m_q = &dsa.groupQ();
146         m_x = &dsa.getX();
147         m_g = &dsa.groupG();
148         m_p = &dsa.groupP();
149         m_mod_q = ModularReducer(dsa.groupQ());
150     }
151 
152     override size_t messageParts() const { return 2; }
153     override size_t messagePartSize() const { return m_q.bytes(); }
154     override size_t maxInputBits() const { return m_q.bits(); }
155 
156     override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng)
157     {    
158 		//import core.memory : GC; GC.disable(); scope(exit) GC.enable();
159         rng.addEntropy(msg, msg_len);
160         
161         BigInt i = BigInt(msg, msg_len);
162         BigInt r = BigInt(0), s = BigInt(0);
163 
164         while (r == 0 || s == 0)
165         {
166             BigInt k;
167             do
168                 k.randomize(rng, m_q.bits());
169             while (k >= *m_q);
170 
171 			import core.sync.mutex, core.sync.condition;
172 			Mutex mutex = ThreadMem.alloc!Mutex();
173 			scope(exit) {
174 				ThreadMem.free(mutex);
175 			}
176 
177             BigInt res;
178 			//logDebug("Expected: ", max(m_g.bytes() + m_g.bytes() % 128, m_x.bytes() + m_x.bytes % 128));
179 			res.reserve(4096);
180 
181 			struct Handler {
182 				shared(Mutex) mtx;
183 				shared(ModularReducer*) mod_q;
184 				shared(const BigInt*) g;
185 				shared(const BigInt*) p;
186 				shared(BigInt*) k2;
187 				shared(BigInt*) res2;
188 				void run() { 	
189 					try {
190 						import botan.libstate.libstate : modexpInit;
191 						modexpInit(); // enable quick path for powermod
192 						BigInt* ret = cast(BigInt*) res2;
193 						{ import memutils.utils;
194 							auto powermod_g_p = scoped!FixedBasePowerModImpl(cast(const(BigInt)*)g, cast(const(BigInt)*)p);
195 							BigInt _res = (cast(ModularReducer)*mod_q).reduce(powermod_g_p(cast(BigInt*)k2));
196 							//logDebug("Got: ", _res.bytes());
197 							synchronized(cast()mtx) ret.load(&_res);
198 						}
199 					}
200 					catch (Exception e) { logDebug("Error: ", e.toString()); }
201 				}
202 			}
203 			
204 			auto handler = Handler(cast(shared) mutex, cast(shared)&m_mod_q, cast(shared)m_g, cast(shared)m_p, cast(shared)&k, cast(shared)&res);
205 			Unique!Thread thr = new Thread(&handler.run);
206 			thr.start();
207             s = inverseMod(&k, m_q);
208 			thr.join();
209 			synchronized(mutex) r = res.dup;
210 			BigInt s_arg = mulAdd(m_x, &r, &i);
211             s = m_mod_q.multiply(&s, &s_arg);
212         }
213         
214         SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes());
215         r.binaryEncode(&output[output.length / 2 - r.bytes()]);
216         s.binaryEncode(&output[output.length - s.bytes()]);
217         return output.move;
218     }
219 private:
220     const BigInt* m_q;
221     const BigInt* m_x;
222     const BigInt* m_g;
223     const BigInt* m_p;
224     ModularReducer m_mod_q;
225 }
226 
227 /**
228 * Object that can verify a DSA signature
229 */
230 final class DSAVerificationOperation : Verification
231 {
232 public:
233     this(in PublicKey pkey) {
234         this(cast(DLSchemePublicKey) pkey);
235     }
236 
237     this(in DSAPublicKey pkey) {
238         this(pkey.m_pub);
239     }
240 
241     this(in DLSchemePublicKey dsa) 
242     {
243         assert(dsa.algoName == DSAPublicKey.algoName);
244         m_dsa = dsa;
245         m_q = &m_dsa.groupQ();
246         m_y = &m_dsa.getY();
247         m_g = &m_dsa.groupG();
248         m_p = &m_dsa.groupP();
249         m_powermod_y_p = FixedBasePowerMod(m_y, m_p);
250         m_mod_p = ModularReducer(*m_p);
251         m_mod_q = ModularReducer(*m_q);
252     }
253 
254     override size_t messageParts() const { return 2; }
255     override size_t messagePartSize() const { return m_q.bytes(); }
256     override size_t maxInputBits() const { return m_q.bits(); }
257 
258     override bool withRecovery() const { return false; }
259 
260     override SecureVector!ubyte verifyMr(const(ubyte)*, size_t) { throw new InvalidState("Message recovery not supported"); }
261     override bool verify(const(ubyte)* msg, size_t msg_len, const(ubyte)* sig, size_t sig_len)
262     {
263 		//import core.memory : GC; GC.disable(); scope(exit) GC.enable();
264         const BigInt* q = &m_mod_q.getModulus();
265         
266         if (sig_len != 2*q.bytes() || msg_len > q.bytes())
267             return false;
268         
269         BigInt r = BigInt(sig, q.bytes());
270         BigInt s = BigInt(sig + q.bytes(), q.bytes());
271         BigInt i = BigInt(msg, msg_len);
272         if (r <= 0 || r >= *q || s <= 0 || s >= *q)
273             return false;
274         
275         s = inverseMod(&s, q);
276 
277         BigInt s_i;
278 		s_i.reserve(max(m_g.bytes() + m_g.bytes() % 128, m_y.bytes() + m_y.bytes() % 128));
279 		import core.sync.mutex, core.sync.condition;
280 		Mutex mutex = ThreadMem.alloc!Mutex();
281 		scope(exit) {
282 			ThreadMem.free(mutex);
283 		}
284 
285 		struct Handler {
286 			shared(Mutex) mtx;
287 			shared(ModularReducer*) mod_q;
288 			shared(const BigInt*)g2;
289 			shared(const BigInt*)p2;
290 			shared(BigInt*) s2;
291 			shared(BigInt*) i2;
292 			shared(BigInt*) s_i2;
293 			void run() { 	
294 				try {
295 					import botan.libstate.libstate : modexpInit, globalState;
296 					modexpInit(); // enable quick path for powermod
297 					globalState();
298 					BigInt* ret = cast(BigInt*) s_i2;
299 					{
300 						import memutils.utils;
301 						auto powermod_g_p = FixedBasePowerMod(cast(const(BigInt)*)g2, cast(const(BigInt)*)p2);
302 						auto mult = (cast(ModularReducer*)mod_q).multiply(cast(const(BigInt)*)s2, cast(const(BigInt)*)i2);
303 						BigInt _res = (cast(FixedBasePowerModImpl)powermod_g_p)(cast(BigInt*)&mult);
304 						synchronized(cast()mtx) ret.load(&_res);
305 					}
306 				} catch (Exception e) {
307 					logDebug("Got error: ", e.toString);
308 				}
309 			}
310 		}
311 		
312 		auto handler = Handler(cast(shared) mutex, cast(shared)&m_mod_q, cast(shared)m_g, cast(shared)m_p, cast(shared)&s, cast(shared)&i, cast(shared)&s_i);
313 		Unique!Thread thr = new Thread(&handler.run);
314 		thr.start();
315         BigInt s_r_0 = (cast(ModularReducer)m_mod_q).multiply(&s, &r);
316         FixedBasePowerModImpl powermod_y_p = cast(FixedBasePowerModImpl)m_powermod_y_p;
317         BigInt s_r = powermod_y_p(&s_r_0);
318 		thr.join();
319         synchronized(mutex) s = m_mod_p.multiply(cast(const(BigInt)*)&s_i, cast(const(BigInt)*)&s_r);
320         auto r2 = m_mod_q.reduce(s.move);
321         return (r2 == r);
322     }
323 
324 private:
325     const DLSchemePublicKey m_dsa;
326     const BigInt* m_q;
327     const BigInt* m_y;
328     const BigInt* m_g;
329     const BigInt* m_p;
330 
331     FixedBasePowerMod m_powermod_y_p;
332     ModularReducer m_mod_p, m_mod_q;
333 }
334 
335 
336 static if (BOTAN_TEST):
337 
338 import botan.test;
339 import botan.pubkey.test;
340 import botan.rng.auto_rng;
341 import botan.pubkey.pubkey;
342 import botan.codec.hex;
343 import memutils.hashmap;
344 
345 import core.atomic;
346 private shared size_t total_tests;
347 
348 size_t testPkKeygen(RandomNumberGenerator rng) {
349     size_t fails;
350     string[] dsa_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"];
351     foreach (dsa; dsa_list) {
352         atomicOp!"+="(total_tests, 1);
353         auto key = DSAPrivateKey(rng, DLGroup(dsa));
354         key.checkKey(rng, true);
355         fails += validateSaveAndLoad(key, rng);
356     }
357     
358     return fails;
359 }
360 
361 size_t dsaSigKat(string p,
362                    string q,
363                    string g,
364                    string x,
365                    string hash,
366                    string msg,
367                    string nonce,
368                    string signature)
369 {
370     atomicOp!"+="(total_tests, 1);
371     
372 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
373     
374     BigInt p_bn = BigInt(p);
375     BigInt q_bn = BigInt(q);
376     BigInt g_bn = BigInt(g);
377     BigInt x_bn = BigInt(x);
378     
379     DLGroup group = DLGroup(p_bn, q_bn, g_bn);
380     auto privkey = DSAPrivateKey(*rng, group.move(), x_bn.move());
381     
382     auto pubkey = DSAPublicKey(privkey);
383     
384     const string padding = "EMSA1(" ~ hash ~ ")";
385     PKVerifier verify = PKVerifier(*pubkey, padding);
386     PKSigner sign = PKSigner(*privkey, padding);
387     return validateSignature(verify, sign, "DSA/" ~ hash, msg, *rng, nonce, signature);
388 }
389 
390 static if (BOTAN_HAS_TESTS && !SKIP_DSA_TEST) unittest
391 {
392     logDebug("Testing dsa.d ...");
393     size_t fails;
394     
395     Unique!AutoSeededRNG rng = new AutoSeededRNG;
396     
397     File dsa_sig = File("test_data/pubkey/dsa.vec", "r");
398     
399     fails += runTestsBb(dsa_sig, "DSA Signature", "Signature", true,
400         (ref HashMap!(string, string) m)
401         {
402             return dsaSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]);
403         });
404 
405     fails += testPkKeygen(*rng);
406     
407     testReport("dsa", total_tests, fails);
408 }
409