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 vz ~= m_ka.deriveKey(0, v).bitsOf(); 140 141 const size_t K_LENGTH = C.length + m_mac_keylen; 142 OctetString K = m_kdf.deriveKey(K_LENGTH, vz); 143 if (K.length != K_LENGTH) 144 throw new EncodingError("DLIES: KDF did not provide sufficient output"); 145 Unique!MessageAuthenticationCode mac = m_mac.clone(); 146 mac.setKey(K.ptr, m_mac_keylen); 147 mac.update(C); 148 foreach (size_t j; 0 .. 8) 149 mac.update(0); 150 SecureVector!ubyte T2 = mac.finished(); 151 if (T != T2) 152 throw new DecodingError("DLIES: message authentication failed"); 153 154 xorBuf(C, K.ptr + m_mac_keylen, C.length); 155 156 return C; 157 } 158 159 private: 160 Vector!ubyte m_my_key; 161 162 Unique!PKKeyAgreement m_ka; 163 Unique!KDF m_kdf; 164 Unique!MessageAuthenticationCode m_mac; 165 size_t m_mac_keylen; 166 } 167 168 169 static if (BOTAN_TEST): 170 import botan.test; 171 import botan.utils.parsing; 172 import botan.pubkey.test; 173 import botan.codec.hex; 174 import botan.rng.auto_rng; 175 import botan.pubkey.pubkey; 176 import botan.libstate.lookup; 177 import botan.pubkey.algo.dh; 178 import std.conv : to; 179 import core.atomic; 180 import memutils.hashmap; 181 182 shared size_t total_tests; 183 184 size_t dliesKat(string p, 185 string g, 186 string x1, 187 string x2, 188 string msg, 189 string ciphertext) 190 { 191 atomicOp!"+="(total_tests, 1); 192 Unique!AutoSeededRNG rng = new AutoSeededRNG; 193 194 BigInt p_bn = BigInt(p); 195 BigInt g_bn = BigInt(g); 196 BigInt x1_bn = BigInt(x1); 197 BigInt x2_bn = BigInt(x2); 198 199 //logTrace("p_bn: ", p_bn.toString()); 200 //logTrace("g_bn: ", g_bn.toString()); 201 //logTrace("x1_bn: ", x1_bn.toString()); 202 //logTrace("x2_bn: ", x2_bn.toString()); 203 204 DLGroup domain = DLGroup(p_bn, g_bn); 205 206 auto from = DHPrivateKey(*rng, domain.dup, x1_bn.move()); 207 auto to = DHPrivateKey(*rng, domain.dup, x2_bn.move()); 208 209 const string opt_str = "KDF2(SHA-1)/HMAC(SHA-1)/16"; 210 211 Vector!string options = splitter(opt_str, '/'); 212 213 if (options.length != 3) 214 throw new Exception("DLIES needs three options: " ~ opt_str); 215 216 const size_t mac_key_len = .to!uint(options[2]); 217 218 auto e = scoped!DLIESEncryptor(from, getKdf(options[0]), retrieveMac(options[1]).clone(), mac_key_len); 219 220 auto d = scoped!DLIESDecryptor(to, getKdf(options[0]), retrieveMac(options[1]).clone(), mac_key_len); 221 222 e.setOtherKey(to.publicValue()); 223 224 return validateEncryption(e, d, "DLIES", msg, "", ciphertext); 225 } 226 227 static if (BOTAN_HAS_TESTS && !SKIP_DLIES_TEST) unittest 228 { 229 logDebug("Testing dlies.d ..."); 230 size_t fails = 0; 231 232 File dlies = File("../test_data/pubkey/dlies.vec", "r"); 233 234 fails += runTestsBb(dlies, "DLIES Encryption", "Ciphertext", true, 235 (ref HashMap!(string, string) m) { 236 return dliesKat(m["P"], m["G"], m["X1"], m["X2"], m["Msg"], m["Ciphertext"]); 237 }); 238 239 testReport("dlies", total_tests, fails); 240 }