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_dh = dh;
153         m_p = &m_dh.groupP();
154         m_powermod_x_p = FixedExponentPowerMod(&m_dh.getX(), m_p);
155         BigInt k = BigInt(rng, m_p.bits() - 1);
156         auto d = (*m_powermod_x_p)(inverseMod(&k, m_p));
157         m_blinder = Blinder(k, d, *m_p);
158     }
159 
160     override SecureVector!ubyte agree(const(ubyte)* w, size_t w_len)
161     {
162         BigInt input = BigInt.decode(w, w_len);
163         
164         if (input <= 1 || input >= (*m_p) - 1)
165             throw new InvalidArgument("DH agreement - invalid key provided");
166         
167         auto powermod_x_p = cast(FixedExponentPowerModImpl) *m_powermod_x_p;
168         const BigInt r = m_blinder.unblind(powermod_x_p(m_blinder.blind(input)));
169         
170         return BigInt.encode1363(r, m_p.bytes());
171     }
172 
173 private:
174     const DLSchemePrivateKey m_dh;
175     const BigInt* m_p;
176 
177     FixedExponentPowerMod m_powermod_x_p;
178     Blinder m_blinder;
179 }
180 
181 
182 static if (BOTAN_TEST):
183 
184 import botan.test;
185 import botan.pubkey.test;
186 import botan.rng.auto_rng;
187 import botan.pubkey.pubkey;
188 import botan.pubkey.algo.dh;
189 import botan.codec.hex;
190 import botan.asn1.oids;
191 import core.atomic;
192 import memutils.hashmap;
193 
194 private shared size_t total_tests;
195 
196 size_t testPkKeygen(RandomNumberGenerator rng)
197 {
198     size_t fails;
199 
200     string[] dh_list = ["modp/ietf/1024", "modp/ietf/2048", "modp/ietf/4096", "dsa/jce/1024"];
201 
202     foreach (dh; dh_list) {
203         atomicOp!"+="(total_tests, 1);
204         logDebug("1) Load private key");
205 		auto key = DHPrivateKey(rng, DLGroup(dh));
206         logDebug("2) Check private key");
207         key.checkKey(rng, true);
208         logDebug("3) Validate");
209         fails += validateSaveAndLoad(key, rng);
210     }
211     
212     return fails;
213 }
214 
215 size_t dhSigKat(string p, string g, string x, string y, string kdf, string outlen, string key)
216 {
217     atomicOp!"+="(total_tests, 1);
218 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
219     
220     BigInt p_bn = BigInt(p);
221     BigInt g_bn = BigInt(g);
222     BigInt x_bn = BigInt(x);
223     BigInt y_bn = BigInt(y);
224     auto domain = DLGroup(p_bn, g_bn);
225     auto mykey = DHPrivateKey(*rng, domain.clone, x_bn.move());
226     auto otherkey = DHPublicKey(domain.move, y_bn.move());
227     
228     if (kdf == "")
229         kdf = "Raw";
230     
231     size_t keylen = 0;
232     if (outlen != "")
233         keylen = to!uint(outlen);
234     
235     auto kas = scoped!PKKeyAgreement(mykey, kdf);
236     
237     return validateKas(kas, "DH/" ~ kdf, otherkey.publicValue(), key, keylen);
238 }
239 
240 static if (BOTAN_HAS_TESTS && !SKIP_DH_TEST) unittest
241 {import std.datetime;
242 	import core.thread : Thread;
243 	Thread.sleep(5.seconds);
244 
245     logDebug("Testing dh.d ...");
246     size_t fails = 0;
247 
248 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
249 
250 
251     File dh_sig = File("test_data/pubkey/dh.vec", "r");
252     
253     fails += runTestsBb(dh_sig, "DH Kex", "K", true,
254         (ref HashMap!(string, string) m) {
255             return dhSigKat(m["P"], m["G"], m["X"], m["Y"], m.get("KDF"), m.get("OutLen"), m["K"]);
256         });
257     fails += testPkKeygen(*rng);
258 
259     testReport("DH", total_tests, fails);
260 
261 }