1 /**
2 * EAX Mode
3 * 
4 * Copyright:
5 * (C) 1999-2007,2013 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.modes.aead.eax;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_AEAD_EAX):
15 
16 import botan.modes.aead.aead;
17 import botan.block.block_cipher;
18 import botan.stream.stream_cipher;
19 import botan.mac.mac;
20 import botan.mac.cmac;
21 import botan.stream.ctr;
22 import botan.utils.parsing;
23 import botan.utils.xor_buf;
24 import botan.utils.mem_ops;
25 import std.algorithm;
26 
27 /**
28 * EAX base class
29 */
30 abstract class EAXMode : AEADMode, Transformation
31 {
32 public:
33     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len)
34     {
35         if (!validNonceLength(nonce_len))
36             throw new InvalidIVLength(name, nonce_len);
37         
38         m_nonce_mac = eaxPrf(0, this.blockSize(), *m_cmac, nonce, nonce_len);
39         
40         m_ctr.setIv(m_nonce_mac.ptr, m_nonce_mac.length);
41         
42         for (size_t i = 0; i != this.blockSize() - 1; ++i)
43             m_cmac.update(0);
44         m_cmac.update(2);
45         
46         return SecureVector!ubyte();
47     }
48 
49 
50     override void setAssociatedData(const(ubyte)* ad, size_t length)
51     {
52 		m_ad_mac = eaxPrf(1, this.blockSize(), *m_cmac, ad, length);
53     }
54 
55     override @property string name() const
56     {
57         return (m_cipher.name ~ "/EAX");
58     }
59 
60     override size_t updateGranularity() const
61     {
62         return 8 * m_cipher.parallelBytes();
63     }
64 
65     override KeyLengthSpecification keySpec() const
66     {
67         return m_cipher.keySpec();
68     }
69 
70     // EAX supports arbitrary nonce lengths
71     override bool validNonceLength(size_t) const { return true; }
72 
73     override size_t tagSize() const { return m_tag_size; }
74 
75     override void clear()
76     {
77         m_cipher.clear();
78         m_ctr.clear();
79         m_cmac.clear();
80         zeroise(m_ad_mac);
81         zeroise(m_nonce_mac);
82     }
83 
84     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
85 
86 protected:
87     final override void keySchedule(const(ubyte)* key, size_t length)
88     {
89         /*
90         * These could share the key schedule, which is one nice part of EAX,
91         * but it's much easier to ignore that here...
92         */
93         m_ctr.setKey(key, length);
94         m_cmac.setKey(key, length);
95         
96         m_ad_mac = eaxPrf(1, this.blockSize(), *m_cmac, null, 0);
97     }
98 
99     /**
100     * Params:
101     *  cipher = the cipher to use
102     *  tag_size = is how big the auth tag will be
103     */
104     this(BlockCipher cipher, size_t tag_size) 
105     {
106         m_tag_size = tag_size ? tag_size : cipher.blockSize();
107         m_cipher = cipher;
108         m_ctr = new CTRBE(m_cipher.clone());
109         m_cmac = new CMAC(m_cipher.clone());
110         if (m_tag_size < 8 || m_tag_size > m_cmac.outputLength)
111             throw new InvalidArgument(name ~ ": Bad tag size " ~ to!string(tag_size));
112     }
113 
114     final size_t blockSize() const { return m_cipher.blockSize(); }
115 
116     size_t m_tag_size;
117 
118     Unique!BlockCipher m_cipher;
119     Unique!StreamCipher m_ctr;
120     Unique!MessageAuthenticationCode m_cmac;
121 
122     SecureVector!ubyte m_ad_mac;
123 
124     SecureVector!ubyte m_nonce_mac;
125 }
126 
127 /**
128 * EAX Encryption
129 */
130 final class EAXEncryption : EAXMode, Transformation
131 {
132 public:
133     /**
134     * Params:
135     *  cipher = a 128-bit block cipher
136     *  tag_size = is how big the auth tag will be
137     */
138     this(BlockCipher cipher, size_t tag_size = 0) 
139     {
140         super(cipher, tag_size);
141     }
142 
143     override size_t outputLength(size_t input_length) const
144     { return input_length + tagSize(); }
145 
146     override size_t minimumFinalSize() const { return 0; }
147 
148     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
149     {
150         assert(buffer.length >= offset, "Offset is sane");
151         const size_t sz = buffer.length - offset;
152         ubyte* buf = buffer.ptr + offset;
153         
154         m_ctr.cipher(buf, buf, sz);
155         m_cmac.update(buf, sz);
156     }
157 
158     override void finish(ref SecureVector!ubyte buffer, size_t offset)
159     {
160         update(buffer, offset);
161         
162         SecureVector!ubyte data_mac = m_cmac.finished();
163         xorBuf(data_mac, m_nonce_mac, data_mac.length);
164         xorBuf(data_mac, m_ad_mac, data_mac.length);
165 
166         buffer ~= data_mac[0 .. tagSize()];
167     }
168 
169     // Interface fallthrough
170     override string provider() const { return "core"; }
171     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
172     override size_t updateGranularity() const { return super.updateGranularity(); }
173     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
174     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
175     override @property string name() const { return super.name; }
176     override void clear() { return super.clear(); }
177 }
178 
179 /**
180 * EAX Decryption
181 */
182 final class EAXDecryption : EAXMode, Transformation
183 {
184 public:
185     /**
186     * Params:
187     *  cipher = a 128-bit block cipher
188     *  tag_size = is how big the auth tag will be
189     */
190     this(BlockCipher cipher, size_t tag_size = 0) 
191     {
192         super(cipher, tag_size); 
193     }
194 
195     override size_t outputLength(size_t input_length) const
196     {
197         assert(input_length > tagSize(), "Sufficient input");
198         return input_length - tagSize();
199     }
200 
201     override size_t minimumFinalSize() const { return tagSize(); }
202 
203     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
204     {
205         assert(buffer.length >= offset, "Offset is sane");
206         const size_t sz = buffer.length - offset;
207         ubyte* buf = buffer.ptr + offset;
208         
209         m_cmac.update(buf, sz);
210         m_ctr.cipher(buf, buf, sz);
211     }
212 
213     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
214     {
215         assert(buffer.length >= offset, "Offset is sane");
216         const size_t sz = buffer.length - offset;
217         ubyte* buf = buffer.ptr + offset;
218         
219         assert(sz >= tagSize(), "Have the tag as part of final input");
220         
221         const size_t remaining = sz - tagSize();
222         
223         if (remaining)
224         {
225             m_cmac.update(buf, remaining);
226             m_ctr.cipher(buf, buf, remaining);
227         }
228         
229         const(ubyte)* included_tag = &buf[remaining];
230         
231         SecureVector!ubyte mac = m_cmac.finished();
232         mac ^= m_nonce_mac;
233         mac ^= m_ad_mac;
234         
235         if (!sameMem(mac.ptr, included_tag, tagSize()))
236             throw new IntegrityFailure("EAX tag check failed");
237         
238         buffer.resize(offset + remaining);
239     }
240 
241     // Interface fallthrough
242     override string provider() const { return "core"; }
243     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
244     override size_t updateGranularity() const { return super.updateGranularity(); }
245     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
246     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
247     override @property string name() const { return super.name; }
248     override void clear() { return super.clear(); }
249 }
250 
251 
252 /*
253 * EAX MAC-based PRF
254 */
255 SecureVector!ubyte eaxPrf(ubyte tag, size_t block_size,
256                           MessageAuthenticationCode mac,
257                           const(ubyte)* input,
258                           size_t length)
259 {
260     foreach (size_t i; 0 .. (block_size - 1))
261         mac.update(0);
262     mac.update(tag);
263     mac.update(input, length);
264     return mac.finished();
265 }