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 }