1 /** 2 * DLIES (Discrete Logarithm/Elliptic Curve Integrated Encryption Scheme): 3 * Essentially the "DHAES" variant of ElGamal encryption. 4 * 5 * Copyright: 6 * (C) 1999-2007 Jack Lloyd 7 * (C) 2014-2015 Etienne Cimon 8 * 9 * License: 10 * Botan is released under the Simplified BSD License (see LICENSE.md) 11 */ 12 module botan.pubkey.algo.dlies; 13 14 import botan.constants; 15 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_DLIES): 16 17 public import botan.pubkey.pubkey; 18 import botan.mac.mac; 19 import botan.kdf.kdf; 20 import botan.utils.xor_buf; 21 import botan.utils.mem_ops; 22 23 /** 24 * DLIES Encryption 25 */ 26 class DLIESEncryptor : PKEncryptor 27 { 28 public: 29 /* 30 * DLIESEncryptor Constructor 31 */ 32 this(in PKKeyAgreementKey key, KDF kdf_obj, MessageAuthenticationCode mac_obj, size_t mac_keylen = 20) 33 { 34 m_ka = new PKKeyAgreement(key, "Raw"); 35 m_kdf = kdf_obj; 36 m_mac = mac_obj; 37 m_mac_keylen = mac_keylen; 38 m_my_key = key.publicValue(); 39 } 40 41 /* 42 * Set the other parties public key 43 */ 44 void setOtherKey()(auto const ref Vector!ubyte ok) 45 { 46 m_other_key = ok.dup; 47 } 48 protected: 49 /* 50 * DLIES Encryption 51 */ 52 Vector!ubyte enc(const(ubyte)* input, size_t length, RandomNumberGenerator rng) const 53 { 54 if (length > maximumInputSize()) 55 throw new InvalidArgument("DLIES: Plaintext too large"); 56 if (m_other_key.empty) 57 throw new InvalidState("DLIES: The other key was never set"); 58 59 SecureVector!ubyte output = SecureVector!ubyte(m_my_key.length + length + m_mac.outputLength); 60 bufferInsert(output, 0, m_my_key); 61 bufferInsert(output, m_my_key.length, input, length); 62 63 SecureVector!ubyte vz = SecureVector!ubyte(m_my_key.ptr[0 .. m_my_key.length]); 64 vz ~= m_ka.deriveKey(0, m_other_key).bitsOf(); 65 66 const size_t K_LENGTH = length + m_mac_keylen; 67 OctetString K = m_kdf.deriveKey(K_LENGTH, vz); 68 69 if (K.length != K_LENGTH) 70 throw new EncodingError("DLIES: KDF did not provide sufficient output"); 71 ubyte* C = &output[m_my_key.length]; 72 73 xorBuf(C, K.ptr + m_mac_keylen, length); 74 Unique!MessageAuthenticationCode mac = m_mac.clone(); 75 mac.setKey(K.ptr, m_mac_keylen); 76 77 mac.update(C, length); 78 foreach (size_t j; 0 .. 8) 79 mac.update(0); 80 81 mac.flushInto(C + length); 82 83 return unlock(output); 84 } 85 86 /* 87 * Return the max size, in bytes, of a message 88 */ 89 size_t maximumInputSize() const 90 { 91 return 32; 92 } 93 94 private: 95 Vector!ubyte m_other_key, m_my_key; 96 97 Unique!PKKeyAgreement m_ka; 98 Unique!KDF m_kdf; 99 Unique!MessageAuthenticationCode m_mac; 100 size_t m_mac_keylen; 101 } 102 103 /** 104 * DLIES Decryption 105 */ 106 class DLIESDecryptor : PKDecryptor 107 { 108 public: 109 /* 110 * DLIESDecryptor Constructor 111 */ 112 this(in PKKeyAgreementKey key, KDF kdf_obj, MessageAuthenticationCode mac_obj, size_t mac_key_len = 20) 113 { 114 m_ka = new PKKeyAgreement(key, "Raw"); 115 m_kdf = kdf_obj; 116 m_mac = mac_obj; 117 m_mac_keylen = mac_key_len; 118 m_my_key = key.publicValue(); 119 } 120 121 protected: 122 /* 123 * DLIES Decryption 124 */ 125 SecureVector!ubyte dec(const(ubyte)* msg, size_t length) const 126 { 127 if (length < m_my_key.length + m_mac.outputLength) 128 throw new DecodingError("DLIES decryption: ciphertext is too short"); 129 130 const size_t CIPHER_LEN = length - m_my_key.length - m_mac.outputLength; 131 132 Vector!ubyte v = Vector!ubyte(msg[0 .. m_my_key.length]); 133 134 SecureVector!ubyte C = SecureVector!ubyte(msg[m_my_key.length .. m_my_key.length + CIPHER_LEN]); 135 136 SecureVector!ubyte T = SecureVector!ubyte(msg[m_my_key.length + CIPHER_LEN .. m_my_key.length + CIPHER_LEN + m_mac.outputLength]); 137 138 SecureVector!ubyte vz = SecureVector!ubyte(msg[0 .. m_my_key.length]); 139 140 auto derived_key = m_ka.deriveKey(0, v); 141 vz ~= derived_key.bitsOf(); 142 143 const size_t K_LENGTH = C.length + m_mac_keylen; 144 OctetString K = m_kdf.deriveKey(K_LENGTH, vz); 145 if (K.length != K_LENGTH) 146 throw new EncodingError("DLIES: KDF did not provide sufficient output"); 147 Unique!MessageAuthenticationCode mac = m_mac.clone(); 148 mac.setKey(K.ptr, m_mac_keylen); 149 mac.update(C); 150 foreach (size_t j; 0 .. 8) 151 mac.update(0); 152 SecureVector!ubyte T2 = mac.finished(); 153 if (T != T2) 154 throw new DecodingError("DLIES: message authentication failed"); 155 156 xorBuf(C, K.ptr + m_mac_keylen, C.length); 157 158 return C; 159 } 160 161 private: 162 Vector!ubyte m_my_key; 163 164 Unique!PKKeyAgreement m_ka; 165 Unique!KDF m_kdf; 166 Unique!MessageAuthenticationCode m_mac; 167 size_t m_mac_keylen; 168 } 169 170 171 static if (BOTAN_TEST): 172 import botan.test; 173 import botan.utils.parsing; 174 import botan.pubkey.test; 175 import botan.codec.hex; 176 import botan.rng.auto_rng; 177 import botan.pubkey.pubkey; 178 import botan.libstate.lookup; 179 import botan.pubkey.algo.dh; 180 import std.conv : to; 181 import core.atomic; 182 import memutils.hashmap; 183 184 shared size_t total_tests; 185 186 size_t dliesKat(string p, 187 string g, 188 string x1, 189 string x2, 190 string msg, 191 string ciphertext) 192 { 193 atomicOp!"+="(total_tests, 1); 194 Unique!AutoSeededRNG rng = new AutoSeededRNG; 195 196 BigInt p_bn = BigInt(p); 197 BigInt g_bn = BigInt(g); 198 BigInt x1_bn = BigInt(x1); 199 BigInt x2_bn = BigInt(x2); 200 201 //logTrace("p_bn: ", p_bn.toString()); 202 //logTrace("g_bn: ", g_bn.toString()); 203 //logTrace("x1_bn: ", x1_bn.toString()); 204 //logTrace("x2_bn: ", x2_bn.toString()); 205 206 DLGroup domain = DLGroup(p_bn, g_bn); 207 208 auto from = DHPrivateKey(*rng, domain.dup, x1_bn.move()); 209 auto to = DHPrivateKey(*rng, domain.dup, x2_bn.move()); 210 211 const string opt_str = "KDF2(SHA-1)/HMAC(SHA-1)/16"; 212 213 Vector!string options = splitter(opt_str, '/'); 214 215 if (options.length != 3) 216 throw new Exception("DLIES needs three options: " ~ opt_str); 217 218 const size_t mac_key_len = .to!uint(options[2]); 219 220 auto e = scoped!DLIESEncryptor(from, getKdf(options[0]), retrieveMac(options[1]).clone(), mac_key_len); 221 222 auto d = scoped!DLIESDecryptor(to, getKdf(options[0]), retrieveMac(options[1]).clone(), mac_key_len); 223 224 e.setOtherKey(to.publicValue()); 225 226 return validateEncryption(e, d, "DLIES", msg, "", ciphertext); 227 } 228 229 static if (BOTAN_HAS_TESTS && !SKIP_DLIES_TEST) unittest 230 { 231 logDebug("Testing dlies.d ..."); 232 size_t fails = 0; 233 234 File dlies = File("test_data/pubkey/dlies.vec", "r"); 235 236 fails += runTestsBb(dlies, "DLIES Encryption", "Ciphertext", true, 237 (ref HashMap!(string, string) m) { 238 return dliesKat(m["P"], m["G"], m["X1"], m["X2"], m["Msg"], m["Ciphertext"]); 239 }); 240 241 testReport("dlies", total_tests, fails); 242 }