1 /**
2 * CCM Mode
3 * 
4 * Copyright:
5 * (C) 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.ccm;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_AEAD_CCM):
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.utils.parsing;
21 import botan.utils.xor_buf;
22 import botan.utils.get_byte;
23 import std.conv : to;
24 import std.algorithm;
25 import botan.codec.hex;
26 
27 /**
28 * Base class for CCM encryption and decryption
29 * @see RFC 3610
30 */
31 abstract class CCMMode : AEADMode, Transformation
32 {
33 public:
34     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len)
35     {
36         if (!validNonceLength(nonce_len))
37             throw new InvalidIVLength(name, nonce_len);
38         
39         m_nonce[] = nonce[0 .. nonce_len];
40         m_msg_buf.clear();
41         
42         return SecureVector!ubyte();
43     }
44 
45     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
46     {
47         assert(buffer.length >= offset, "Offset is sane");
48         const size_t sz = buffer.length - offset;
49         ubyte* buf = buffer.ptr + offset;
50         
51         m_msg_buf ~= buf[0 .. sz];
52         logDebug("m_msg_buf: ", hexEncode(m_msg_buf.ptr, m_msg_buf.length));
53         buffer.length = offset; // truncate msg
54     }
55 
56     override void setAssociatedData(const(ubyte)* ad, size_t length)
57     {
58         m_ad_buf.clear();
59         if (length)
60         {
61             // FIXME: support larger AD using length encoding rules
62             assert(length < (0xFFFF - 0xFF), "Supported CCM AD length");
63             
64             m_ad_buf.pushBack(get_byte(0, cast(ushort) length));
65             m_ad_buf.pushBack(get_byte(1, cast(ushort) length));
66             m_ad_buf ~= ad[0 .. length];
67             while (m_ad_buf.length % BS)
68                 m_ad_buf.pushBack(cast(ubyte)0); // pad with zeros to full block size
69         }
70     }
71 
72     override @property string name() const
73     {
74         return (m_cipher.name ~ "/CCM(" ~ to!string(tagSize()) ~ "," ~ to!string(L())) ~ ")";
75     }
76 
77     size_t updateGranularity() const
78     {
79         /*
80         This value does not particularly matter as regardless update
81         buffers all input, so in theory this could be 1. However as for instance
82         TransformationFilter creates updateGranularity() ubyte buffers, use a
83         somewhat large size to avoid bouncing on a tiny buffer.
84         */
85         return m_cipher.parallelBytes();
86     }
87 
88 
89     override KeyLengthSpecification keySpec() const
90     {
91         return m_cipher.keySpec();
92     }
93 
94     override bool validNonceLength(size_t n) const
95     {
96         return (n == (15-L()));
97     }
98 
99     override size_t defaultNonceLength() const
100     {
101         return (15-L());
102     }
103 
104     override void clear()
105     {
106         m_cipher.clear();
107         m_msg_buf.clear();
108         m_ad_buf.clear();
109     }
110 
111     override size_t tagSize() const { return m_tag_size; }
112 
113 protected:
114     __gshared immutable size_t BS = 16; // intrinsic to CCM definition
115 
116     /*
117     * CCMMode Constructor
118     */
119     this(BlockCipher cipher, size_t tag_size, size_t L)
120     { 
121         m_tag_size = tag_size;
122         m_L = L;
123         m_cipher = cipher;
124         if (m_cipher.blockSize() != BS)
125             throw new InvalidArgument(m_cipher.name ~ " cannot be used with CCM mode");
126         
127         if (L < 2 || L > 8)
128             throw new InvalidArgument("Invalid CCM L value " ~ to!string(L));
129         
130         if (tag_size < 4 || tag_size > 16 || tag_size % 2 != 0)
131             throw new InvalidArgument("invalid CCM tag length " ~ to!string(tag_size));
132     }
133 
134     final size_t L() const { return m_L; }
135 
136     final BlockCipher cipher() const { return cast()*m_cipher; }
137 
138     final void encodeLength(size_t len, ubyte* output)
139     {
140         const size_t len_bytes = L();
141         
142         assert(len_bytes < (size_t).sizeof, "Length field fits");
143         
144         foreach (size_t i; 0 .. len_bytes)
145             output[len_bytes-1-i] = get_byte((size_t).sizeof-1-i, len);
146         
147         assert((len >> (len_bytes*8)) == 0, "Message length fits in field");
148     }
149 
150     final void inc(ref SecureVector!ubyte C)
151     {
152         for (size_t i = 0; i != C.length; ++i)
153             if (++(C.ptr[C.length-i-1]))
154                 break;
155     }
156 
157     final ref const(SecureVector!ubyte) adBuf() const { return m_ad_buf; }
158 
159     final ref const(SecureVector!ubyte) msgBuf() const { return m_msg_buf; }
160 
161     final SecureVector!ubyte formatB0(size_t sz)
162     {
163         SecureVector!ubyte B0 = SecureVector!ubyte(BS);
164         
165         const ubyte b_flags = cast(ubyte) ((m_ad_buf.length ? 64 : 0) + (((tagSize()/2)-1) << 3) + (L()-1));
166         
167         B0[0] = b_flags;
168         assert(B0.length >= m_nonce.length);
169         copyMem(&B0[1], m_nonce.ptr, m_nonce.length);
170         encodeLength(sz, &B0[m_nonce.length+1]);
171         
172         return B0.move;
173     }
174 
175     final SecureVector!ubyte formatC0()
176     {
177         SecureVector!ubyte C = SecureVector!ubyte(BS);
178         
179         const ubyte a_flags = cast(ubyte)(L()-1);
180         
181         C[0] = a_flags;
182         assert(C.length >= m_nonce.length + 1);
183         copyMem(&C[1], m_nonce.ptr, m_nonce.length);
184         
185         return C.move;
186     }
187 
188     final override void keySchedule(const(ubyte)* key, size_t length)
189     {
190         m_cipher.setKey(key, length);
191     }
192 
193 protected:
194     const size_t m_tag_size;
195     const size_t m_L;
196 
197     Unique!BlockCipher m_cipher;
198     SecureVector!ubyte m_nonce, m_msg_buf, m_ad_buf;
199 }
200 
201 /**
202 * CCM Encryption
203 */
204 final class CCMEncryption : CCMMode, Transformation
205 {
206 public:
207     /**
208     * Params:
209     *  cipher = a 128-bit block cipher
210     *  tag_size = is how big the auth tag will be (even values
211     *                      between 4 and 16 are accepted)
212     *  L = length of L parameter. The total message length
213     *              must be less than 2**L bytes, and the nonce is 15-L bytes.
214     */
215     this(BlockCipher cipher, size_t tag_size = 16, size_t L = 3) 
216     {
217         super(cipher, tag_size, L);
218     }
219 
220     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
221     {
222         import std.algorithm : max;
223         assert(buffer.length >= offset, "Offset is sane");
224         buffer.resize(max(buffer.length, offset + msgBuf().length));
225         buffer[offset .. offset + msgBuf().length] = msgBuf()[0 .. msgBuf().length];
226         const size_t sz = buffer.length - offset;
227         ubyte* buf = buffer.ptr + offset;
228         
229         const SecureVector!ubyte* ad = &adBuf();
230         assert(ad.length % BS == 0, "AD is block size multiple");
231         
232         BlockCipher E = cipher();
233         
234         SecureVector!ubyte T = SecureVector!ubyte(BS);
235         E.encrypt(formatB0(sz), T);
236         
237         for (size_t i = 0; i != ad.length; i += BS)
238         {
239             xorBuf(T.ptr, &(*ad)[i], BS);
240             E.encrypt(T);
241         }
242 
243         SecureVector!ubyte C = formatC0();
244         SecureVector!ubyte S0 = SecureVector!ubyte(BS);
245         E.encrypt(C, S0);
246         inc(C);
247         SecureVector!ubyte X = SecureVector!ubyte(BS);
248         
249         const(ubyte)* buf_end = &buf[sz];
250         
251         while (buf != buf_end)
252         {
253             const size_t to_proc = std.algorithm.min(BS, buf_end - buf);
254             
255             xorBuf(T.ptr, buf, to_proc);
256             E.encrypt(T);            
257             E.encrypt(C, X);
258             xorBuf(buf, X.ptr, to_proc);
259             inc(C);
260             buf += to_proc;
261         }
262         T ^= S0;
263         
264         buffer ~= T.ptr[0 .. tagSize()];
265     }
266 
267     override size_t outputLength(size_t input_length) const
268     { return input_length + tagSize(); }
269 
270     override size_t minimumFinalSize() const { return 0; }
271 
272     // Interface fallthrough
273     override string provider() const { return "core"; }
274     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
275     override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); }
276     override size_t updateGranularity() const { return super.updateGranularity(); }
277     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
278     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
279     override @property string name() const { return super.name; }
280     override void clear() { return super.clear(); }
281 
282 }
283 
284 /**
285 * CCM Decryption
286 */
287 final class CCMDecryption : CCMMode, Transformation
288 {
289 public:
290     /**
291     * Params:
292     *  cipher = a 128-bit block cipher
293     *  tag_size = is how big the auth tag will be (even values
294     *                      between 4 and 16 are accepted)
295     *  L = length of L parameter. The total message length
296     *              must be less than 2**L bytes, and the nonce is 15-L bytes.
297     */
298     this(BlockCipher cipher, size_t tag_size = 16, size_t L = 3) 
299     {
300         super(cipher, tag_size, L);
301     }
302 
303     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
304     {
305         import std.algorithm : max;
306         assert(buffer.length >= offset, "Offset is sane");
307         buffer.resize(max(buffer.length, offset + msgBuf().length));
308         buffer.ptr[offset .. offset + msgBuf().length] = msgBuf().ptr[0 .. msgBuf().length];
309         
310         const size_t sz = buffer.length - offset;
311         ubyte* buf = buffer.ptr + offset;
312         
313         assert(sz >= tagSize(), "We have the tag");
314         
315         const SecureVector!ubyte* ad = &adBuf();
316         assert(ad.length % BS == 0, "AD is block size multiple");
317         
318         BlockCipher E = cipher();
319         
320         SecureVector!ubyte T = SecureVector!ubyte(BS);
321         E.encrypt(formatB0(sz - tagSize()), T);
322         
323         for (size_t i = 0; i != ad.length; i += BS)
324         {
325             xorBuf(T.ptr, &(*ad)[i], BS);
326             E.encrypt(T);
327         }
328         
329         SecureVector!ubyte C = formatC0();
330         
331         SecureVector!ubyte S0 = SecureVector!ubyte(BS);
332         E.encrypt(C, S0);
333         inc(C);
334         
335         SecureVector!ubyte X = SecureVector!ubyte(BS);
336         
337         const(ubyte)* buf_end = &buf[sz - tagSize()];
338         
339         while (buf < buf_end)
340         {
341             const size_t to_proc = std.algorithm.min(BS, buf_end - buf);
342             
343             E.encrypt(C, X);
344             xorBuf(buf, X.ptr, to_proc);
345             inc(C);
346             
347             xorBuf(T.ptr, buf, to_proc);
348             E.encrypt(T);
349             
350             buf += to_proc;
351         }
352         
353         T ^= S0;
354         
355         if (!sameMem(T.ptr, buf_end, tagSize()))
356             throw new IntegrityFailure("CCM tag check failed");
357         
358         buffer.resize(buffer.length - tagSize());
359     }
360 
361     override size_t outputLength(size_t input_length) const
362     {
363         assert(input_length > tagSize(), "Sufficient input");
364         return input_length - tagSize();
365     }
366 
367     override size_t minimumFinalSize() const { return tagSize(); }
368 
369     // Interface fallthrough
370     override string provider() const { return "core"; }
371     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
372     override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); }
373     override size_t updateGranularity() const { return super.updateGranularity(); }
374     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
375     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
376     override @property string name() const { return super.name; }
377     override void clear() { return super.clear(); }
378 }