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