1 /**
2 * Diffie-Hellman
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.dh;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_DIFFIE_HELLMAN):
15 
16 public import botan.pubkey.algo.dl_algo;
17 public import botan.pubkey.pubkey;
18 public import botan.pubkey.algo.ec_group;
19 import botan.math.numbertheory.pow_mod;
20 import botan.pubkey.blinding;
21 import botan.pubkey.pk_ops;
22 import botan.math.numbertheory.numthry;
23 import botan.pubkey.workfactor;
24 import botan.rng.rng;
25 import memutils.helpers : Embed;
26 
27 struct DHOptions {
28     enum algoName = "DH";
29     enum format = DLGroup.ANSI_X9_42;
30 }
31 
32 /**
33 * This class represents Diffie-Hellman public keys.
34 */
35 struct DHPublicKey
36 {
37 public:
38     alias Options = DHOptions;
39     __gshared immutable string algoName = Options.algoName;
40 
41     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits)
42     {
43 		m_owned = true;
44         m_pub = new DLSchemePublicKey(Options(), alg_id, key_bits);
45     }
46 
47     /**
48     * Construct a public key with the specified parameters.
49     *
50     * Params:
51     *  grp = the DL group to use in the key
52     *  y1 = the public value y
53     */
54     this(DLGroup grp, BigInt y1)
55     {
56 		m_owned = true;
57         m_pub = new DLSchemePublicKey(Options(), grp.move, y1.move);
58     }
59 
60     this(PublicKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
61     this(PrivateKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; }
62 
63     mixin Embed!(m_pub, m_owned);
64 
65 	bool m_owned;
66     DLSchemePublicKey m_pub;
67 }
68 
69 /**
70 * This class represents Diffie-Hellman private keys.
71 */
72 struct DHPrivateKey
73 {
74 public:
75     alias Options = DHOptions;
76     __gshared immutable string algoName = Options.algoName;
77 
78     /**
79     * Load a DH private key
80     * Params:
81     *  alg_id = the algorithm id
82     *  key_bits = the subject public key
83     *  rng = a random number generator
84     */
85     this()(in AlgorithmIdentifier alg_id,
86            auto const ref SecureVector!ubyte key_bits,
87            RandomNumberGenerator rng) 
88     {
89 
90 		m_owned = true;
91         m_priv = new DLSchemePrivateKey(Options(), alg_id, key_bits);
92         if (m_priv.getY() == 0)
93             m_priv.setY(powerMod(m_priv.groupG(), m_priv.getX(), m_priv.groupP()));
94         m_priv.loadCheck(rng);
95     }
96 
97     /**
98     * Construct a private key with predetermined value.
99     *
100     * Params:
101     *  rng = random number generator to use
102     *  grp = the group to be used in the key
103     *  x_arg = the key's secret value (or if zero, generate a new key)
104     */
105     this(RandomNumberGenerator rng, DLGroup grp, BigInt x_arg = BigInt(0))
106     {
107         
108         const BigInt* p = &grp.getP();
109 
110         bool x_arg_0;
111         if (x_arg == 0) {
112             x_arg_0 = true;
113             x_arg.randomize(rng, 2 * dlWorkFactor(p.bits()));
114         }
115         BigInt y1 = powerMod(grp.getG(), x_arg, *p);
116         
117 		m_owned = true;
118         m_priv = new DLSchemePrivateKey(Options(), grp.move, y1.move, x_arg.move);
119 
120         if (x_arg_0)
121             m_priv.genCheck(rng);
122         else
123             m_priv.loadCheck(rng);
124     }
125 
126     this(PrivateKey pkey) { m_priv = cast(DLSchemePrivateKey) pkey; }
127 
128     mixin Embed!(m_priv, m_owned);
129 	bool m_owned;
130     DLSchemePrivateKey m_priv;
131 
132 
133 }
134 
135 /**
136 * DH operation
137 */
138 class DHKAOperation : KeyAgreement
139 {
140 public:
141     this(in PrivateKey pkey, RandomNumberGenerator rng) {
142         this(cast(DLSchemePrivateKey) pkey, rng);
143     }
144 
145     this(in DHPrivateKey pkey, RandomNumberGenerator rng) {
146         this(pkey.m_priv, rng);
147     }
148 
149     this(in DLSchemePrivateKey dh, RandomNumberGenerator rng) 
150     {
151         assert(dh.algoName == DHPublicKey.algoName);
152         m_p = &dh.groupP();
153         m_powermod_x_p = FixedExponentPowerMod(dh.getX(), *m_p);
154         BigInt k = BigInt(rng, m_p.bits() - 1);
155         auto d = (*m_powermod_x_p)(inverseMod(k, *m_p));
156         m_blinder = Blinder(k, d, *m_p);
157     }
158 
159     override SecureVector!ubyte agree(const(ubyte)* w, size_t w_len)
160     {
161         BigInt input = BigInt.decode(w, w_len);
162         
163         if (input <= 1 || input >= *m_p - 1)
164             throw new InvalidArgument("DH agreement - invalid key provided");
165         
166         const BigInt r = m_blinder.unblind((*m_powermod_x_p)(m_blinder.blind(input)));
167         
168         return BigInt.encode1363(r, m_p.bytes());
169     }
170 
171 private:
172     const BigInt* m_p;
173 
174     FixedExponentPowerMod m_powermod_x_p;
175     Blinder m_blinder;
176 }
177 
178 
179 static if (BOTAN_TEST):
180 
181 import botan.test;
182 import botan.pubkey.test;
183 import botan.rng.auto_rng;
184 import botan.pubkey.pubkey;
185 import botan.pubkey.algo.dh;
186 import botan.codec.hex;
187 import botan.asn1.oids;
188 import core.atomic;
189 import memutils.hashmap;
190 
191 private shared size_t total_tests;
192 
193 size_t testPkKeygen(RandomNumberGenerator rng)
194 {
195     size_t fails;
196 
197     string[] dh_list = ["modp/ietf/1024", "modp/ietf/2048", "modp/ietf/4096", "dsa/jce/1024"];
198 
199     foreach (dh; dh_list) {
200         atomicOp!"+="(total_tests, 1);
201         logDebug("1) Load private key");
202 		auto key = DHPrivateKey(rng, DLGroup(dh));
203         logDebug("2) Check private key");
204         key.checkKey(rng, true);
205         logDebug("3) Validate");
206         fails += validateSaveAndLoad(key, rng);
207     }
208     
209     return fails;
210 }
211 
212 size_t dhSigKat(string p, string g, string x, string y, string kdf, string outlen, string key)
213 {
214     atomicOp!"+="(total_tests, 1);
215 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
216     
217     BigInt p_bn = BigInt(p);
218     BigInt g_bn = BigInt(g);
219     BigInt x_bn = BigInt(x);
220     BigInt y_bn = BigInt(y);
221     auto domain = DLGroup(p_bn, g_bn);
222     auto mykey = DHPrivateKey(*rng, domain.dup, x_bn.move());
223     auto otherkey = DHPublicKey(domain.move, y_bn.move());
224     
225     if (kdf == "")
226         kdf = "Raw";
227     
228     size_t keylen = 0;
229     if (outlen != "")
230         keylen = to!uint(outlen);
231     
232     auto kas = scoped!PKKeyAgreement(mykey, kdf);
233     
234     return validateKas(kas, "DH/" ~ kdf, otherkey.publicValue(), key, keylen);
235 }
236 
237 static if (BOTAN_HAS_TESTS && !SKIP_DH_TEST) unittest
238 {import std.datetime;
239 	import core.thread : Thread;
240 	Thread.sleep(5.seconds);
241 
242     logDebug("Testing dh.d ...");
243     size_t fails = 0;
244 
245 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
246 
247 
248     File dh_sig = File("../test_data/pubkey/dh.vec", "r");
249     
250     fails += runTestsBb(dh_sig, "DH Kex", "K", true,
251         (ref HashMap!(string, string) m) {
252             return dhSigKat(m["P"], m["G"], m["X"], m["Y"], m.get("KDF"), m.get("OutLen"), m["K"]);
253         });
254     fails += testPkKeygen(*rng);
255 
256     testReport("DH", total_tests, fails);
257 
258 }