1 /**
2 * Rabin-Williams
3 * 
4 * Copyright:
5 * (C) 1999-2007 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.rw;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_RW):
15 
16 public import botan.pubkey.pubkey;
17 import botan.pubkey.algo.if_algo;
18 import botan.pubkey.pk_ops;
19 import botan.math.numbertheory.reducer;
20 import botan.pubkey.blinding;
21 import botan.math.numbertheory.numthry;
22 import botan.pubkey.algo.keypair;
23 import botan.utils.parsing;
24 import botan.utils.types;
25 import memutils.helpers;
26 import std.algorithm;
27 import std.concurrency;
28 import core.thread;
29 
30 struct RWOptions {
31     enum algoName = "RW";
32 
33     /*
34     * Check Private Rabin-Williams Parameters
35     */
36     static bool checkKey(in IFSchemePrivateKey privkey, RandomNumberGenerator rng, bool strong)
37     {
38         if (!privkey.checkKeyImpl(rng, strong))
39             return false;
40         
41         if (!strong)
42             return true;
43         
44         auto p_minus_1 = privkey.getP() - 1;
45         auto q_minus_1 = privkey.getQ() - 1;
46         auto arg1 = (privkey.getE() * privkey.getD());
47         auto arg2 = (lcm(&p_minus_1, &q_minus_1) >> 1);
48         auto mod_res = arg1 % arg2;
49         logTrace(mod_res.toString());
50         import botan.math.bigint.divide;
51         BigInt q, r;
52         logTrace("Calling divide with arg1arg2:");
53         logTrace(arg1.toString());
54         logTrace(arg2.toString());
55         divide(&arg1, &arg2, &q, &r, true);
56         logTrace(r.toString());
57 
58         if (mod_res != 1)
59             return false;
60                
61         return signatureConsistencyCheck(rng, privkey, "EMSA2(SHA-1)");
62     }
63 }
64 /**
65 * Rabin-Williams Public Key
66 */
67 struct RWPublicKey
68 {
69 public:
70     alias Options = RWOptions;
71     __gshared immutable string algoName = Options.algoName;
72 
73     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits)
74     {
75 		m_owned = true;
76         m_pub = new IFSchemePublicKey(Options(), alg_id, key_bits);
77     }
78 
79     this(BigInt mod, BigInt exponent)
80     {
81 		m_owned = true;
82         m_pub = new IFSchemePublicKey(Options(), mod.move(), exponent.move());
83     }
84 
85     this(PrivateKey pkey) { m_pub = cast(IFSchemePublicKey) pkey; }
86     this(PublicKey pkey) { m_pub = cast(IFSchemePublicKey) pkey; }
87 
88     mixin Embed!(m_pub, m_owned);
89 
90 	bool m_owned;
91     IFSchemePublicKey m_pub;
92 }
93 
94 /**
95 * Rabin-Williams Private Key
96 */
97 struct RWPrivateKey
98 {
99 public:
100     alias Options = RWOptions;
101     __gshared immutable string algoName = Options.algoName;
102 
103     this(in AlgorithmIdentifier alg_id,
104          const ref SecureVector!ubyte key_bits,
105          RandomNumberGenerator rng) 
106     {
107 		m_owned = true;
108         m_priv = new IFSchemePrivateKey(Options(), rng, alg_id, key_bits);
109     }
110 
111     this(RandomNumberGenerator rng,
112          BigInt p, BigInt q,
113          BigInt e, BigInt d = BigInt(0),
114          BigInt n = BigInt(0))
115     {
116 		m_owned = true;
117         m_priv = new IFSchemePrivateKey(Options(), rng, p.move(), q.move(), e.move(), d.move(), n.move());
118     }
119 
120     /*
121     * Create a Rabin-Williams private key
122     */
123     this(RandomNumberGenerator rng, size_t bits, size_t exp = 2)
124     {
125         if (bits < 1024)
126             throw new InvalidArgument(algoName ~ ": Can't make a key that is only " ~
127                                        to!string(bits) ~ " bits long");
128         if (exp < 2 || exp % 2 == 1)
129             throw new InvalidArgument(algoName ~ ": Invalid encryption exponent");
130 
131         BigInt p, q, e, d, n, d1, d2;
132 
133         e = exp;
134         
135         do
136         {
137             p = randomPrime(rng, (bits + 1) / 2, e / 2, 3, 4);
138             q = randomPrime(rng, bits - p.bits(), e / 2, ((p % 8 == 3) ? 7 : 3), 8);
139             n = p * q;
140         } while (n.bits() != bits);
141         auto p_minus_1 = p-1;
142         auto q_minus_1 = q-1;    
143         auto d_0 = lcm(&p_minus_1, &q_minus_1);
144         d_0 >>= 1;
145         d = inverseMod(&e, &d_0);
146 
147 		m_owned = true;
148         m_priv = new IFSchemePrivateKey(Options(), rng, p.move(), q.move(), e.move(), d.move(), n.move());
149 
150         genCheck(rng);
151     }
152 
153     mixin Embed!(m_priv, m_owned);
154 
155     this(PrivateKey pkey) { m_priv = cast(IFSchemePrivateKey) pkey; }
156 	bool m_owned;
157     IFSchemePrivateKey m_priv;
158 }
159 
160 /**
161 * Rabin-Williams Signature Operation
162 */
163 final class RWSignatureOperation : Signature
164 {
165 public:
166     this(in RWPrivateKey pkey) {
167         this(pkey.m_priv);
168     }
169 
170     this(in PrivateKey pkey) {
171         this(cast(IFSchemePrivateKey) pkey);
172     }
173 
174     this(in IFSchemePrivateKey rw) 
175     {
176         assert(rw.algoName == RWPublicKey.algoName);
177         m_priv_key = rw;
178         m_n = &m_priv_key.getN();
179         m_e = &m_priv_key.getE();
180         m_q = &m_priv_key.getQ();
181         m_c = &m_priv_key.getC();
182         m_d1 = &m_priv_key.getD1();
183         m_p = &m_priv_key.getP();
184         m_powermod_d1_p = FixedExponentPowerMod(m_d1, m_p);
185         m_powermod_d2_q = FixedExponentPowerMod(&m_priv_key.getD2(), m_q);
186         m_mod_p = ModularReducer(*m_p);
187     }
188     override size_t messageParts() const { return 1; }
189     override size_t messagePartSize() const { return 0; }
190     override size_t maxInputBits() const { return (m_n.bits() - 1); }
191 
192     override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng)
193     {
194 		import core.memory : GC; GC.disable(); scope(exit) GC.enable();
195 		import core.thread;
196         rng.addEntropy(msg, msg_len);
197 
198         if (!m_blinder.initialized()) { // initialize here because we need rng
199             BigInt k = BigInt(rng, std.algorithm.min(160, m_n.bits() - 1));
200             auto e = powerMod(&k, m_e, m_n);
201             m_blinder = Blinder(e, inverseMod(&k, m_n), *m_n);
202         }
203 
204         BigInt i = BigInt(msg, msg_len);
205         
206         if (i >= *m_n || i % 16 != 12)
207             throw new InvalidArgument("Rabin-Williams: invalid input");
208         
209         if (jacobi(&i, m_n) != 1)
210             i >>= 1;        
211         i = m_blinder.blind(i);
212         const BigInt j1 = (cast(FixedExponentPowerModImpl)*m_powermod_d1_p)(cast(BigInt*)&i);
213 		const BigInt j2 = (cast(FixedExponentPowerModImpl)*m_powermod_d2_q)(cast(BigInt*)&i);		
214 		BigInt j3 = m_mod_p.reduce(subMul(&j1, &j2, m_c));
215         BigInt r = m_blinder.unblind(mulAdd(&j3, m_q, &j2)); 
216         BigInt cmp2 = *m_n - r;
217         BigInt min_val = r.move();
218         if (cmp2 < min_val)
219             min_val = cmp2.move();
220         auto ret = BigInt.encode1363(&min_val, m_n.bytes());
221         return ret;
222     }
223 private:
224     const IFSchemePrivateKey m_priv_key;
225     const BigInt* m_n;
226     const BigInt* m_e;
227     const BigInt* m_q;
228     const BigInt* m_c;
229     const BigInt* m_d1;
230     const BigInt* m_p;
231 
232     FixedExponentPowerMod m_powermod_d1_p, m_powermod_d2_q;
233     ModularReducer m_mod_p;
234     Blinder m_blinder;
235 }
236 
237 /**
238 * Rabin-Williams Verification Operation
239 */
240 final class RWVerificationOperation : Verification
241 {
242 public:
243     this(in PublicKey pkey) {
244         this(cast(IFSchemePublicKey) pkey);
245     }
246 
247     this(in RWPublicKey pkey) {
248         this(pkey.m_pub);
249     }
250 
251     this(in IFSchemePublicKey rw)
252     {
253         assert(rw.algoName == RWPublicKey.algoName);
254         m_n = &rw.getN();
255         m_e = &rw.getE();
256         m_powermod_e_n = FixedExponentPowerMod(m_e, m_n);
257     }
258     override size_t messageParts() const { return 1; }
259     override size_t messagePartSize() const { return 0; }
260     override size_t maxInputBits() const { return (m_n.bits() - 1); }
261     override bool withRecovery() const { return true; }
262 
263     override bool verify(const(ubyte)*, size_t, const(ubyte)*, size_t)
264     {
265         throw new InvalidState("Message recovery required");
266     }
267 
268     override SecureVector!ubyte verifyMr(const(ubyte)* msg, size_t msg_len)
269     {
270         BigInt m = BigInt(msg, msg_len);
271         
272         if ((m > (*m_n >> 1)) || m.isNegative())
273             throw new InvalidArgument("RW signature verification: m > n / 2 || m < 0");
274         m_powermod_e_n = FixedExponentPowerMod(m_e, m_n);
275         BigInt r = (cast()*m_powermod_e_n)(cast(BigInt*)&m);
276         if (r % 16 == 12)
277             return BigInt.encodeLocked(&r);
278         if (r % 8 == 6)
279             return BigInt.encodeLocked(r*2);
280         
281         r = (*m_n) - r;
282         if (r % 16 == 12)
283             return BigInt.encodeLocked(&r);
284         if (r % 8 == 6)
285             return BigInt.encodeLocked(r*2);
286         
287         throw new InvalidArgument("RW signature verification: Invalid signature");
288     }
289 
290 private:
291     const BigInt* m_n;
292     const BigInt* m_e;
293     FixedExponentPowerMod m_powermod_e_n;
294 }
295 
296 
297 static if (BOTAN_TEST):
298 import botan.test;
299 import botan.pubkey.test;
300 import botan.rng.auto_rng;
301 import botan.codec.hex;
302 import core.atomic;
303 import memutils.hashmap;
304 
305 shared size_t total_tests;
306 __gshared immutable string padding = "EMSA2(SHA-1)";
307 
308 size_t testPkKeygen(RandomNumberGenerator rng)
309 {
310     atomicOp!"+="(total_tests, 1);
311     size_t fails;
312     auto rw1024 = RWPrivateKey(rng, 1024);
313     rw1024.checkKey(rng, true);
314     fails += validateSaveAndLoad(rw1024, rng);
315     return fails;
316 }
317 size_t rwSigKat(string e,
318                   string p,
319                   string q,
320                   string msg,
321                   string signature)
322 {
323     atomicOp!"+="(total_tests, 1);
324 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
325     
326     auto privkey = RWPrivateKey(*rng, BigInt(p), BigInt(q), BigInt(e));
327     
328     auto pubkey = RWPublicKey(privkey);
329     
330     PKVerifier verify = PKVerifier(pubkey, padding);
331     PKSigner sign = PKSigner(privkey, padding);
332     
333     return validateSignature(verify, sign, "RW/" ~ padding, msg, *rng, signature);
334 }
335 
336 size_t rwSigVerify(string e,
337                      string n,
338                      string msg,
339                      string signature)
340 {
341     atomicOp!"+="(total_tests, 1);
342 
343     BigInt e_bn = BigInt(e);
344     BigInt n_bn = BigInt(n);
345     
346     auto key = RWPublicKey(n_bn.move(), e_bn.move());
347     
348     PKVerifier verify = PKVerifier(key, padding);
349     
350     if (!verify.verifyMessage(hexDecode(msg), hexDecode(signature)))
351         return 1;
352     return 0;
353 }
354 
355 static if (BOTAN_HAS_TESTS && !SKIP_RW_TEST) unittest
356 {
357 	import core.thread : Thread;
358 	//Thread.sleep(10.seconds);
359     logDebug("Testing rw.d ...");
360     size_t fails = 0;
361     
362 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
363     
364     fails += testPkKeygen(*rng);
365     
366     File rw_sig = File("test_data/pubkey/rw_sig.vec", "r");
367     File rw_verify = File("test_data/pubkey/rw_verify.vec", "r");
368     
369     fails += runTestsBb(rw_sig, "RW Signature", "Signature", true,
370         (ref HashMap!(string, string) m) {
371             return rwSigKat(m["E"], m["P"], m["Q"], m["Msg"], m["Signature"]);
372         });
373     
374     fails += runTestsBb(rw_verify, "RW Verify", "Signature", true,
375         (ref HashMap!(string, string) m) {
376             return rwSigVerify(m["E"], m["N"], m["Msg"], m["Signature"]);
377         });
378 
379     testReport("rw", total_tests, fails);
380 }