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 }