xref: /optee_os/core/lib/libtomcrypt/src/encauth/ccm/ccm_memory.c (revision 2a65ecaf7d6f855e24ce1a117fe1931f7378f82c)
1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2 /* SPDX-License-Identifier: Unlicense */
3 #include "tomcrypt_private.h"
4 
5 /**
6   @file ccm_memory.c
7   CCM support, process a block of memory, Tom St Denis
8 */
9 
10 #ifdef LTC_CCM_MODE
11 
12 /**
13    CCM encrypt/decrypt and produce an authentication tag
14 
15      *1 'pt', 'ct' and 'tag' can both be 'in' or 'out', depending on 'direction'
16 
17    @param cipher     The index of the cipher desired
18    @param key        The secret key to use
19    @param keylen     The length of the secret key (octets)
20    @param uskey      A previously scheduled key [optional can be NULL]
21    @param nonce      The session nonce [use once]
22    @param noncelen   The length of the nonce
23    @param header     The header for the session
24    @param headerlen  The length of the header (octets)
25    @param pt         [*1] The plaintext
26    @param ptlen      The length of the plaintext (octets)
27    @param ct         [*1] The ciphertext
28    @param tag        [*1] The destination tag
29    @param taglen     The max size and resulting size of the authentication tag
30    @param direction  Encrypt or Decrypt direction (0 or 1)
31    @return CRYPT_OK if successful
32 */
ccm_memory(int cipher,const unsigned char * key,unsigned long keylen,symmetric_key * uskey,const unsigned char * nonce,unsigned long noncelen,const unsigned char * header,unsigned long headerlen,unsigned char * pt,unsigned long ptlen,unsigned char * ct,unsigned char * tag,unsigned long * taglen,int direction)33 int ccm_memory(int cipher,
34     const unsigned char *key,    unsigned long keylen,
35     symmetric_key       *uskey,
36     const unsigned char *nonce,  unsigned long noncelen,
37     const unsigned char *header, unsigned long headerlen,
38           unsigned char *pt,     unsigned long ptlen,
39           unsigned char *ct,
40           unsigned char *tag,    unsigned long *taglen,
41                     int  direction)
42 {
43    unsigned char  PAD[16], ctr[16], CTRPAD[16], ptTag[16], b, *pt_real;
44    unsigned char *pt_work = NULL;
45    symmetric_key *skey;
46    int            err;
47    unsigned long  len, L, x, y, z, CTRlen;
48 
49    if (uskey == NULL) {
50       LTC_ARGCHK(key    != NULL);
51    }
52    LTC_ARGCHK(nonce  != NULL);
53    if (headerlen > 0) {
54       LTC_ARGCHK(header != NULL);
55    }
56    LTC_ARGCHK(pt     != NULL);
57    LTC_ARGCHK(ct     != NULL);
58    LTC_ARGCHK(tag    != NULL);
59    LTC_ARGCHK(taglen != NULL);
60 
61    pt_real = pt;
62 
63 #ifdef LTC_FAST
64    if (16 % sizeof(LTC_FAST_TYPE)) {
65       return CRYPT_INVALID_ARG;
66    }
67 #endif
68 
69    /* check cipher input */
70    if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
71       return err;
72    }
73    if (cipher_descriptor[cipher]->block_length != 16) {
74       return CRYPT_INVALID_CIPHER;
75    }
76 
77    /* make sure the taglen is valid */
78    if (*taglen < 4 || *taglen > 16 || (*taglen % 2) == 1 || headerlen > 0x7fffffffu) {
79       return CRYPT_INVALID_ARG;
80    }
81 
82    /* is there an accelerator? */
83    if (cipher_descriptor[cipher]->accel_ccm_memory != NULL) {
84        return cipher_descriptor[cipher]->accel_ccm_memory(
85            key,    keylen,
86            uskey,
87            nonce,  noncelen,
88            header, headerlen,
89            pt,     ptlen,
90            ct,
91            tag,    taglen,
92            direction);
93    }
94 
95    /* let's get the L value */
96    len = ptlen;
97    L   = 0;
98    while (len) {
99       ++L;
100       len >>= 8;
101    }
102    if (L <= 1) {
103       L = 2;
104    }
105 
106    /* increase L to match the nonce len */
107    noncelen = (noncelen > 13) ? 13 : noncelen;
108    if ((15 - noncelen) > L) {
109       L = 15 - noncelen;
110    }
111    if (L > 8) {
112       return CRYPT_INVALID_ARG;
113    }
114 
115    /* allocate mem for the symmetric key */
116    if (uskey == NULL) {
117       skey = XMALLOC(sizeof(*skey));
118       if (skey == NULL) {
119          return CRYPT_MEM;
120       }
121 
122       /* initialize the cipher */
123       if ((err = cipher_descriptor[cipher]->setup(key, keylen, 0, skey)) != CRYPT_OK) {
124          XFREE(skey);
125          return err;
126       }
127    } else {
128       skey = uskey;
129    }
130 
131    /* initialize buffer for pt */
132    if (direction == CCM_DECRYPT && ptlen > 0) {
133       pt_work = XMALLOC(ptlen);
134       if (pt_work == NULL) {
135          goto error;
136       }
137       pt = pt_work;
138    }
139 
140    /* form B_0 == flags | Nonce N | l(m) */
141    x = 0;
142    PAD[x++] = (unsigned char)(((headerlen > 0) ? (1<<6) : 0) |
143             (((*taglen - 2)>>1)<<3)        |
144             (L-1));
145 
146    /* nonce */
147    for (y = 0; y < 15 - L; y++) {
148        PAD[x++] = nonce[y];
149    }
150 
151    /* store len */
152    len = ptlen;
153 
154    /* shift len so the upper bytes of len are the contents of the length */
155    for (y = L; y < 4; y++) {
156        len <<= 8;
157    }
158 
159    /* store l(m) (only store 32-bits) */
160    for (y = 0; L > 4 && (L-y)>4; y++) {
161        PAD[x++] = 0;
162    }
163    for (; y < L; y++) {
164        if (x >= sizeof(PAD)) {
165           err = CRYPT_INVALID_ARG;
166           goto error;
167        }
168        PAD[x++] = (unsigned char)((len >> 24) & 255);
169        len <<= 8;
170    }
171 
172    /* encrypt PAD */
173    if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
174        goto error;
175    }
176 
177    /* handle header */
178    if (headerlen > 0) {
179       x = 0;
180 
181       /* store length */
182       if (headerlen < ((1UL<<16) - (1UL<<8))) {
183          PAD[x++] ^= (headerlen>>8) & 255;
184          PAD[x++] ^= headerlen & 255;
185       } else {
186          PAD[x++] ^= 0xFF;
187          PAD[x++] ^= 0xFE;
188          PAD[x++] ^= (headerlen>>24) & 255;
189          PAD[x++] ^= (headerlen>>16) & 255;
190          PAD[x++] ^= (headerlen>>8) & 255;
191          PAD[x++] ^= headerlen & 255;
192       }
193 
194       /* now add the data */
195       for (y = 0; y < headerlen; y++) {
196           if (x == 16) {
197              /* full block so let's encrypt it */
198              if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
199                 goto error;
200              }
201              x = 0;
202           }
203           PAD[x++] ^= header[y];
204       }
205 
206       /* remainder */
207       if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
208          goto error;
209       }
210    }
211 
212    /* setup the ctr counter */
213    x = 0;
214 
215    /* flags */
216    ctr[x++] = (unsigned char)L-1;
217 
218    /* nonce */
219    for (y = 0; y < (16 - (L+1)); ++y) {
220       ctr[x++] = nonce[y];
221    }
222    /* offset */
223    while (x < 16) {
224       ctr[x++] = 0;
225    }
226 
227    x      = 0;
228    CTRlen = 16;
229 
230    /* now handle the PT */
231    if (ptlen > 0) {
232       y = 0;
233 #ifdef LTC_FAST
234       if (ptlen & ~15)  {
235           if (direction == CCM_ENCRYPT) {
236              for (; y < (ptlen & ~15); y += 16) {
237                 /* increment the ctr? */
238                 for (z = 15; z > 15-L; z--) {
239                     ctr[z] = (ctr[z] + 1) & 255;
240                     if (ctr[z]) break;
241                 }
242                 if ((err = cipher_descriptor[cipher]->ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
243                    goto error;
244                 }
245 
246                 /* xor the PT against the pad first */
247                 for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
248                     *(LTC_FAST_TYPE_PTR_CAST(&PAD[z]))  ^= *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z]));
249                     *(LTC_FAST_TYPE_PTR_CAST(&ct[y+z])) = *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z])) ^ *(LTC_FAST_TYPE_PTR_CAST(&CTRPAD[z]));
250                 }
251                 if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
252                    goto error;
253                 }
254              }
255           } else { /* direction == CCM_DECRYPT */
256              for (; y < (ptlen & ~15); y += 16) {
257                 /* increment the ctr? */
258                 for (z = 15; z > 15-L; z--) {
259                     ctr[z] = (ctr[z] + 1) & 255;
260                     if (ctr[z]) break;
261                 }
262                 if ((err = cipher_descriptor[cipher]->ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
263                    goto error;
264                 }
265 
266                 /* xor the PT against the pad last */
267                 for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
268                     *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z])) = *(LTC_FAST_TYPE_PTR_CAST(&ct[y+z])) ^ *(LTC_FAST_TYPE_PTR_CAST(&CTRPAD[z]));
269                     *(LTC_FAST_TYPE_PTR_CAST(&PAD[z]))  ^= *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z]));
270                 }
271                 if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
272                    goto error;
273                 }
274              }
275           }
276       }
277 #endif
278 
279       for (; y < ptlen; y++) {
280           /* increment the ctr? */
281           if (CTRlen == 16) {
282              for (z = 15; z > 15-L; z--) {
283                  ctr[z] = (ctr[z] + 1) & 255;
284                  if (ctr[z]) break;
285              }
286              if ((err = cipher_descriptor[cipher]->ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
287                 goto error;
288              }
289              CTRlen = 0;
290           }
291 
292           /* if we encrypt we add the bytes to the MAC first */
293           if (direction == CCM_ENCRYPT) {
294              b     = pt[y];
295              ct[y] = b ^ CTRPAD[CTRlen++];
296           } else {
297              b     = ct[y] ^ CTRPAD[CTRlen++];
298              pt[y] = b;
299           }
300 
301           if (x == 16) {
302              if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
303                 goto error;
304              }
305              x = 0;
306           }
307           PAD[x++] ^= b;
308       }
309 
310       if (x != 0) {
311          if ((err = cipher_descriptor[cipher]->ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
312             goto error;
313          }
314       }
315    }
316 
317    /* setup CTR for the TAG (zero the count) */
318    for (y = 15; y > 15 - L; y--) {
319       ctr[y] = 0x00;
320    }
321    if ((err = cipher_descriptor[cipher]->ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
322       goto error;
323    }
324 
325    if (skey != uskey) {
326       cipher_descriptor[cipher]->done(skey);
327 #ifdef LTC_CLEAN_STACK
328       zeromem(skey,   sizeof(*skey));
329 #endif
330    }
331 
332    if (direction == CCM_ENCRYPT) {
333       /* store the TAG */
334       for (x = 0; x < 16 && x < *taglen; x++) {
335           tag[x] = PAD[x] ^ CTRPAD[x];
336       }
337       *taglen = x;
338    } else { /* direction == CCM_DECRYPT */
339       /* decrypt the tag */
340       for (x = 0; x < 16 && x < *taglen; x++) {
341          ptTag[x] = tag[x] ^ CTRPAD[x];
342       }
343       *taglen = x;
344 
345       /* check validity of the decrypted tag against the computed PAD (in constant time) */
346       /* HACK: the boolean value of XMEM_NEQ becomes either 0 (CRYPT_OK) or 1 (CRYPT_ERR).
347        *       there should be a better way of setting the correct error code in constant
348        *       time.
349        */
350       err = XMEM_NEQ(ptTag, PAD, *taglen);
351 
352       /* Zero the plaintext if the tag was invalid (in constant time) */
353       if (ptlen > 0) {
354          copy_or_zeromem(pt, pt_real, ptlen, err);
355       }
356    }
357 
358 #ifdef LTC_CLEAN_STACK
359    zeromem(PAD,    sizeof(PAD));
360    zeromem(CTRPAD, sizeof(CTRPAD));
361    if (pt_work != NULL) {
362      zeromem(pt_work, ptlen);
363    }
364 #endif
365 error:
366    if (pt_work) {
367       XFREE(pt_work);
368    }
369    if (skey != uskey) {
370       XFREE(skey);
371    }
372 
373    return err;
374 }
375 
376 #endif
377