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_nr = nr;
153         m_q = &m_nr.groupQ();
154         m_x = &m_nr.getX();
155         m_powermod_g_p = FixedBasePowerMod(&m_nr.groupG(), &m_nr.groupP());
156         m_mod_q = ModularReducer(m_nr.groupQ());
157     }
158 
159     override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng)
160     {
161         rng.addEntropy(msg, msg_len);
162         
163         BigInt f = BigInt(msg, msg_len);
164         
165         if (f >= *m_q)
166             throw new InvalidArgument("NR_Signature_Operation: Input is out of range");
167         
168         BigInt c, d;
169         
170         while (c == 0)
171         {
172             BigInt k;
173             do
174                 k.randomize(rng, m_q.bits());
175             while (k >= *m_q);
176             auto pow_mod = (cast()*m_powermod_g_p)(&k);
177             c = m_mod_q.reduce(pow_mod + f);
178             d = m_mod_q.reduce(k - (*m_x) * c);
179         }
180         
181         SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes());
182         c.binaryEncode(&output[output.length / 2 - c.bytes()]);
183         d.binaryEncode(&output[output.length - d.bytes()]);
184         return output;
185     }
186 private: 
187     const DLSchemePrivateKey m_nr;
188     const BigInt* m_q;
189     const BigInt* m_x;
190     FixedBasePowerMod m_powermod_g_p;
191     ModularReducer m_mod_q;
192 }
193 
194 /**
195 * Nyberg-Rueppel verification operation
196 */
197 final class NRVerificationOperation : Verification
198 {
199 public:
200     this(in PublicKey pkey) {
201         this(cast(DLSchemePublicKey) pkey);
202     }
203 
204     this(in NRPublicKey pkey) {
205         this(pkey.m_pub);
206     }
207 
208     this(in DLSchemePublicKey nr) 
209     {
210         assert(nr.algoName == NRPublicKey.algoName);
211         m_nr = nr;
212         m_q = &nr.groupQ();
213         m_y = &nr.getY();
214         m_p = &nr.groupP();
215         m_g = &nr.groupG();
216         m_powermod_g_p = FixedBasePowerMod(m_g, m_p);
217         m_powermod_y_p = FixedBasePowerMod(m_y, m_p);
218         m_mod_p = ModularReducer(nr.groupP());
219         m_mod_q = ModularReducer(nr.groupQ());
220     }
221 
222     override size_t messageParts() const { return 2; }
223     override size_t messagePartSize() const { return m_q.bytes(); }
224     override size_t maxInputBits() const { return (m_q.bits() - 1); }
225 
226     override bool withRecovery() const { return true; }
227 
228     override bool verify(const(ubyte)*, size_t, const(ubyte)*, size_t)
229     {
230         throw new InvalidState("Message recovery required");
231     }
232 
233     override SecureVector!ubyte verifyMr(const(ubyte)* msg, size_t msg_len)
234     {
235 		//import core.memory : GC; GC.disable(); scope(exit) GC.enable();
236         const BigInt* q = &m_mod_q.getModulus(); // TODO: why not use m_q?
237         if (msg_len != 2*q.bytes())
238             throw new InvalidArgument("NR verification: Invalid signature");
239         
240         BigInt c = BigInt(msg, q.bytes());
241         BigInt d = BigInt(msg + q.bytes(), q.bytes());
242         
243         if (c.isZero() || c >= *q || d >= *q)
244             throw new InvalidArgument("NR verification: Invalid signature");
245         BigInt g_d = (cast(FixedBasePowerModImpl)*m_powermod_g_p)(&d);
246         BigInt y_p = (cast(FixedBasePowerModImpl)*m_powermod_y_p)(&c);
247 		BigInt i = m_mod_p.multiply(&g_d, &y_p);
248         return BigInt.encodeLocked(m_mod_q.reduce(c - i));
249     }
250 private:
251     const DLSchemePublicKey m_nr;
252     const BigInt* m_q;
253     const BigInt* m_y;
254     const BigInt* m_p;
255     const BigInt* m_g;
256 
257     FixedBasePowerMod m_powermod_g_p, m_powermod_y_p;
258     ModularReducer m_mod_p, m_mod_q;
259 }
260 
261 
262 static if (BOTAN_TEST):
263 
264 import botan.test;
265 import botan.pubkey.test;
266 import botan.pubkey.pubkey;
267 import botan.codec.hex;
268 import botan.rng.auto_rng;
269 import core.atomic;
270 import memutils.hashmap;
271 
272 private shared size_t total_tests;
273 
274 size_t testPkKeygen(RandomNumberGenerator rng)
275 {    
276     size_t fails;
277     string[] nr_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"];
278 
279     foreach (nr; nr_list) {
280         atomicOp!"+="(total_tests, 1);
281         auto key = NRPrivateKey(rng, DLGroup(nr));
282         key.checkKey(rng, true);
283         fails += validateSaveAndLoad(key, rng);
284     }
285     
286     return fails;
287 }
288 
289 size_t nrSigKat(string p, string q, string g, string x, 
290                   string hash, string msg, string nonce, string signature)
291 {
292     //logTrace("msg: ", msg);
293     atomicOp!"+="(total_tests, 1);
294 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
295     
296     BigInt p_bn = BigInt(p);
297     BigInt q_bn = BigInt(q);
298     BigInt g_bn = BigInt(g);
299     BigInt x_bn = BigInt(x);
300     
301     DLGroup group = DLGroup(p_bn, q_bn, g_bn);
302 
303     auto privkey = NRPrivateKey(*rng, group.move(), x_bn.move());
304     auto pubkey = NRPublicKey(privkey);
305     
306     const string padding = "EMSA1(" ~ hash ~ ")";
307     
308     PKVerifier verify = PKVerifier(pubkey, padding);
309     PKSigner sign = PKSigner(privkey, padding);
310     
311     return validateSignature(verify, sign, "nr/" ~ hash, msg, *rng, nonce, signature);
312 }
313 
314 static if (BOTAN_HAS_TESTS && !SKIP_NR_TEST) unittest
315 {
316     logDebug("Testing nr.d ...");
317     size_t fails = 0;
318     
319 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
320     
321     File nr_sig = File("test_data/pubkey/nr.vec", "r");
322     
323     fails += runTestsBb(nr_sig, "NR Signature", "Signature", true,
324         (ref HashMap!(string, string) m) {
325             return nrSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]);
326         });
327 
328     fails += testPkKeygen(*rng);
329     
330     testReport("nr", total_tests, fails);
331 }