xref: /freebsd/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c (revision dece5b05f577fc4c6b89af7ad48dfbdf7cc4945c)
1 /*
2  * Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 /* Dispatch functions for ccm mode */
11 
12 #include <openssl/proverr.h>
13 #include "prov/ciphercommon.h"
14 #include "prov/ciphercommon_ccm.h"
15 #include "prov/providercommon.h"
16 #include "fips/fipsindicator.h"
17 
18 static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out,
19     size_t *padlen, const unsigned char *in,
20     size_t len);
21 
22 static int ccm_tls_init(PROV_CCM_CTX *ctx, unsigned char *aad, size_t alen)
23 {
24     size_t len;
25 
26     if (!ossl_prov_is_running() || alen != EVP_AEAD_TLS1_AAD_LEN)
27         return 0;
28 
29     /* Save the aad for later use. */
30     memcpy(ctx->buf, aad, alen);
31     ctx->tls_aad_len = alen;
32 
33     len = ctx->buf[alen - 2] << 8 | ctx->buf[alen - 1];
34     if (len < EVP_CCM_TLS_EXPLICIT_IV_LEN)
35         return 0;
36 
37     /* Correct length for explicit iv. */
38     len -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
39 
40     if (!ctx->enc) {
41         if (len < ctx->m)
42             return 0;
43         /* Correct length for tag. */
44         len -= ctx->m;
45     }
46     ctx->buf[alen - 2] = (unsigned char)(len >> 8);
47     ctx->buf[alen - 1] = (unsigned char)(len & 0xff);
48 
49     /* Extra padding: tag appended to record. */
50     return ctx->m;
51 }
52 
53 static int ccm_tls_iv_set_fixed(PROV_CCM_CTX *ctx, unsigned char *fixed,
54     size_t flen)
55 {
56     if (flen != EVP_CCM_TLS_FIXED_IV_LEN)
57         return 0;
58 
59     /* Copy to first part of the iv. */
60     memcpy(ctx->iv, fixed, flen);
61     return 1;
62 }
63 
64 static size_t ccm_get_ivlen(PROV_CCM_CTX *ctx)
65 {
66     return 15 - ctx->l;
67 }
68 
69 int ossl_ccm_set_ctx_params(void *vctx, const OSSL_PARAM params[])
70 {
71     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
72     const OSSL_PARAM *p;
73     size_t sz;
74 
75     if (ossl_param_is_empty(params))
76         return 1;
77 
78     p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
79     if (p != NULL) {
80         if (p->data_type != OSSL_PARAM_OCTET_STRING) {
81             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
82             return 0;
83         }
84         if ((p->data_size & 1) || (p->data_size < 4) || p->data_size > 16) {
85             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
86             return 0;
87         }
88 
89         if (p->data != NULL) {
90             if (ctx->enc) {
91                 ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED);
92                 return 0;
93             }
94             memcpy(ctx->buf, p->data, p->data_size);
95             ctx->tag_set = 1;
96         }
97         ctx->m = p->data_size;
98     }
99 
100     p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN);
101     if (p != NULL) {
102         size_t ivlen;
103 
104         if (!OSSL_PARAM_get_size_t(p, &sz)) {
105             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
106             return 0;
107         }
108         ivlen = 15 - sz;
109         if (ivlen < 2 || ivlen > 8) {
110             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
111             return 0;
112         }
113         if (ctx->l != ivlen) {
114             ctx->l = ivlen;
115             ctx->iv_set = 0;
116         }
117     }
118 
119     p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
120     if (p != NULL) {
121         if (p->data_type != OSSL_PARAM_OCTET_STRING) {
122             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
123             return 0;
124         }
125         sz = ccm_tls_init(ctx, p->data, p->data_size);
126         if (sz == 0) {
127             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
128             return 0;
129         }
130         ctx->tls_aad_pad_sz = sz;
131     }
132 
133     p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED);
134     if (p != NULL) {
135         if (p->data_type != OSSL_PARAM_OCTET_STRING) {
136             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
137             return 0;
138         }
139         if (ccm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) {
140             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
141             return 0;
142         }
143     }
144 
145     return 1;
146 }
147 
148 int ossl_ccm_get_ctx_params(void *vctx, OSSL_PARAM params[])
149 {
150     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
151     OSSL_PARAM *p;
152 
153     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
154     if (p != NULL && !OSSL_PARAM_set_size_t(p, ccm_get_ivlen(ctx))) {
155         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
156         return 0;
157     }
158 
159     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
160     if (p != NULL) {
161         size_t m = ctx->m;
162 
163         if (!OSSL_PARAM_set_size_t(p, m)) {
164             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
165             return 0;
166         }
167     }
168 
169     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
170     if (p != NULL) {
171         if (ccm_get_ivlen(ctx) > p->data_size) {
172             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
173             return 0;
174         }
175         if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size)
176             && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) {
177             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
178             return 0;
179         }
180     }
181 
182     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
183     if (p != NULL) {
184         if (ccm_get_ivlen(ctx) > p->data_size) {
185             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
186             return 0;
187         }
188         if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size)
189             && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) {
190             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
191             return 0;
192         }
193     }
194 
195     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
196     if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) {
197         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
198         return 0;
199     }
200 
201     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
202     if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
203         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
204         return 0;
205     }
206 
207     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
208     if (p != NULL) {
209         if (!ctx->enc || !ctx->tag_set) {
210             ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET);
211             return 0;
212         }
213         if (p->data_type != OSSL_PARAM_OCTET_STRING) {
214             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
215             return 0;
216         }
217         if (!ctx->hw->gettag(ctx, p->data, p->data_size))
218             return 0;
219         ctx->tag_set = 0;
220         ctx->iv_set = 0;
221         ctx->len_set = 0;
222     }
223     return OSSL_FIPS_IND_GET_CTX_PARAM_APPROVED(ctx, params);
224 }
225 
226 static int ccm_init(void *vctx, const unsigned char *key, size_t keylen,
227     const unsigned char *iv, size_t ivlen,
228     const OSSL_PARAM params[], int enc)
229 {
230     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
231 
232     if (!ossl_prov_is_running())
233         return 0;
234 
235     ctx->enc = enc;
236 
237     if (iv != NULL) {
238         if (ivlen != ccm_get_ivlen(ctx)) {
239             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
240             return 0;
241         }
242         memcpy(ctx->iv, iv, ivlen);
243         ctx->iv_set = 1;
244     }
245     if (key != NULL) {
246         if (keylen != ctx->keylen) {
247             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
248             return 0;
249         }
250         if (!ctx->hw->setkey(ctx, key, keylen))
251             return 0;
252     }
253     return ossl_ccm_set_ctx_params(ctx, params);
254 }
255 
256 int ossl_ccm_einit(void *vctx, const unsigned char *key, size_t keylen,
257     const unsigned char *iv, size_t ivlen,
258     const OSSL_PARAM params[])
259 {
260     return ccm_init(vctx, key, keylen, iv, ivlen, params, 1);
261 }
262 
263 int ossl_ccm_dinit(void *vctx, const unsigned char *key, size_t keylen,
264     const unsigned char *iv, size_t ivlen,
265     const OSSL_PARAM params[])
266 {
267     return ccm_init(vctx, key, keylen, iv, ivlen, params, 0);
268 }
269 
270 int ossl_ccm_stream_update(void *vctx, unsigned char *out, size_t *outl,
271     size_t outsize, const unsigned char *in,
272     size_t inl)
273 {
274     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
275 
276     if (outsize < inl) {
277         ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
278         return 0;
279     }
280 
281     if (!ccm_cipher_internal(ctx, out, outl, in, inl)) {
282         ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
283         return 0;
284     }
285     return 1;
286 }
287 
288 int ossl_ccm_stream_final(void *vctx, unsigned char *out, size_t *outl,
289     size_t outsize)
290 {
291     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
292     unsigned char dummy_in = 0, dummy_out = 0;
293 
294     if (!ossl_prov_is_running())
295         return 0;
296 
297     /*
298      * Encryption sets tag_set after processing the payload, while successful
299      * decryption clears iv_set. Use those transitions to avoid processing an
300      * operation twice.
301      */
302     if (!ctx->key_set
303         || (ctx->iv_set && (!ctx->enc || !ctx->tag_set)
304             && ccm_cipher_internal(ctx, &dummy_out, outl, &dummy_in, 0) <= 0))
305         return 0;
306 
307     *outl = 0;
308     return 1;
309 }
310 
311 int ossl_ccm_cipher(void *vctx, unsigned char *out, size_t *outl, size_t outsize,
312     const unsigned char *in, size_t inl)
313 {
314     PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
315 
316     if (!ossl_prov_is_running())
317         return 0;
318 
319     if (in == NULL)
320         return ossl_ccm_stream_final(vctx, out, outl, outsize);
321 
322     if (outsize < inl) {
323         ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
324         return 0;
325     }
326 
327     if (ccm_cipher_internal(ctx, out, outl, in, inl) <= 0)
328         return 0;
329 
330     *outl = inl;
331     return 1;
332 }
333 
334 /* Copy the buffered iv */
335 static int ccm_set_iv(PROV_CCM_CTX *ctx, size_t mlen)
336 {
337     const PROV_CCM_HW *hw = ctx->hw;
338 
339     if (!hw->setiv(ctx, ctx->iv, ccm_get_ivlen(ctx), mlen))
340         return 0;
341     ctx->len_set = 1;
342     return 1;
343 }
344 
345 static int ccm_tls_cipher(PROV_CCM_CTX *ctx,
346     unsigned char *out, size_t *padlen,
347     const unsigned char *in, size_t len)
348 {
349     int rv = 0;
350     size_t olen = 0;
351 
352     if (!ossl_prov_is_running())
353         goto err;
354 
355     /* Encrypt/decrypt must be performed in place */
356     if (in == NULL || out != in || len < EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m)
357         goto err;
358 
359     /* If encrypting set explicit IV from sequence number (start of AAD) */
360     if (ctx->enc)
361         memcpy(out, ctx->buf, EVP_CCM_TLS_EXPLICIT_IV_LEN);
362     /* Get rest of IV from explicit IV */
363     memcpy(ctx->iv + EVP_CCM_TLS_FIXED_IV_LEN, in, EVP_CCM_TLS_EXPLICIT_IV_LEN);
364     /* Correct length value */
365     len -= EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m;
366     if (!ccm_set_iv(ctx, len))
367         goto err;
368 
369     /* Use saved AAD */
370     if (!ctx->hw->setaad(ctx, ctx->buf, ctx->tls_aad_len))
371         goto err;
372 
373     /* Fix buffer to point to payload */
374     in += EVP_CCM_TLS_EXPLICIT_IV_LEN;
375     out += EVP_CCM_TLS_EXPLICIT_IV_LEN;
376     if (ctx->enc) {
377         if (!ctx->hw->auth_encrypt(ctx, in, out, len, out + len, ctx->m))
378             goto err;
379         olen = len + EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m;
380     } else {
381         if (!ctx->hw->auth_decrypt(ctx, in, out, len,
382                 (unsigned char *)in + len, ctx->m))
383             goto err;
384         olen = len;
385     }
386     rv = 1;
387 err:
388     *padlen = olen;
389     return rv;
390 }
391 
392 static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out,
393     size_t *padlen, const unsigned char *in,
394     size_t len)
395 {
396     int rv = 0;
397     size_t olen = 0;
398     const PROV_CCM_HW *hw = ctx->hw;
399 
400     /* If no key set, return error */
401     if (!ctx->key_set)
402         return 0;
403 
404     if (ctx->tls_aad_len != UNINITIALISED_SIZET)
405         return ccm_tls_cipher(ctx, out, padlen, in, len);
406 
407     /* EVP_*Final() doesn't return any data */
408     if (in == NULL && out != NULL)
409         goto finish;
410 
411     if (!ctx->iv_set)
412         goto err;
413 
414     if (out == NULL) {
415         if (in == NULL) {
416             if (!ccm_set_iv(ctx, len))
417                 goto err;
418         } else {
419             /* If we have AAD, we need a message length */
420             if (!ctx->len_set && len)
421                 goto err;
422             if (!hw->setaad(ctx, in, len))
423                 goto err;
424         }
425     } else {
426         /* If not set length yet do it */
427         if (!ctx->len_set && !ccm_set_iv(ctx, len))
428             goto err;
429 
430         if (ctx->enc) {
431             if (!hw->auth_encrypt(ctx, in, out, len, NULL, 0))
432                 goto err;
433             ctx->tag_set = 1;
434         } else {
435             /* The tag must be set before actually decrypting data */
436             if (!ctx->tag_set)
437                 goto err;
438 
439             if (!hw->auth_decrypt(ctx, in, out, len, ctx->buf, ctx->m))
440                 goto err;
441             /* Finished - reset flags so calling this method again will fail */
442             ctx->iv_set = 0;
443             ctx->tag_set = 0;
444             ctx->len_set = 0;
445         }
446     }
447     olen = len;
448 finish:
449     rv = 1;
450 err:
451     *padlen = olen;
452     return rv;
453 }
454 
455 void ossl_ccm_initctx(PROV_CCM_CTX *ctx, size_t keybits, const PROV_CCM_HW *hw)
456 {
457     ctx->keylen = keybits / 8;
458     ctx->key_set = 0;
459     ctx->iv_set = 0;
460     ctx->tag_set = 0;
461     ctx->len_set = 0;
462     ctx->l = 8;
463     ctx->m = 12;
464     ctx->tls_aad_len = UNINITIALISED_SIZET;
465     ctx->hw = hw;
466 }
467