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 }