1 /**
2 * Nyberg-Rueppel
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.nr;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_NYBERG_RUEPPEL):
15 
16 public import botan.pubkey.pubkey;
17 import botan.pubkey.algo.dl_algo;
18 import botan.pubkey.pk_ops;
19 import botan.math.numbertheory.numthry;
20 import botan.math.numbertheory.reducer;
21 import botan.math.numbertheory.numthry;
22 import botan.pubkey.algo.keypair;
23 import botan.rng.rng;
24 import std.concurrency;
25 import core.thread;
26 import memutils.helpers : Embed;
27 import std.algorithm : max;
28 
29 struct NROptions {
30     enum algoName = "NR";
31     enum format = DLGroup.ANSI_X9_42;
32     enum msgParts = 2;
33 
34     /*
35     * Check Private Nyberg-Rueppel Parameters
36     */
37     static bool checkKey(in DLSchemePrivateKey privkey, RandomNumberGenerator rng, bool strong)
38     {
39         if (!privkey.checkKeyImpl(rng, strong) || privkey.m_x >= privkey.groupQ())
40             return false;
41         
42         if (!strong)
43             return true;
44         
45         return signatureConsistencyCheck(rng, privkey, "EMSA1(SHA-1)");
46     }
47 
48 }
49 
50 /**
51 * Nyberg-Rueppel Public Key
52 */
53 struct NRPublicKey
54 {
55 public:
56     alias Options = NROptions;
57     __gshared immutable string algoName = Options.algoName;
58 
59     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
60     {
61 		m_owned = true;
62         m_pub = new DLSchemePublicKey(Options(), alg_id, key_bits);
63     }
64 
65     /*
66     * NRPublicKey Constructor
67     */
68     this(DLGroup grp, BigInt y1)
69     {
70 		m_owned = true;
71         m_pub = new DLSchemePublicKey(Options(), grp.move, y1.move);
72     }
73 
74     this(PublicKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
75     this(PrivateKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
76 
77     mixin Embed!(m_pub, m_owned);
78 
79 	bool m_owned;
80     DLSchemePublicKey m_pub;
81 }
82 
83 /**
84 * Nyberg-Rueppel Private Key
85 */
86 struct NRPrivateKey
87 {
88 public:
89     alias Options = NROptions;
90     __gshared immutable string algoName = Options.algoName;
91 
92     /*
93     * Create a NR private key
94     */
95     this(RandomNumberGenerator rng, DLGroup grp, BigInt x_arg = BigInt(0))
96     {
97         bool x_arg_0;
98         if (x_arg == 0) {
99             x_arg_0 = true;
100             auto bi = BigInt(2);
101             x_arg = BigInt.randomInteger(rng, bi, grp.getQ() - 1);
102         }
103         BigInt y1 = powerMod(grp.getG(), x_arg, grp.getP());
104 
105 		m_owned = true;
106         m_priv = new DLSchemePrivateKey(Options(), grp.move, y1.move, x_arg.move);
107 
108         if (x_arg_0)
109             m_priv.genCheck(rng);
110         else
111             m_priv.loadCheck(rng);
112     }
113 
114     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits, RandomNumberGenerator rng)
115     { 
116 		m_owned = true;
117         m_priv = new DLSchemePrivateKey(Options(), alg_id, key_bits);
118        
119         m_priv.setY(powerMod(m_priv.groupG(), m_priv.m_x, m_priv.groupP()));
120         
121         m_priv.loadCheck(rng);
122     }
123 
124     mixin Embed!(m_priv, m_owned);
125 
126 	bool m_owned;
127     DLSchemePrivateKey m_priv;
128 
129 }
130 
131 /**
132 * Nyberg-Rueppel signature operation
133 */
134 final class NRSignatureOperation : Signature
135 {
136 public:
137     override size_t messageParts() const { return 2; }
138     override size_t messagePartSize() const { return m_q.bytes(); }
139     override size_t maxInputBits() const { return (m_q.bits() - 1); }
140 
141     this(in PrivateKey pkey) {
142         this(cast(DLSchemePrivateKey) pkey);
143     }
144 
145     this(in NRPrivateKey pkey) {
146         this(pkey.m_priv);
147     }
148 
149     this(in DLSchemePrivateKey nr)
150     {
151         assert(nr.algoName == NRPublicKey.algoName);
152         m_q = &nr.groupQ();
153         m_x = &nr.getX();
154         m_powermod_g_p = FixedBasePowerMod(nr.groupG(), nr.groupP());
155         m_mod_q = ModularReducer(nr.groupQ());
156     }
157 
158     override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng)
159     {
160         rng.addEntropy(msg, msg_len);
161         
162         BigInt f = BigInt(msg, msg_len);
163         
164         if (f >= *m_q)
165             throw new InvalidArgument("NR_Signature_Operation: Input is out of range");
166         
167         BigInt c, d;
168         
169         while (c == 0)
170         {
171             BigInt k;
172             do
173                 k.randomize(rng, m_q.bits());
174             while (k >= *m_q);
175             
176             c = m_mod_q.reduce((*m_powermod_g_p)(k) + f);
177             d = m_mod_q.reduce(k - (*m_x) * c);
178         }
179         
180         SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes());
181         c.binaryEncode(&output[output.length / 2 - c.bytes()]);
182         d.binaryEncode(&output[output.length - d.bytes()]);
183         return output;
184     }
185 private:
186     const BigInt* m_q;
187     const BigInt* m_x;
188     FixedBasePowerMod m_powermod_g_p;
189     ModularReducer m_mod_q;
190 }
191 
192 /**
193 * Nyberg-Rueppel verification operation
194 */
195 final class NRVerificationOperation : Verification
196 {
197 public:
198     this(in PublicKey pkey) {
199         this(cast(DLSchemePublicKey) pkey);
200     }
201 
202     this(in NRPublicKey pkey) {
203         this(pkey.m_pub);
204     }
205 
206     this(in DLSchemePublicKey nr) 
207     {
208         assert(nr.algoName == NRPublicKey.algoName);
209         m_q = &nr.groupQ();
210         m_y = &nr.getY();
211         m_p = &nr.groupP();
212         m_powermod_g_p = FixedBasePowerMod(nr.groupG(), nr.groupP());
213         m_mod_p = ModularReducer(nr.groupP());
214         m_mod_q = ModularReducer(nr.groupQ());
215     }
216 
217     override size_t messageParts() const { return 2; }
218     override size_t messagePartSize() const { return m_q.bytes(); }
219     override size_t maxInputBits() const { return (m_q.bits() - 1); }
220 
221     override bool withRecovery() const { return true; }
222 
223     override bool verify(const(ubyte)*, size_t, const(ubyte)*, size_t)
224     {
225         throw new InvalidState("Message recovery required");
226     }
227 
228     override SecureVector!ubyte verifyMr(const(ubyte)* msg, size_t msg_len)
229     {
230 		//import core.memory : GC; GC.disable(); scope(exit) GC.enable();
231         const BigInt* q = &m_mod_q.getModulus(); // TODO: why not use m_q?
232         if (msg_len != 2*q.bytes())
233             throw new InvalidArgument("NR verification: Invalid signature");
234         
235         BigInt c = BigInt(msg, q.bytes());
236         BigInt d = BigInt(msg + q.bytes(), q.bytes());
237         
238         if (c.isZero() || c >= *q || d >= *q)
239             throw new InvalidArgument("NR verification: Invalid signature");
240 
241 		import core.sync.mutex, core.sync.condition;
242 		Mutex mutex = ThreadMem.alloc!Mutex();
243 		scope(exit) {
244 			ThreadMem.free(mutex);
245 		}
246 
247         BigInt res;
248 		res.reserve(max(m_q.bytes() + m_q.bytes() % 128, m_y.bytes() + m_y.bytes() % 128));
249 
250 		struct Handler {
251 			shared(Mutex) mtx;
252 			shared(const BigInt*) y;
253 			shared(const BigInt*) p;
254 			shared(BigInt*) c2;
255 			shared(BigInt*) res2; 
256 			void run() { 
257 				try {
258 					import botan.libstate.libstate : modexpInit;
259 					modexpInit(); // enable quick path for powermod
260 					BigInt* ret = cast(BigInt*) res2;
261 					{
262 						import memutils.utils;
263 						FixedBasePowerModImpl powermod_y_p = ThreadMem.alloc!FixedBasePowerModImpl(*cast(const BigInt*)y, *cast(const BigInt*)p);
264 						scope(exit) ThreadMem.free(powermod_y_p);
265 						synchronized(cast()mtx) ret.load( powermod_y_p(*cast(BigInt*)c2) );
266 					}
267 				} catch (Exception e) { logDebug("Error: ", e.toString()); }
268 
269 			}
270 		}
271 			
272 		auto handler = Handler(cast(shared) mutex, cast(shared)m_y, cast(shared)m_p, cast(shared)&c, cast(shared)&res);
273 
274 		Unique!Thread thr = new Thread(&handler.run);
275 		thr.start();
276         BigInt g_d = (*m_powermod_g_p)(d);
277 		thr.join();
278 		BigInt i;
279 		synchronized(mutex) i = m_mod_p.multiply(g_d, res);
280         return BigInt.encodeLocked(m_mod_q.reduce(c - i));
281     }
282 private:
283     const BigInt* m_q;
284     const BigInt* m_y;
285     const BigInt* m_p;
286 
287     FixedBasePowerMod m_powermod_g_p;
288     ModularReducer m_mod_p, m_mod_q;
289 }
290 
291 
292 static if (BOTAN_TEST):
293 
294 import botan.test;
295 import botan.pubkey.test;
296 import botan.pubkey.pubkey;
297 import botan.codec.hex;
298 import botan.rng.auto_rng;
299 import core.atomic;
300 import memutils.hashmap;
301 
302 private shared size_t total_tests;
303 
304 size_t testPkKeygen(RandomNumberGenerator rng)
305 {    
306     size_t fails;
307     string[] nr_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"];
308 
309     foreach (nr; nr_list) {
310         atomicOp!"+="(total_tests, 1);
311         auto key = NRPrivateKey(rng, DLGroup(nr));
312         key.checkKey(rng, true);
313         fails += validateSaveAndLoad(key, rng);
314     }
315     
316     return fails;
317 }
318 
319 size_t nrSigKat(string p, string q, string g, string x, 
320                   string hash, string msg, string nonce, string signature)
321 {
322     //logTrace("msg: ", msg);
323     atomicOp!"+="(total_tests, 1);
324 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
325     
326     BigInt p_bn = BigInt(p);
327     BigInt q_bn = BigInt(q);
328     BigInt g_bn = BigInt(g);
329     BigInt x_bn = BigInt(x);
330     
331     DLGroup group = DLGroup(p_bn, q_bn, g_bn);
332 
333     auto privkey = NRPrivateKey(*rng, group.move(), x_bn.move());
334     auto pubkey = NRPublicKey(privkey);
335     
336     const string padding = "EMSA1(" ~ hash ~ ")";
337     
338     PKVerifier verify = PKVerifier(pubkey, padding);
339     PKSigner sign = PKSigner(privkey, padding);
340     
341     return validateSignature(verify, sign, "nr/" ~ hash, msg, *rng, nonce, signature);
342 }
343 
344 static if (BOTAN_HAS_TESTS && !SKIP_NR_TEST) unittest
345 {
346     logDebug("Testing nr.d ...");
347     size_t fails = 0;
348     
349 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
350     
351     File nr_sig = File("../test_data/pubkey/nr.vec", "r");
352     
353     fails += runTestsBb(nr_sig, "NR Signature", "Signature", true,
354         (ref HashMap!(string, string) m) {
355             return nrSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]);
356         });
357 
358     fails += testPkKeygen(*rng);
359     
360     testReport("nr", total_tests, fails);
361 }