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 }