1 /**
2 * ECDH
3 * 
4 * Copyright:
5 * (C) 2007 Falko Strenzke, FlexSecure GmbH
6 *          Manuel Hartl, FlexSecure GmbH
7 * (C) 2008-2010 Jack Lloyd
8 * (C) 2014-2015 Etienne Cimon
9 *
10 * License:
11 * Botan is released under the Simplified BSD License (see LICENSE.md)
12 */
13 module botan.pubkey.algo.ecdh;
14 
15 import botan.constants;
16 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_ECDH):
17 
18 public import botan.pubkey.pubkey;
19 import botan.pubkey.algo.ecc_key;
20 import botan.pubkey.pk_ops;
21 import botan.math.bigint.bigint;
22 import memutils.helpers : Embed;
23 
24 struct ECDHOptions {
25     enum algoName = "ECDH";
26     enum msgParts = 1;
27 }
28 
29 /**
30 * This class represents ECDH Public Keys.
31 */
32 struct ECDHPublicKey
33 {
34 public:
35     alias Options = ECDHOptions;
36     __gshared immutable string algoName = Options.algoName;
37 
38     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
39     { 
40 		m_owned = true;
41         m_pub = new ECPublicKey(Options(), alg_id, key_bits);
42     }
43 
44     /**
45     * Construct a public key from a given public point.
46     *
47     * Params:
48     *  dom_par = the domain parameters associated with this key
49     *  public_point = the public point defining this key
50     */
51     this()(auto const ref ECGroup dom_par, auto const ref PointGFp public_point) 
52     {
53 		m_owned = true;
54         m_pub = new ECPublicKey(Options(), dom_par, public_point);
55     }
56 
57     this(PrivateKey pkey) { m_pub = cast(ECPublicKey) pkey; }
58     this(PublicKey pkey) { m_pub = cast(ECPublicKey) pkey; }
59 
60     mixin Embed!(m_pub, m_owned);
61 
62 	bool m_owned;
63     ECPublicKey m_pub;
64 }
65 
66 /**
67 * This class represents ECDH Private Keys.
68 */
69 struct ECDHPrivateKey
70 {
71 public:
72     alias Options = ECDHOptions;
73     __gshared immutable string algoName = Options.algoName;
74 
75     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
76     {
77 		m_owned = true;
78         m_priv = new ECPrivateKey(Options(), alg_id, key_bits);
79     }
80 
81     /**
82     * Generate a new private key
83     * Params:
84     *  rng = a random number generator
85     *  domain = parameters to used for this key
86     *  x = the private key; if zero, a new random key is generated
87     */
88     this(RandomNumberGenerator rng, const ref ECGroup domain, BigInt x = BigInt(0)) 
89     {
90 		m_owned = true;
91         m_priv = new ECPrivateKey(Options(), rng, domain, x);
92     }
93 
94     this(RandomNumberGenerator rng, const ref ECGroup domain) { auto bi = BigInt(0); this(rng, domain, bi.move()); }
95 
96     this(PrivateKey pkey) { m_priv = cast(ECPrivateKey) pkey; }
97 
98     mixin Embed!(m_priv, m_owned);
99 
100 	bool m_owned;
101     ECPrivateKey m_priv;
102 
103 }
104 
105 /**
106 * ECDH operation
107 */
108 final class ECDHKAOperation : KeyAgreement
109 {
110 public:
111     this(in PrivateKey pkey) {
112         this(cast(ECPrivateKey) pkey);
113     }
114 
115     this(in ECDHPrivateKey pkey) {
116         this(pkey.m_priv);
117     }
118 
119     this(in ECPrivateKey key) 
120     {
121         m_priv = key;
122         m_curve = &m_priv.domain().getCurve();
123         m_cofactor = &m_priv.domain().getCofactor();
124         auto order_ = &m_priv.domain().getOrder();
125         auto private_value = &m_priv.privateValue();
126         m_l_times_priv = inverseMod(m_cofactor, order_) * private_value;
127     }
128 
129     override SecureVector!ubyte agree(const(ubyte)* w, size_t w_len)
130     {
131         PointGFp point = OS2ECP(w, w_len, *m_curve);
132 		auto tmp = point * m_cofactor;
133         PointGFp S = tmp * &m_l_times_priv;
134 
135         assert(S.onTheCurve(), "ECDH agreed value was on the curve");
136         
137         return BigInt.encode1363(S.getAffineX(),
138                                   m_curve.getP().bytes());
139     }
140 private:
141     const ECPrivateKey m_priv;
142     const CurveGFp* m_curve;
143     const BigInt* m_cofactor;
144     BigInt m_l_times_priv;
145 }
146 
147 static if (BOTAN_TEST):
148 
149 import botan.test;
150 import botan.pubkey.pubkey;
151 import botan.cert.x509.x509self;
152 import botan.asn1.der_enc;
153 import botan.rng.auto_rng;
154 import core.atomic : atomicOp;
155 shared(size_t) total_tests;
156 
157 size_t testEcdhNormalDerivation(RandomNumberGenerator rng)
158 {
159     size_t fails = 0;
160     ECGroup dom_pars = ECGroup(OID("1.3.132.0.8"));
161 
162 
163     auto private_a = ECDHPrivateKey(rng, dom_pars);
164     
165     auto private_b = ECDHPrivateKey(rng, dom_pars); //public_a.getCurve()
166     
167     auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)");
168     auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)");
169     
170     SymmetricKey alice_key = ka.deriveKey(32, private_b.publicValue());
171     SymmetricKey bob_key = kb.deriveKey(32, private_a.publicValue());
172     // 1 test
173     if (alice_key != bob_key)
174     {
175         logError("The two keys didn't match!");
176         logDebug("Alice's key was: " ~ alice_key.toString());
177         logDebug("Bob's key was: " ~ bob_key.toString());
178         atomicOp!"+="(total_tests, cast(size_t)1);
179         ++fails;
180     }
181 
182     return fails;
183 }
184 
185 size_t testEcdhSomeDp(RandomNumberGenerator rng)
186 {
187     size_t fails = 0;
188     
189     Vector!string oids;
190     oids.pushBack("1.2.840.10045.3.1.7");
191     oids.pushBack("1.3.132.0.8");
192     oids.pushBack("1.2.840.10045.3.1.1");
193     // 3 tests
194     foreach (oid_str; oids[])
195     {
196         OID oid = OID(oid_str);
197         ECGroup dom_pars = ECGroup(oid);
198         
199         auto private_a = ECDHPrivateKey(rng, dom_pars);
200         auto private_b = ECDHPrivateKey(rng, dom_pars);
201         
202         auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)");
203         auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)");
204         
205         SymmetricKey alice_key = ka.deriveKey(32, private_b.publicValue());
206         SymmetricKey bob_key = kb.deriveKey(32, private_a.publicValue());
207         
208         mixin( CHECK_MESSAGE( `alice_key == bob_key`, "different keys - Alice s key was: ` ~ alice_key.toString() ~ `, Bob's key was: ` ~ bob_key.toString() ~ `" ) );
209     }
210     
211     return fails;
212 }
213 
214 size_t testEcdhDerDerivation(RandomNumberGenerator rng)
215 {
216     size_t fails = 0;
217     
218     Vector!string oids;
219     oids.pushBack("1.2.840.10045.3.1.7");
220     oids.pushBack("1.3.132.0.8");
221     oids.pushBack("1.2.840.10045.3.1.1");
222     // 3 tests
223     foreach (oid_str; oids[])
224     {
225         OID oid = OID(oid_str);
226         ECGroup dom_pars = ECGroup(oid);
227         
228         auto private_a = ECDHPrivateKey(rng, dom_pars);
229         auto private_b = ECDHPrivateKey(rng, dom_pars);
230         
231         Vector!ubyte key_a = private_a.publicValue();
232         Vector!ubyte key_b = private_b.publicValue();
233         
234         auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)");
235         auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)");
236         
237         SymmetricKey alice_key = ka.deriveKey(32, key_b);
238         SymmetricKey bob_key = kb.deriveKey(32, key_a);
239         
240         mixin( CHECK_MESSAGE( `alice_key == bob_key`, "different keys - Alice's key was: ` ~ alice_key.toString() ~ `, Bob's key was: ` ~ bob_key.toString() ~ `" ) );
241         
242     }
243     
244     return fails;
245 }
246 
247 static if (BOTAN_HAS_TESTS && !SKIP_ECDH_TEST) unittest
248 {
249     logDebug("Testing ecdh.d ...");
250     size_t fails = 0;
251     
252 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
253     
254     fails += testEcdhNormalDerivation(*rng);
255     fails += testEcdhSomeDp(*rng);
256     fails += testEcdhDerDerivation(*rng);
257     
258     testReport("ECDH", total_tests, fails);
259 }