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 							FixedBasePowerModImpl powermod_g_p = ThreadMem.alloc!FixedBasePowerModImpl((*cast(const BigInt*)g), (*cast(const BigInt*)p));
195 							scope(exit) ThreadMem.free(powermod_g_p);
196 							BigInt _res = (cast(ModularReducer*)mod_q).reduce(powermod_g_p(*cast(BigInt*)k2));
197 							//logDebug("Got: ", _res.bytes());
198 							synchronized(cast()mtx) ret.load(_res);
199 						}
200 					}
201 					catch (Exception e) { logDebug("Error: ", e.toString()); }
202 				}
203 			}
204 			
205 			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);
206 			Unique!Thread thr = new Thread(&handler.run);
207 			thr.start();
208             s = inverseMod(k, *m_q);
209 			thr.join();
210 			synchronized(mutex) r = res.dup;
211 			BigInt s_arg = mulAdd(*m_x, r, i);
212             s = m_mod_q.multiply(s, s_arg);
213         }
214         
215         SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes());
216         r.binaryEncode(&output[output.length / 2 - r.bytes()]);
217         s.binaryEncode(&output[output.length - s.bytes()]);
218         return output.move;
219     }
220 private:
221     const BigInt* m_q;
222     const BigInt* m_x;
223     const BigInt* m_g;
224     const BigInt* m_p;
225     ModularReducer m_mod_q;
226 }
227 
228 /**
229 * Object that can verify a DSA signature
230 */
231 final class DSAVerificationOperation : Verification
232 {
233 public:
234     this(in PublicKey pkey) {
235         this(cast(DLSchemePublicKey) pkey);
236     }
237 
238     this(in DSAPublicKey pkey) {
239         this(pkey.m_pub);
240     }
241 
242     this(in DLSchemePublicKey dsa) 
243     {
244         assert(dsa.algoName == DSAPublicKey.algoName);
245         m_q = &dsa.groupQ();
246         m_y = &dsa.getY();
247         m_g = &dsa.groupG();
248         m_p = &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 						FixedBasePowerModImpl powermod_g_p = ThreadMem.alloc!FixedBasePowerModImpl(*cast(const BigInt*)g2, *cast(const BigInt*)p2);
302 						scope(exit) ThreadMem.free(powermod_g_p);
303 						auto mult = (*cast(ModularReducer*)mod_q).multiply(*cast(BigInt*)s2, *cast(BigInt*)i2);
304 						BigInt _res = powermod_g_p(mult);
305 						synchronized(cast()mtx) ret.load(_res);
306 					}
307 				} catch (Exception e) {
308 					logDebug("Got error: ", e.toString);
309 				}
310 			}
311 		}
312 		
313 		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);
314 		Unique!Thread thr = new Thread(&handler.run);
315 		thr.start();
316 		auto mult = m_mod_q.multiply(s, r);
317         BigInt s_r = (*m_powermod_y_p)(mult.move);
318 		thr.join();
319         synchronized(mutex) s = m_mod_p.multiply(s_i, s_r);
320         auto r2 = m_mod_q.reduce(s.move);
321         return (r2 == r);
322     }
323 
324 private:
325     const BigInt* m_q;
326     const BigInt* m_y;
327     const BigInt* m_g;
328     const BigInt* m_p;
329 
330     FixedBasePowerMod m_powermod_y_p;
331     ModularReducer m_mod_p, m_mod_q;
332 }
333 
334 
335 static if (BOTAN_TEST):
336 
337 import botan.test;
338 import botan.pubkey.test;
339 import botan.rng.auto_rng;
340 import botan.pubkey.pubkey;
341 import botan.codec.hex;
342 import memutils.hashmap;
343 
344 import core.atomic;
345 private shared size_t total_tests;
346 
347 size_t testPkKeygen(RandomNumberGenerator rng) {
348     size_t fails;
349     string[] dsa_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"];
350     foreach (dsa; dsa_list) {
351         atomicOp!"+="(total_tests, 1);
352         auto key = DSAPrivateKey(rng, DLGroup(dsa));
353         key.checkKey(rng, true);
354         fails += validateSaveAndLoad(key, rng);
355     }
356     
357     return fails;
358 }
359 
360 size_t dsaSigKat(string p,
361                    string q,
362                    string g,
363                    string x,
364                    string hash,
365                    string msg,
366                    string nonce,
367                    string signature)
368 {
369     atomicOp!"+="(total_tests, 1);
370     
371 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
372     
373     BigInt p_bn = BigInt(p);
374     BigInt q_bn = BigInt(q);
375     BigInt g_bn = BigInt(g);
376     BigInt x_bn = BigInt(x);
377     
378     DLGroup group = DLGroup(p_bn, q_bn, g_bn);
379     auto privkey = DSAPrivateKey(*rng, group.move(), x_bn.move());
380     
381     auto pubkey = DSAPublicKey(*privkey);
382     
383     const string padding = "EMSA1(" ~ hash ~ ")";
384     PKVerifier verify = PKVerifier(*pubkey, padding);
385     PKSigner sign = PKSigner(*privkey, padding);
386     return validateSignature(verify, sign, "DSA/" ~ hash, msg, *rng, nonce, signature);
387 }
388 
389 static if (BOTAN_HAS_TESTS && !SKIP_DSA_TEST) unittest
390 {
391     logDebug("Testing dsa.d ...");
392     size_t fails;
393     
394     Unique!AutoSeededRNG rng = new AutoSeededRNG;
395     
396     File dsa_sig = File("../test_data/pubkey/dsa.vec", "r");
397     
398     fails += runTestsBb(dsa_sig, "DSA Signature", "Signature", true,
399         (ref HashMap!(string, string) m)
400         {
401             return dsaSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]);
402         });
403 
404     fails += testPkKeygen(*rng);
405     
406     testReport("dsa", total_tests, fails);
407 }
408