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