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 }