Added CCM and CCM_8 cipher suites.
[BoarSSL] / Crypto / BlockCipherCore.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26
27 namespace Crypto {
28
29 /*
30 * This class is a convenient base class for implementations of
31 * IBlockCipher. Block cipher implementations must implement:
32 * int BlockSize { get; }
33 * void SetKey(byte[] key, int off, int len)
34 * void BlockEncrypt(byte[] data, int off)
35 * void BlockDecrypt(byte[] data, int off)
36 *
37 * Note that 'BlockSize' is invoked from the constructor of this class.
38 *
39 * Implementations MAY also override the default implementations of:
40 * void CBCEncrypt(byte[] iv, byte[] data)
41 * void CBCEncrypt(byte[] iv, byte[] data, int off, int len)
42 * void CBCDecrypt(byte[] iv, byte[] data)
43 * void CBCDecrypt(byte[] iv, byte[] data, int off, int len)
44 * uint CTRRun(byte[] iv, uint cc, byte[] data)
45 * uint CTRRun(byte[] iv, uint cc, byte[] data, int off, int len)
46 * void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt, byte[] data)
47 * void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt,
48 * byte[] data, int off, int len)
49 * Note that CBCEncrypt(byte[],byte[]) (respectively
50 * CBCDecrypt(byte[],byte[]), CTRRun(byte[],uint,byte[]) and
51 * CTRCBCRun(byte[],byte[],bool,byte[])) simply calls
52 * CBCEncrypt(byte[],byte[],int,int) (respectively
53 * CBCDecrypt(byte[],byte[],int,int), CTRRun(byte[],uint,byte[],int,int)
54 * and CTRCBCRun(byte[],byte[],bool,byte[],int,int)) so implementations
55 * who wish to override these methods may content themselves with
56 * overriding the four methods with the "off" and "len" extra parameters.
57 */
58
59 public abstract class BlockCipherCore : IBlockCipher {
60
61 byte[] tmp;
62
63 /*
64 * This constructor invokes 'BlockSize'.
65 */
66 public BlockCipherCore()
67 {
68 tmp = new byte[BlockSize];
69 }
70
71 /* see IBlockCipher */
72 public abstract int BlockSize { get; }
73
74 /*
75 * This method is implemented by calling SetKey(byte[],int,int).
76 */
77 public virtual void SetKey(byte[] key)
78 {
79 SetKey(key, 0, key.Length);
80 }
81
82 /* see IBlockCipher */
83 public abstract void SetKey(byte[] key, int off, int len);
84
85 /*
86 * This method is implemented by calling BlockEncrypt(byte[],int).
87 */
88 public virtual void BlockEncrypt(byte[] buf)
89 {
90 BlockEncrypt(buf, 0);
91 }
92
93 /* see IBlockCipher */
94 public abstract void BlockEncrypt(byte[] data, int off);
95
96 /*
97 * This method is implemented by calling BlockDecrypt(byte[],int).
98 */
99 public virtual void BlockDecrypt(byte[] buf)
100 {
101 BlockDecrypt(buf, 0);
102 }
103
104 /* see IBlockCipher */
105 public abstract void BlockDecrypt(byte[] data, int off);
106
107 /*
108 * This method is implemented by calling
109 * CBCEncrypt(byte[],byte[],int,int).
110 */
111 public virtual void CBCEncrypt(byte[] iv, byte[] data)
112 {
113 CBCEncrypt(iv, data, 0, data.Length);
114 }
115
116 /* see IBlockCipher */
117 public virtual void CBCEncrypt(
118 byte[] iv, byte[] data, int off, int len)
119 {
120 int blen = BlockSize;
121 if (iv.Length != blen) {
122 throw new CryptoException("wrong IV length");
123 }
124 if (len >= blen) {
125 for (int i = 0; i < blen; i ++) {
126 data[off + i] ^= iv[i];
127 }
128 BlockEncrypt(data, off);
129 off += blen;
130 len -= blen;
131 while (len >= blen) {
132 for (int i = 0; i < blen; i ++) {
133 data[off + i] ^= data[off + i - blen];
134 }
135 BlockEncrypt(data, off);
136 off += blen;
137 len -= blen;
138 }
139 }
140 if (len != 0) {
141 throw new CryptoException("data length is not"
142 + " multiple of the block size");
143 }
144 }
145
146 /*
147 * This method is implemented by calling
148 * CBCDecrypt(byte[],byte[],int,int).
149 */
150 public virtual void CBCDecrypt(byte[] iv, byte[] data)
151 {
152 CBCDecrypt(iv, data, 0, data.Length);
153 }
154
155 /* see IBlockCipher */
156 public virtual void CBCDecrypt(
157 byte[] iv, byte[] data, int off, int len)
158 {
159 int blen = BlockSize;
160 if (iv.Length != blen) {
161 throw new CryptoException("wrong IV length");
162 }
163 int dblen = blen << 1;
164 off += len;
165 while (len >= dblen) {
166 off -= blen;
167 BlockDecrypt(data, off);
168 for (int i = 0; i < blen; i ++) {
169 data[off + i] ^= data[off + i - blen];
170 }
171 len -= blen;
172 }
173 if (len >= blen) {
174 off -= blen;
175 BlockDecrypt(data, off);
176 for (int i = 0; i < blen; i ++) {
177 data[off + i] ^= iv[i];
178 }
179 len -= blen;
180 }
181 if (len != 0) {
182 throw new CryptoException("data length is not"
183 + " multiple of the block size");
184 }
185 }
186
187 /*
188 * This method is implemented by calling
189 * CTRRun(byte[],uint,byte[],int,int).
190 */
191 public virtual uint CTRRun(byte[] iv, uint cc, byte[] data)
192 {
193 return CTRRun(iv, cc, data, 0, data.Length);
194 }
195
196 /* see IBlockCipher */
197 public virtual uint CTRRun(
198 byte[] iv, uint cc, byte[] data, int off, int len)
199 {
200 int blen = BlockSize;
201 if (iv.Length != blen - 4) {
202 throw new CryptoException("wrong IV length");
203 }
204 while (len > 0) {
205 Array.Copy(iv, 0, tmp, 0, blen - 4);
206 tmp[blen - 4] = (byte)(cc >> 24);
207 tmp[blen - 3] = (byte)(cc >> 16);
208 tmp[blen - 2] = (byte)(cc >> 8);
209 tmp[blen - 1] = (byte)cc;
210 BlockEncrypt(tmp, 0);
211 int clen = Math.Min(blen, len);
212 for (int i = 0; i < clen; i ++) {
213 data[off + i] ^= tmp[i];
214 }
215 off += clen;
216 len -= clen;
217 cc ++;
218 }
219 return cc;
220 }
221
222 /*
223 * This method is implemented by calling
224 * CTRCBCRun(byte[],byte[],bool,byte[],int,int).
225 */
226 public virtual void CTRCBCRun(byte[] ctr, byte[] cbcmac,
227 bool encrypt, byte[] data)
228 {
229 CTRCBCRun(ctr, cbcmac, encrypt, data, 0, data.Length);
230 }
231
232 /* see IBlockCipher */
233 public virtual void CTRCBCRun(byte[] ctr, byte[] cbcmac,
234 bool encrypt, byte[] data, int off, int len)
235 {
236 if (!encrypt) {
237 CBCMac(cbcmac, data, off, len);
238 }
239 DoCTRFull(ctr, data, off, len);
240 if (encrypt) {
241 CBCMac(cbcmac, data, off, len);
242 }
243 }
244
245 void DoCTRFull(byte[] ctr, byte[] data, int off, int len)
246 {
247 int blen = BlockSize;
248 if (ctr.Length != blen) {
249 throw new CryptoException("wrong counter length");
250 }
251 while (len > 0) {
252 Array.Copy(ctr, 0, tmp, 0, blen);
253 uint cc = 1;
254 for (int i = blen - 1; i >= 0; i --) {
255 uint x = ctr[i] + cc;
256 ctr[i] = (byte)x;
257 cc = x >> 8;
258 }
259 BlockEncrypt(tmp, 0);
260 int clen = Math.Min(blen, len);
261 for (int i = 0; i < clen; i ++) {
262 data[off + i] ^= tmp[i];
263 }
264 off += clen;
265 len -= clen;
266 }
267 }
268
269 /* see IBlockCipher */
270 public void CBCMac(byte[] cbcmac, byte[] data, int off, int len)
271 {
272 int blen = BlockSize;
273 if (cbcmac.Length != blen) {
274 throw new CryptoException("wrong MAC length");
275 }
276 while (len > 0) {
277 for (int i = 0; i < blen; i ++) {
278 cbcmac[i] ^= data[off + i];
279 }
280 BlockEncrypt(cbcmac, 0);
281 off += blen;
282 len -= blen;
283 }
284 }
285
286 /* see IBlockCipher */
287 public abstract IBlockCipher Dup();
288 }
289
290 }