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