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