1 /**
2 * SIV 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.siv;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_AEAD_SIV):
15 import botan.modes.aead.aead;
16 import botan.block.block_cipher;
17 import botan.stream.stream_cipher;
18 import botan.mac.mac;
19 import botan.mac.cmac;
20 import botan.stream.ctr;
21 import botan.utils.parsing;
22 import botan.utils.xor_buf;
23 import std.algorithm;
24 
25 /**
26 * Base class for SIV encryption and decryption (@see RFC 5297)
27 */
28 abstract class SIVMode : AEADMode, Transformation
29 {
30 public:
31     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len)
32     {
33         if (!validNonceLength(nonce_len))
34             throw new InvalidIVLength(name, nonce_len);
35         
36         if (nonce_len)
37             m_nonce = m_cmac.process(nonce, nonce_len);
38         else
39             m_nonce.clear();
40         
41         m_msg_buf.clear();
42         
43         return SecureVector!ubyte();
44     }
45 
46     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
47     {
48         assert(buffer.length >= offset, "Offset is sane");
49         //logTrace("Update: ", cast(ubyte[])buffer[]);
50         const size_t sz = buffer.length - offset;
51         ubyte* buf = buffer.ptr + offset;
52         m_msg_buf ~= buf[0 .. sz];
53         buffer.resize(offset); // truncate msg
54     }
55 
56     final void setAssociatedDataN(size_t n, const(ubyte)* ad, size_t length)
57     {
58         if (n >= m_ad_macs.length)
59             m_ad_macs.resize(n+1);
60         
61         m_ad_macs[n] = m_cmac.process(ad, length);
62     }
63 
64     override void setAssociatedData(const(ubyte)* ad, size_t ad_len)
65     {
66         setAssociatedDataN(0, ad, ad_len);
67     }
68 
69     override @property string name() const
70     {
71         return m_name;
72     }
73 
74     override size_t updateGranularity() const
75     {
76         /*
77         This value does not particularly matter as regardless update
78         buffers all input, so in theory this could be 1. However as for instance
79         TransformationFilter creates updateGranularity() ubyte buffers, use a
80         somewhat large size to avoid bouncing on a tiny buffer.
81         */
82         return 128;
83     }
84 
85     override KeyLengthSpecification keySpec() const
86     {
87         return m_cmac.keySpec().multiple(2);
88     }
89 
90     override bool validNonceLength(size_t) const
91     {
92         return true;
93     }
94 
95     override void clear()
96     {
97         m_ctr.clear();
98         m_nonce.clear();
99         m_msg_buf.clear();
100         m_ad_macs.clear();
101     }
102 
103     override size_t tagSize() const { return 16; }
104     
105     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
106 
107 protected:
108     this(BlockCipher cipher) 
109     {
110         m_name = cipher.name ~ "/SIV";
111         m_ctr = new CTRBE(cipher.clone());
112         m_cmac = new CMAC(cipher);
113     }
114 
115     final StreamCipher ctr() { return *m_ctr; }
116 
117     final void setCtrIv(SecureVector!ubyte V)
118     {
119         V[8] &= 0x7F;
120         V[12] &= 0x7F;
121         
122         ctr().setIv(V.ptr, V.length);
123     }
124 
125     final ref const(SecureVector!ubyte) msgBuf() { return m_msg_buf; }
126 
127     final SecureVector!ubyte S2V(const(ubyte)* text, size_t text_len)
128     {
129         const ubyte[16] zero;
130         
131         SecureVector!ubyte V = m_cmac.process(zero.ptr, 16);
132         
133         for (size_t i = 0; i != m_ad_macs.length; ++i)
134         {
135             V = CMAC.polyDouble(V);
136             V ^= m_ad_macs[i];
137         }
138         
139         if (m_nonce.length > 0)
140         {
141             V = CMAC.polyDouble(V);
142             V ^= m_nonce;
143         }
144         
145         if (text_len < 16)
146         {
147             V = CMAC.polyDouble(V);
148             xorBuf(V.ptr, text, text_len);
149             V[text_len] ^= 0x80;
150             return m_cmac.process(V);
151         }
152         
153         m_cmac.update(text, text_len - 16);
154         xorBuf(V.ptr, text + text_len - 16, 16);
155         m_cmac.update(V);
156         
157         return m_cmac.finished();
158     }
159 protected:
160 
161     final override void keySchedule(const(ubyte)* key, size_t length)
162     {
163         const size_t keylen = length / 2;
164         m_cmac.setKey(key, keylen);
165         m_ctr.setKey(key + keylen, keylen);
166         m_ad_macs.clear();
167     }
168 
169 private:
170 
171     const string m_name;
172 
173     Unique!StreamCipher m_ctr;
174     Unique!MessageAuthenticationCode m_cmac;
175     SecureVector!ubyte m_nonce, m_msg_buf;
176     Vector!( SecureArray!ubyte ) m_ad_macs;
177 }
178 
179 /**
180 * SIV Encryption
181 */
182 final class SIVEncryption : SIVMode, Transformation
183 {
184 public:
185     /**
186     * Params:
187     *  cipher = a block cipher
188     */
189     this(BlockCipher cipher)
190     {
191         super(cipher);
192     }
193 
194     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
195     {
196         import std.algorithm : max;
197         assert(buffer.length >= offset, "Offset is sane");
198         
199         if (msgBuf().length > 0)
200         {
201             auto buffer2 = msgBuf().dup;
202             buffer2 ~= buffer;
203             buffer = buffer2.move;
204         }
205 
206         SecureVector!ubyte V = S2V(buffer.ptr + offset, buffer.length - offset);
207         if (V.length > 0) {
208             auto buffer2 = V.dup;
209             buffer2 ~= buffer;
210             buffer = buffer2.move;
211         }
212         setCtrIv(V.dup);
213         ctr().cipher1(buffer.ptr + offset + V.length, buffer.length - offset - V.length);
214     }
215     
216     override size_t outputLength(size_t input_length) const
217     { return input_length + tagSize(); }
218 
219     override size_t minimumFinalSize() const { return 0; }
220 
221     // Interface fallthrough
222     override string provider() const { return "core"; }
223     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
224     override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); }
225     override size_t updateGranularity() const { return super.updateGranularity(); }
226     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
227     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
228     override @property string name() const { return super.name; }
229     override void clear() { return super.clear(); }
230 }
231 
232 /**
233 * SIV Decryption
234 */
235 final class SIVDecryption : SIVMode, Transformation
236 {
237 public:
238     /**
239     * Params:
240     *  cipher = a 128-bit block cipher
241     */
242     this(BlockCipher cipher)
243     {
244         super(cipher);
245     }
246 
247     override void finish(ref SecureVector!ubyte buffer, size_t offset)
248     {
249         import std.algorithm : max;
250         assert(buffer.length >= offset, "Offset is sane");
251 
252         if (msgBuf().length > 0) {
253             auto buffer2 = msgBuf().dup;
254             buffer2 ~= buffer;
255             buffer = buffer2.move;
256         }
257 
258         const size_t sz = buffer.length - offset;
259         
260         assert(sz >= tagSize(), "We have the tag");
261 
262         SecureVector!ubyte V = SecureVector!ubyte(buffer.ptr[offset .. offset + 16]);
263         
264         setCtrIv(V.dup);
265         
266         ctr().cipher(buffer.ptr + offset + V.length, buffer.ptr + offset, buffer.length - offset - V.length);
267         
268         SecureVector!ubyte T = S2V(buffer.ptr + offset, buffer.length - offset - V.length);
269         
270         if (T != V)
271             throw new IntegrityFailure("SIV tag check failed");
272         
273         buffer.resize(buffer.length - tagSize());
274     }
275 
276     override size_t outputLength(size_t input_length) const
277     {
278         assert(input_length > tagSize(), "Sufficient input");
279         return input_length - tagSize();
280     }
281 
282     override size_t minimumFinalSize() const { return tagSize(); }
283 
284     // Interface fallthrough
285     override string provider() const { return "core"; }
286     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
287     override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); }
288     override size_t updateGranularity() const { return super.updateGranularity(); }
289     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
290     override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); }
291     override @property string name() const { return super.name; }
292     override void clear() { return super.clear(); }
293 }