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_curve = &key.domain().getCurve(); 122 m_cofactor = &key.domain().getCofactor(); 123 m_l_times_priv = inverseMod(*m_cofactor, key.domain().getOrder()) * key.privateValue(); 124 } 125 126 override SecureVector!ubyte agree(const(ubyte)* w, size_t w_len) 127 { 128 PointGFp point = OS2ECP(w, w_len, *m_curve); 129 auto tmp = point * (*m_cofactor); 130 PointGFp S = tmp * m_l_times_priv; 131 132 assert(S.onTheCurve(), "ECDH agreed value was on the curve"); 133 134 return BigInt.encode1363(S.getAffineX(), 135 m_curve.getP().bytes()); 136 } 137 private: 138 const CurveGFp* m_curve; 139 const BigInt* m_cofactor; 140 BigInt m_l_times_priv; 141 } 142 143 static if (BOTAN_TEST): 144 145 import botan.test; 146 import botan.pubkey.pubkey; 147 import botan.cert.x509.x509self; 148 import botan.asn1.der_enc; 149 import botan.rng.auto_rng; 150 import core.atomic : atomicOp; 151 shared(size_t) total_tests; 152 153 size_t testEcdhNormalDerivation(RandomNumberGenerator rng) 154 { 155 size_t fails = 0; 156 ECGroup dom_pars = ECGroup(OID("1.3.132.0.8")); 157 158 159 auto private_a = ECDHPrivateKey(rng, dom_pars); 160 161 auto private_b = ECDHPrivateKey(rng, dom_pars); //public_a.getCurve() 162 163 auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)"); 164 auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)"); 165 166 SymmetricKey alice_key = ka.deriveKey(32, private_b.publicValue()); 167 SymmetricKey bob_key = kb.deriveKey(32, private_a.publicValue()); 168 // 1 test 169 if (alice_key != bob_key) 170 { 171 logError("The two keys didn't match!"); 172 logDebug("Alice's key was: " ~ alice_key.toString()); 173 logDebug("Bob's key was: " ~ bob_key.toString()); 174 atomicOp!"+="(total_tests, cast(size_t)1); 175 ++fails; 176 } 177 178 return fails; 179 } 180 181 size_t testEcdhSomeDp(RandomNumberGenerator rng) 182 { 183 size_t fails = 0; 184 185 Vector!string oids; 186 oids.pushBack("1.2.840.10045.3.1.7"); 187 oids.pushBack("1.3.132.0.8"); 188 oids.pushBack("1.2.840.10045.3.1.1"); 189 // 3 tests 190 foreach (oid_str; oids[]) 191 { 192 OID oid = OID(oid_str); 193 ECGroup dom_pars = ECGroup(oid); 194 195 auto private_a = ECDHPrivateKey(rng, dom_pars); 196 auto private_b = ECDHPrivateKey(rng, dom_pars); 197 198 auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)"); 199 auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)"); 200 201 SymmetricKey alice_key = ka.deriveKey(32, private_b.publicValue()); 202 SymmetricKey bob_key = kb.deriveKey(32, private_a.publicValue()); 203 204 mixin( CHECK_MESSAGE( `alice_key == bob_key`, "different keys - Alice s key was: ` ~ alice_key.toString() ~ `, Bob's key was: ` ~ bob_key.toString() ~ `" ) ); 205 } 206 207 return fails; 208 } 209 210 size_t testEcdhDerDerivation(RandomNumberGenerator rng) 211 { 212 size_t fails = 0; 213 214 Vector!string oids; 215 oids.pushBack("1.2.840.10045.3.1.7"); 216 oids.pushBack("1.3.132.0.8"); 217 oids.pushBack("1.2.840.10045.3.1.1"); 218 // 3 tests 219 foreach (oid_str; oids[]) 220 { 221 OID oid = OID(oid_str); 222 ECGroup dom_pars = ECGroup(oid); 223 224 auto private_a = ECDHPrivateKey(rng, dom_pars); 225 auto private_b = ECDHPrivateKey(rng, dom_pars); 226 227 Vector!ubyte key_a = private_a.publicValue(); 228 Vector!ubyte key_b = private_b.publicValue(); 229 230 auto ka = scoped!PKKeyAgreement(private_a, "KDF2(SHA-1)"); 231 auto kb = scoped!PKKeyAgreement(private_b, "KDF2(SHA-1)"); 232 233 SymmetricKey alice_key = ka.deriveKey(32, key_b); 234 SymmetricKey bob_key = kb.deriveKey(32, key_a); 235 236 mixin( CHECK_MESSAGE( `alice_key == bob_key`, "different keys - Alice's key was: ` ~ alice_key.toString() ~ `, Bob's key was: ` ~ bob_key.toString() ~ `" ) ); 237 238 } 239 240 return fails; 241 } 242 243 static if (BOTAN_HAS_TESTS && !SKIP_ECDH_TEST) unittest 244 { 245 logDebug("Testing ecdh.d ..."); 246 size_t fails = 0; 247 248 Unique!AutoSeededRNG rng = new AutoSeededRNG; 249 250 fails += testEcdhNormalDerivation(*rng); 251 fails += testEcdhSomeDp(*rng); 252 fails += testEcdhDerDerivation(*rng); 253 254 testReport("ECDH", total_tests, fails); 255 }