xref: /freebsd/crypto/openssl/crypto/modes/cts128.c (revision d6b92ffa)
1 /* ====================================================================
2  * Copyright (c) 2008 The OpenSSL Project. All rights reserved.
3  *
4  * Rights for redistribution and usage in source and binary
5  * forms are granted according to the OpenSSL license.
6  */
7 
8 #include <openssl/crypto.h>
9 #include "modes_lcl.h"
10 #include <string.h>
11 
12 #ifndef MODES_DEBUG
13 # ifndef NDEBUG
14 #  define NDEBUG
15 # endif
16 #endif
17 #include <assert.h>
18 
19 /*
20  * Trouble with Ciphertext Stealing, CTS, mode is that there is no
21  * common official specification, but couple of cipher/application
22  * specific ones: RFC2040 and RFC3962. Then there is 'Proposal to
23  * Extend CBC Mode By "Ciphertext Stealing"' at NIST site, which
24  * deviates from mentioned RFCs. Most notably it allows input to be
25  * of block length and it doesn't flip the order of the last two
26  * blocks. CTS is being discussed even in ECB context, but it's not
27  * adopted for any known application. This implementation provides
28  * two interfaces: one compliant with above mentioned RFCs and one
29  * compliant with the NIST proposal, both extending CBC mode.
30  */
31 
32 size_t CRYPTO_cts128_encrypt_block(const unsigned char *in,
33                                    unsigned char *out, size_t len,
34                                    const void *key, unsigned char ivec[16],
35                                    block128_f block)
36 {
37     size_t residue, n;
38 
39     assert(in && out && key && ivec);
40 
41     if (len <= 16)
42         return 0;
43 
44     if ((residue = len % 16) == 0)
45         residue = 16;
46 
47     len -= residue;
48 
49     CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
50 
51     in += len;
52     out += len;
53 
54     for (n = 0; n < residue; ++n)
55         ivec[n] ^= in[n];
56     (*block) (ivec, ivec, key);
57     memcpy(out, out - 16, residue);
58     memcpy(out - 16, ivec, 16);
59 
60     return len + residue;
61 }
62 
63 size_t CRYPTO_nistcts128_encrypt_block(const unsigned char *in,
64                                        unsigned char *out, size_t len,
65                                        const void *key,
66                                        unsigned char ivec[16],
67                                        block128_f block)
68 {
69     size_t residue, n;
70 
71     assert(in && out && key && ivec);
72 
73     if (len < 16)
74         return 0;
75 
76     residue = len % 16;
77 
78     len -= residue;
79 
80     CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
81 
82     if (residue == 0)
83         return len;
84 
85     in += len;
86     out += len;
87 
88     for (n = 0; n < residue; ++n)
89         ivec[n] ^= in[n];
90     (*block) (ivec, ivec, key);
91     memcpy(out - 16 + residue, ivec, 16);
92 
93     return len + residue;
94 }
95 
96 size_t CRYPTO_cts128_encrypt(const unsigned char *in, unsigned char *out,
97                              size_t len, const void *key,
98                              unsigned char ivec[16], cbc128_f cbc)
99 {
100     size_t residue;
101     union {
102         size_t align;
103         unsigned char c[16];
104     } tmp;
105 
106     assert(in && out && key && ivec);
107 
108     if (len <= 16)
109         return 0;
110 
111     if ((residue = len % 16) == 0)
112         residue = 16;
113 
114     len -= residue;
115 
116     (*cbc) (in, out, len, key, ivec, 1);
117 
118     in += len;
119     out += len;
120 
121 #if defined(CBC_HANDLES_TRUNCATED_IO)
122     memcpy(tmp.c, out - 16, 16);
123     (*cbc) (in, out - 16, residue, key, ivec, 1);
124     memcpy(out, tmp.c, residue);
125 #else
126     memset(tmp.c, 0, sizeof(tmp));
127     memcpy(tmp.c, in, residue);
128     memcpy(out, out - 16, residue);
129     (*cbc) (tmp.c, out - 16, 16, key, ivec, 1);
130 #endif
131     return len + residue;
132 }
133 
134 size_t CRYPTO_nistcts128_encrypt(const unsigned char *in, unsigned char *out,
135                                  size_t len, const void *key,
136                                  unsigned char ivec[16], cbc128_f cbc)
137 {
138     size_t residue;
139     union {
140         size_t align;
141         unsigned char c[16];
142     } tmp;
143 
144     assert(in && out && key && ivec);
145 
146     if (len < 16)
147         return 0;
148 
149     residue = len % 16;
150 
151     len -= residue;
152 
153     (*cbc) (in, out, len, key, ivec, 1);
154 
155     if (residue == 0)
156         return len;
157 
158     in += len;
159     out += len;
160 
161 #if defined(CBC_HANDLES_TRUNCATED_IO)
162     (*cbc) (in, out - 16 + residue, residue, key, ivec, 1);
163 #else
164     memset(tmp.c, 0, sizeof(tmp));
165     memcpy(tmp.c, in, residue);
166     (*cbc) (tmp.c, out - 16 + residue, 16, key, ivec, 1);
167 #endif
168     return len + residue;
169 }
170 
171 size_t CRYPTO_cts128_decrypt_block(const unsigned char *in,
172                                    unsigned char *out, size_t len,
173                                    const void *key, unsigned char ivec[16],
174                                    block128_f block)
175 {
176     size_t residue, n;
177     union {
178         size_t align;
179         unsigned char c[32];
180     } tmp;
181 
182     assert(in && out && key && ivec);
183 
184     if (len <= 16)
185         return 0;
186 
187     if ((residue = len % 16) == 0)
188         residue = 16;
189 
190     len -= 16 + residue;
191 
192     if (len) {
193         CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
194         in += len;
195         out += len;
196     }
197 
198     (*block) (in, tmp.c + 16, key);
199 
200     memcpy(tmp.c, tmp.c + 16, 16);
201     memcpy(tmp.c, in + 16, residue);
202     (*block) (tmp.c, tmp.c, key);
203 
204     for (n = 0; n < 16; ++n) {
205         unsigned char c = in[n];
206         out[n] = tmp.c[n] ^ ivec[n];
207         ivec[n] = c;
208     }
209     for (residue += 16; n < residue; ++n)
210         out[n] = tmp.c[n] ^ in[n];
211 
212     return 16 + len + residue;
213 }
214 
215 size_t CRYPTO_nistcts128_decrypt_block(const unsigned char *in,
216                                        unsigned char *out, size_t len,
217                                        const void *key,
218                                        unsigned char ivec[16],
219                                        block128_f block)
220 {
221     size_t residue, n;
222     union {
223         size_t align;
224         unsigned char c[32];
225     } tmp;
226 
227     assert(in && out && key && ivec);
228 
229     if (len < 16)
230         return 0;
231 
232     residue = len % 16;
233 
234     if (residue == 0) {
235         CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
236         return len;
237     }
238 
239     len -= 16 + residue;
240 
241     if (len) {
242         CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
243         in += len;
244         out += len;
245     }
246 
247     (*block) (in + residue, tmp.c + 16, key);
248 
249     memcpy(tmp.c, tmp.c + 16, 16);
250     memcpy(tmp.c, in, residue);
251     (*block) (tmp.c, tmp.c, key);
252 
253     for (n = 0; n < 16; ++n) {
254         unsigned char c = in[n];
255         out[n] = tmp.c[n] ^ ivec[n];
256         ivec[n] = in[n + residue];
257         tmp.c[n] = c;
258     }
259     for (residue += 16; n < residue; ++n)
260         out[n] = tmp.c[n] ^ tmp.c[n - 16];
261 
262     return 16 + len + residue;
263 }
264 
265 size_t CRYPTO_cts128_decrypt(const unsigned char *in, unsigned char *out,
266                              size_t len, const void *key,
267                              unsigned char ivec[16], cbc128_f cbc)
268 {
269     size_t residue;
270     union {
271         size_t align;
272         unsigned char c[32];
273     } tmp;
274 
275     assert(in && out && key && ivec);
276 
277     if (len <= 16)
278         return 0;
279 
280     if ((residue = len % 16) == 0)
281         residue = 16;
282 
283     len -= 16 + residue;
284 
285     if (len) {
286         (*cbc) (in, out, len, key, ivec, 0);
287         in += len;
288         out += len;
289     }
290 
291     memset(tmp.c, 0, sizeof(tmp));
292     /*
293      * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
294      */
295     (*cbc) (in, tmp.c, 16, key, tmp.c + 16, 0);
296 
297     memcpy(tmp.c, in + 16, residue);
298 #if defined(CBC_HANDLES_TRUNCATED_IO)
299     (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
300 #else
301     (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
302     memcpy(out, tmp.c, 16 + residue);
303 #endif
304     return 16 + len + residue;
305 }
306 
307 size_t CRYPTO_nistcts128_decrypt(const unsigned char *in, unsigned char *out,
308                                  size_t len, const void *key,
309                                  unsigned char ivec[16], cbc128_f cbc)
310 {
311     size_t residue;
312     union {
313         size_t align;
314         unsigned char c[32];
315     } tmp;
316 
317     assert(in && out && key && ivec);
318 
319     if (len < 16)
320         return 0;
321 
322     residue = len % 16;
323 
324     if (residue == 0) {
325         (*cbc) (in, out, len, key, ivec, 0);
326         return len;
327     }
328 
329     len -= 16 + residue;
330 
331     if (len) {
332         (*cbc) (in, out, len, key, ivec, 0);
333         in += len;
334         out += len;
335     }
336 
337     memset(tmp.c, 0, sizeof(tmp));
338     /*
339      * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
340      */
341     (*cbc) (in + residue, tmp.c, 16, key, tmp.c + 16, 0);
342 
343     memcpy(tmp.c, in, residue);
344 #if defined(CBC_HANDLES_TRUNCATED_IO)
345     (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
346 #else
347     (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
348     memcpy(out, tmp.c, 16 + residue);
349 #endif
350     return 16 + len + residue;
351 }
352 
353 #if defined(SELFTEST)
354 # include <stdio.h>
355 # include <openssl/aes.h>
356 
357 /* test vectors from RFC 3962 */
358 static const unsigned char test_key[16] = "chicken teriyaki";
359 static const unsigned char test_input[64] =
360     "I would like the" " General Gau's C"
361     "hicken, please, " "and wonton soup.";
362 static const unsigned char test_iv[16] =
363     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
364 
365 static const unsigned char vector_17[17] = {
366     0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
367     0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f,
368     0x97
369 };
370 
371 static const unsigned char vector_31[31] = {
372     0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
373     0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22,
374     0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
375     0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5
376 };
377 
378 static const unsigned char vector_32[32] = {
379     0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
380     0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
381     0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
382     0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84
383 };
384 
385 static const unsigned char vector_47[47] = {
386     0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
387     0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
388     0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
389     0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e,
390     0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
391     0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5
392 };
393 
394 static const unsigned char vector_48[48] = {
395     0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
396     0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
397     0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
398     0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8,
399     0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
400     0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
401 };
402 
403 static const unsigned char vector_64[64] = {
404     0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
405     0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
406     0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
407     0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
408     0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
409     0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40,
410     0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
411     0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
412 };
413 
414 static AES_KEY encks, decks;
415 
416 void test_vector(const unsigned char *vector, size_t len)
417 {
418     unsigned char iv[sizeof(test_iv)];
419     unsigned char cleartext[64], ciphertext[64];
420     size_t tail;
421 
422     printf("vector_%d\n", len);
423     fflush(stdout);
424 
425     if ((tail = len % 16) == 0)
426         tail = 16;
427     tail += 16;
428 
429     /* test block-based encryption */
430     memcpy(iv, test_iv, sizeof(test_iv));
431     CRYPTO_cts128_encrypt_block(test_input, ciphertext, len, &encks, iv,
432                                 (block128_f) AES_encrypt);
433     if (memcmp(ciphertext, vector, len))
434         fprintf(stderr, "output_%d mismatch\n", len), exit(1);
435     if (memcmp(iv, vector + len - tail, sizeof(iv)))
436         fprintf(stderr, "iv_%d mismatch\n", len), exit(1);
437 
438     /* test block-based decryption */
439     memcpy(iv, test_iv, sizeof(test_iv));
440     CRYPTO_cts128_decrypt_block(ciphertext, cleartext, len, &decks, iv,
441                                 (block128_f) AES_decrypt);
442     if (memcmp(cleartext, test_input, len))
443         fprintf(stderr, "input_%d mismatch\n", len), exit(2);
444     if (memcmp(iv, vector + len - tail, sizeof(iv)))
445         fprintf(stderr, "iv_%d mismatch\n", len), exit(2);
446 
447     /* test streamed encryption */
448     memcpy(iv, test_iv, sizeof(test_iv));
449     CRYPTO_cts128_encrypt(test_input, ciphertext, len, &encks, iv,
450                           (cbc128_f) AES_cbc_encrypt);
451     if (memcmp(ciphertext, vector, len))
452         fprintf(stderr, "output_%d mismatch\n", len), exit(3);
453     if (memcmp(iv, vector + len - tail, sizeof(iv)))
454         fprintf(stderr, "iv_%d mismatch\n", len), exit(3);
455 
456     /* test streamed decryption */
457     memcpy(iv, test_iv, sizeof(test_iv));
458     CRYPTO_cts128_decrypt(ciphertext, cleartext, len, &decks, iv,
459                           (cbc128_f) AES_cbc_encrypt);
460     if (memcmp(cleartext, test_input, len))
461         fprintf(stderr, "input_%d mismatch\n", len), exit(4);
462     if (memcmp(iv, vector + len - tail, sizeof(iv)))
463         fprintf(stderr, "iv_%d mismatch\n", len), exit(4);
464 }
465 
466 void test_nistvector(const unsigned char *vector, size_t len)
467 {
468     unsigned char iv[sizeof(test_iv)];
469     unsigned char cleartext[64], ciphertext[64], nistvector[64];
470     size_t tail;
471 
472     printf("nistvector_%d\n", len);
473     fflush(stdout);
474 
475     if ((tail = len % 16) == 0)
476         tail = 16;
477 
478     len -= 16 + tail;
479     memcpy(nistvector, vector, len);
480     /* flip two last blocks */
481     memcpy(nistvector + len, vector + len + 16, tail);
482     memcpy(nistvector + len + tail, vector + len, 16);
483     len += 16 + tail;
484     tail = 16;
485 
486     /* test block-based encryption */
487     memcpy(iv, test_iv, sizeof(test_iv));
488     CRYPTO_nistcts128_encrypt_block(test_input, ciphertext, len, &encks, iv,
489                                     (block128_f) AES_encrypt);
490     if (memcmp(ciphertext, nistvector, len))
491         fprintf(stderr, "output_%d mismatch\n", len), exit(1);
492     if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
493         fprintf(stderr, "iv_%d mismatch\n", len), exit(1);
494 
495     /* test block-based decryption */
496     memcpy(iv, test_iv, sizeof(test_iv));
497     CRYPTO_nistcts128_decrypt_block(ciphertext, cleartext, len, &decks, iv,
498                                     (block128_f) AES_decrypt);
499     if (memcmp(cleartext, test_input, len))
500         fprintf(stderr, "input_%d mismatch\n", len), exit(2);
501     if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
502         fprintf(stderr, "iv_%d mismatch\n", len), exit(2);
503 
504     /* test streamed encryption */
505     memcpy(iv, test_iv, sizeof(test_iv));
506     CRYPTO_nistcts128_encrypt(test_input, ciphertext, len, &encks, iv,
507                               (cbc128_f) AES_cbc_encrypt);
508     if (memcmp(ciphertext, nistvector, len))
509         fprintf(stderr, "output_%d mismatch\n", len), exit(3);
510     if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
511         fprintf(stderr, "iv_%d mismatch\n", len), exit(3);
512 
513     /* test streamed decryption */
514     memcpy(iv, test_iv, sizeof(test_iv));
515     CRYPTO_nistcts128_decrypt(ciphertext, cleartext, len, &decks, iv,
516                               (cbc128_f) AES_cbc_encrypt);
517     if (memcmp(cleartext, test_input, len))
518         fprintf(stderr, "input_%d mismatch\n", len), exit(4);
519     if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
520         fprintf(stderr, "iv_%d mismatch\n", len), exit(4);
521 }
522 
523 int main()
524 {
525     AES_set_encrypt_key(test_key, 128, &encks);
526     AES_set_decrypt_key(test_key, 128, &decks);
527 
528     test_vector(vector_17, sizeof(vector_17));
529     test_vector(vector_31, sizeof(vector_31));
530     test_vector(vector_32, sizeof(vector_32));
531     test_vector(vector_47, sizeof(vector_47));
532     test_vector(vector_48, sizeof(vector_48));
533     test_vector(vector_64, sizeof(vector_64));
534 
535     test_nistvector(vector_17, sizeof(vector_17));
536     test_nistvector(vector_31, sizeof(vector_31));
537     test_nistvector(vector_32, sizeof(vector_32));
538     test_nistvector(vector_47, sizeof(vector_47));
539     test_nistvector(vector_48, sizeof(vector_48));
540     test_nistvector(vector_64, sizeof(vector_64));
541 
542     return 0;
543 }
544 #endif
545