diff options
Diffstat (limited to 'providers/implementations/ciphers')
7 files changed, 1119 insertions, 0 deletions
diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index fb2b53e58a..7f0b76c01b 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -19,6 +19,8 @@ $SM4_GOAL=../../libimplementations.a $RC4_GOAL=../../libimplementations.a $RC5_GOAL=../../libimplementations.a $RC2_GOAL=../../libimplementations.a +$CHACHA_GOAL=../../libimplementations.a +$CHACHAPOLY_GOAL=../../libimplementations.a IF[{- !$disabled{des} -}] SOURCE[$TDES_1_GOAL]=cipher_tdes.c cipher_tdes_hw.c @@ -100,3 +102,13 @@ IF[{- !$disabled{rc2} -}] SOURCE[$RC2_GOAL]=\ cipher_rc2.c cipher_rc2_hw.c ENDIF + +IF[{- !$disabled{chacha} -}] + SOURCE[$CHACHA_GOAL]=\ + cipher_chacha20.c cipher_chacha20_hw.c + IF[{- !$disabled{poly1305} -}] + SOURCE[$CHACHAPOLY_GOAL]=\ + cipher_chacha20_poly1305.c cipher_chacha20_poly1305_hw.c + ENDIF +ENDIF + diff --git a/providers/implementations/ciphers/cipher_chacha20.c b/providers/implementations/ciphers/cipher_chacha20.c new file mode 100644 index 0000000000..7cc57e7043 --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20.c @@ -0,0 +1,187 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Dispatch functions for chacha20 cipher */ + +#include "cipher_chacha20.h" +#include "prov/implementations.h" +#include "prov/providercommonerr.h" + +#define CHACHA20_KEYLEN (CHACHA_KEY_SIZE) +#define CHACHA20_BLKLEN (1) +#define CHACHA20_IVLEN (CHACHA_CTR_SIZE) +/* TODO(3.0) Figure out what flags are required */ +#define CHACHA20_FLAGS (EVP_CIPH_CUSTOM_IV | EVP_CIPH_ALWAYS_CALL_INIT) + +static OSSL_OP_cipher_newctx_fn chacha20_newctx; +static OSSL_OP_cipher_freectx_fn chacha20_freectx; +static OSSL_OP_cipher_get_params_fn chacha20_get_params; +static OSSL_OP_cipher_get_ctx_params_fn chacha20_get_ctx_params; +static OSSL_OP_cipher_set_ctx_params_fn chacha20_set_ctx_params; +static OSSL_OP_cipher_gettable_ctx_params_fn chacha20_gettable_ctx_params; +static OSSL_OP_cipher_settable_ctx_params_fn chacha20_settable_ctx_params; +#define chacha20_cipher cipher_generic_cipher +#define chacha20_update cipher_generic_stream_update +#define chacha20_final cipher_generic_stream_final +#define chacha20_gettable_params cipher_generic_gettable_params + +void chacha20_initctx(PROV_CHACHA20_CTX *ctx) +{ + cipher_generic_initkey(ctx, CHACHA20_KEYLEN * 8, + CHACHA20_BLKLEN * 8, + CHACHA20_IVLEN * 8, + 0, CHACHA20_FLAGS, + PROV_CIPHER_HW_chacha20(CHACHA20_KEYLEN * 8), + NULL); +} + +static void *chacha20_newctx(void *provctx) +{ + PROV_CHACHA20_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) + chacha20_initctx(ctx); + return ctx; +} + +static void chacha20_freectx(void *vctx) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx; + + if (ctx != NULL) { + OPENSSL_clear_free(ctx, sizeof(ctx)); + } +} + +static int chacha20_get_params(OSSL_PARAM params[]) +{ + return cipher_generic_get_params(params, 0, CHACHA20_FLAGS, + CHACHA20_KEYLEN * 8, + CHACHA20_BLKLEN * 8, + CHACHA20_IVLEN * 8); +} + +static int chacha20_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_IVLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_KEYLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + return 1; +} + +static const OSSL_PARAM chacha20_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *chacha20_gettable_ctx_params(void) +{ + return chacha20_known_gettable_ctx_params; +} + +static int chacha20_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + size_t len; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_KEYLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_IVLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + return 1; +} + +static const OSSL_PARAM chacha20_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *chacha20_settable_ctx_params(void) +{ + return chacha20_known_settable_ctx_params; +} + +int chacha20_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen) +{ + int ret; + + ret= cipher_generic_einit(vctx, key, keylen, iv, ivlen); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw; + + hw->initiv(ctx); + } + return ret; +} + +int chacha20_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen) +{ + int ret; + + ret= cipher_generic_dinit(vctx, key, keylen, iv, ivlen); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw; + + hw->initiv(ctx); + } + return ret; +} + +/* chacha20_functions */ +const OSSL_DISPATCH chacha20_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_freectx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))chacha20_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))chacha20_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_update }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_cipher}, + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))chacha20_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,(void (*)(void))chacha20_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))chacha20_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))chacha20_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_settable_ctx_params }, + { 0, NULL } +}; + diff --git a/providers/implementations/ciphers/cipher_chacha20.h b/providers/implementations/ciphers/cipher_chacha20.h new file mode 100644 index 0000000000..f934c0033f --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20.h @@ -0,0 +1,34 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "include/crypto/chacha.h" +#include "prov/ciphercommon.h" + +typedef struct { + PROV_CIPHER_CTX base; /* must be first */ + union { + OSSL_UNION_ALIGN; + unsigned int d[CHACHA_KEY_SIZE / 4]; + } key; + unsigned int counter[CHACHA_CTR_SIZE / 4]; + unsigned char buf[CHACHA_BLK_SIZE]; + unsigned int partial_len; +} PROV_CHACHA20_CTX; + +typedef struct prov_cipher_hw_chacha20_st { + PROV_CIPHER_HW base; /* must be first */ + int (*initiv)(PROV_CIPHER_CTX *ctx); + +} PROV_CIPHER_HW_CHACHA20; + +const PROV_CIPHER_HW *PROV_CIPHER_HW_chacha20(size_t keybits); + +OSSL_OP_cipher_encrypt_init_fn chacha20_einit; +OSSL_OP_cipher_decrypt_init_fn chacha20_dinit; +void chacha20_initctx(PROV_CHACHA20_CTX *ctx); diff --git a/providers/implementations/ciphers/cipher_chacha20_hw.c b/providers/implementations/ciphers/cipher_chacha20_hw.c new file mode 100644 index 0000000000..dce0a6c11c --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20_hw.c @@ -0,0 +1,121 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* chacha20 cipher implementation */ + +#include "cipher_chacha20.h" + +static int chacha20_initkey(PROV_CIPHER_CTX *bctx, const uint8_t *key, + size_t keylen) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int i; + + if (key != NULL) { + for (i = 0; i < CHACHA_KEY_SIZE; i += 4) + ctx->key.d[i / 4] = CHACHA_U8TOU32(key + i); + } + ctx->partial_len = 0; + return 1; +} + +static int chacha20_initiv(PROV_CIPHER_CTX *bctx) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int i; + + if (bctx->iv_set) { + for (i = 0; i < CHACHA_CTR_SIZE; i += 4) + ctx->counter[i / 4] = CHACHA_U8TOU32(bctx->oiv + i); + } + return 1; +} + +static int chacha20_cipher(PROV_CIPHER_CTX *bctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int n, rem, ctr32; + + n = ctx->partial_len; + if (n > 0) { + while (inl > 0 && n < CHACHA_BLK_SIZE) { + *out++ = *in++ ^ ctx->buf[n++]; + inl--; + } + ctx->partial_len = n; + + if (inl == 0) + return 1; + + if (n == CHACHA_BLK_SIZE) { + ctx->partial_len = 0; + ctx->counter[0]++; + if (ctx->counter[0] == 0) + ctx->counter[1]++; + } + } + + rem = (unsigned int)(inl % CHACHA_BLK_SIZE); + inl -= rem; + ctr32 = ctx->counter[0]; + while (inl >= CHACHA_BLK_SIZE) { + size_t blocks = inl / CHACHA_BLK_SIZE; + + /* + * 1<<28 is just a not-so-small yet not-so-large number... + * Below condition is practically never met, but it has to + * be checked for code correctness. + */ + if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28)) + blocks = (1U << 28); + + /* + * As ChaCha20_ctr32 operates on 32-bit counter, caller + * has to handle overflow. 'if' below detects the + * overflow, which is then handled by limiting the + * amount of blocks to the exact overflow point... + */ + ctr32 += (unsigned int)blocks; + if (ctr32 < blocks) { + blocks -= ctr32; + ctr32 = 0; + } + blocks *= CHACHA_BLK_SIZE; + ChaCha20_ctr32(out, in, blocks, ctx->key.d, ctx->counter); + inl -= blocks; + in += blocks; + out += blocks; + + ctx->counter[0] = ctr32; + if (ctr32 == 0) ctx->counter[1]++; + } + + if (rem > 0) { + memset(ctx->buf, 0, sizeof(ctx->buf)); + ChaCha20_ctr32(ctx->buf, ctx->buf, CHACHA_BLK_SIZE, + ctx->key.d, ctx->counter); + for (n = 0; n < rem; n++) + out[n] = in[n] ^ ctx->buf[n]; + ctx->partial_len = rem; + } + + return 1; +} + +static const PROV_CIPHER_HW_CHACHA20 chacha20_hw = { + { chacha20_initkey, chacha20_cipher }, + chacha20_initiv +}; + +const PROV_CIPHER_HW *PROV_CIPHER_HW_chacha20(size_t keybits) +{ + return (PROV_CIPHER_HW *)&chacha20_hw; +} + diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/providers/implementations/ciphers/cipher_chacha20_poly1305.c new file mode 100644 index 0000000000..cc702cb8fc --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.c @@ -0,0 +1,314 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Dispatch functions for chacha20_poly1305 cipher */ + +#include "cipher_chacha20_poly1305.h" +#include "prov/implementations.h" +#include "prov/providercommonerr.h" + + +#define CHACHA20_POLY1305_KEYLEN CHACHA_KEY_SIZE +#define CHACHA20_POLY1305_BLKLEN 1 +#define CHACHA20_POLY1305_MAX_IVLEN 12 +#define CHACHA20_POLY1305_MODE 0 +/* TODO(3.0) Figure out what flags are required */ +#define CHACHA20_POLY1305_FLAGS (EVP_CIPH_FLAG_AEAD_CIPHER \ + | EVP_CIPH_ALWAYS_CALL_INIT \ + | EVP_CIPH_CTRL_INIT \ + | EVP_CIPH_CUSTOM_COPY \ + | EVP_CIPH_FLAG_CUSTOM_CIPHER \ + | EVP_CIPH_CUSTOM_IV \ + | EVP_CIPH_CUSTOM_IV_LENGTH) + +static OSSL_OP_cipher_newctx_fn chacha20_poly1305_newctx; +static OSSL_OP_cipher_freectx_fn chacha20_poly1305_freectx; +static OSSL_OP_cipher_encrypt_init_fn chacha20_poly1305_einit; +static OSSL_OP_cipher_decrypt_init_fn chacha20_poly1305_dinit; +static OSSL_OP_cipher_get_params_fn chacha20_poly1305_get_params; +static OSSL_OP_cipher_get_ctx_params_fn chacha20_poly1305_get_ctx_params; +static OSSL_OP_cipher_set_ctx_params_fn chacha20_poly1305_set_ctx_params; +static OSSL_OP_cipher_cipher_fn chacha20_poly1305_cipher; +static OSSL_OP_cipher_final_fn chacha20_poly1305_final; +static OSSL_OP_cipher_gettable_ctx_params_fn chacha20_poly1305_gettable_ctx_params; +#define chacha20_poly1305_settable_ctx_params cipher_aead_settable_ctx_params +#define chacha20_poly1305_gettable_params cipher_generic_gettable_params +#define chacha20_poly1305_update chacha20_poly1305_cipher + +static void *chacha20_poly1305_newctx(void *provctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + cipher_generic_initkey(&ctx->base, CHACHA20_POLY1305_KEYLEN * 8, + CHACHA20_POLY1305_BLKLEN * 8, + CHACHA20_POLY1305_IVLEN * 8, + CHACHA20_POLY1305_MODE, + CHACHA20_POLY1305_FLAGS, + PROV_CIPHER_HW_chacha20_poly1305( + CHACHA20_POLY1305_KEYLEN * 8), + NULL); + ctx->nonce_len = CHACHA20_POLY1305_IVLEN; + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + chacha20_initctx(&ctx->chacha); + } + return ctx; +} + +static void chacha20_poly1305_freectx(void *vctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + + if (ctx != NULL) + OPENSSL_clear_free(ctx, sizeof(ctx)); +} + +static int chacha20_poly1305_get_params(OSSL_PARAM params[]) +{ + return cipher_generic_get_params(params, 0, CHACHA20_POLY1305_FLAGS, + CHACHA20_POLY1305_KEYLEN * 8, + CHACHA20_POLY1305_BLKLEN * 8, + CHACHA20_POLY1305_IVLEN * 8); +} + +static int chacha20_poly1305_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_set_size_t(p, ctx->nonce_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_POLY1305_KEYLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tag_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + if (!ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOTSET); + return 0; + } + if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAGLEN); + return 0; + } + memcpy(p->data, ctx->tag, p->data_size); + } + + return 1; +} + +static const OSSL_PARAM chacha20_poly1305_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), + OSSL_PARAM_END +}; +static const OSSL_PARAM *chacha20_poly1305_gettable_ctx_params(void) +{ + return chacha20_poly1305_known_gettable_ctx_params; +} + +static int chacha20_poly1305_set_ctx_params(void *vctx, + const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + size_t len; + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->base.hw; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_POLY1305_KEYLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len == 0 || len > CHACHA20_POLY1305_MAX_IVLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + ctx->nonce_len = len; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAGLEN); + return 0; + } + if (p->data != NULL) { + if (ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); + return 0; + } + memcpy(ctx->tag, p->data, p->data_size); + } + ctx->tag_len = p->data_size; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + len = hw->tls_init(&ctx->base, p->data, p->data_size); + if (len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return 0; + } + ctx->tls_aad_pad_sz = len; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (hw->tls_iv_set_fixed(&ctx->base, p->data, p->data_size) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IVLEN); + return 0; + } + } + /* ignore OSSL_CIPHER_PARAM_AEAD_MAC_KEY */ + return 1; +} + +static int chacha20_poly1305_einit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen) +{ + int ret; + + ret = cipher_generic_einit(vctx, key, keylen, iv, ivlen); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + hw->initiv(ctx); + } + return ret; +} + +static int chacha20_poly1305_dinit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen) +{ + int ret; + + ret = cipher_generic_dinit(vctx, key, keylen, iv, ivlen); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + hw->initiv(ctx); + } + return ret; +} + +static int chacha20_poly1305_cipher(void *vctx, unsigned char *out, + size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!hw->aead_cipher(ctx, out, outl, in, inl)) + return 0; + + *outl = inl; + return 1; +} + +static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + if (hw->aead_cipher(ctx, out, outl, NULL, 0) <= 0) + return 0; + + *outl = 0; + return 1; +} + +/* chacha20_poly1305_functions */ +const OSSL_DISPATCH chacha20_poly1305_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_poly1305_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_poly1305_freectx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))chacha20_poly1305_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))chacha20_poly1305_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_poly1305_update }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_poly1305_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_poly1305_cipher }, + { OSSL_FUNC_CIPHER_GET_PARAMS, + (void (*)(void))chacha20_poly1305_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, + (void (*)(void))chacha20_poly1305_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_settable_ctx_params }, + { 0, NULL } +}; + diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.h b/providers/implementations/ciphers/cipher_chacha20_poly1305.h new file mode 100644 index 0000000000..00b173d1ed --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.h @@ -0,0 +1,43 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Dispatch functions for chacha20_poly1305 cipher */ + +#include "include/crypto/poly1305.h" +#include "cipher_chacha20.h" + +#define NO_TLS_PAYLOAD_LENGTH ((size_t)-1) +#define CHACHA20_POLY1305_IVLEN 12 + +typedef struct { + PROV_CIPHER_CTX base; /* must be first */ + PROV_CHACHA20_CTX chacha; + POLY1305 poly1305; + unsigned int nonce[12 / 4]; + unsigned char tag[POLY1305_BLOCK_SIZE]; + unsigned char tls_aad[POLY1305_BLOCK_SIZE]; + struct { uint64_t aad, text; } len; + unsigned int aad : 1; + unsigned int mac_inited : 1; + size_t tag_len, nonce_len; + size_t tls_payload_length; + size_t tls_aad_pad_sz; +} PROV_CHACHA20_POLY1305_CTX; + +typedef struct prov_cipher_hw_chacha_aead_st { + PROV_CIPHER_HW base; /* must be first */ + int (*aead_cipher)(PROV_CIPHER_CTX *dat, unsigned char *out, size_t *outl, + const unsigned char *in, size_t len); + int (*initiv)(PROV_CIPHER_CTX *ctx); + int (*tls_init)(PROV_CIPHER_CTX *ctx, unsigned char *aad, size_t alen); + int (*tls_iv_set_fixed)(PROV_CIPHER_CTX *ctx, unsigned char *fixed, + size_t flen); +} PROV_CIPHER_HW_CHACHA20_POLY1305; + +const PROV_CIPHER_HW *PROV_CIPHER_HW_chacha20_poly1305(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c new file mode 100644 index 0000000000..51e5275b7e --- /dev/null +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c @@ -0,0 +1,408 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* chacha20_poly1305 cipher implementation */ + +#include "cipher_chacha20_poly1305.h" + +static int chacha_poly1305_tls_init(PROV_CIPHER_CTX *bctx, + unsigned char *aad, size_t alen) +{ + unsigned int len; + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + if (alen != EVP_AEAD_TLS1_AAD_LEN) + return 0; + + memcpy(ctx->tls_aad, aad, EVP_AEAD_TLS1_AAD_LEN); + len = aad[EVP_AEAD_TLS1_AAD_LEN - 2] << 8 | aad[EVP_AEAD_TLS1_AAD_LEN - 1]; + aad = ctx->tls_aad; + if (!bctx->enc) { + if (len < POLY1305_BLOCK_SIZE) + return 0; + len -= POLY1305_BLOCK_SIZE; /* discount attached tag */ + aad[EVP_AEAD_TLS1_AAD_LEN - 2] = (unsigned char)(len >> 8); + aad[EVP_AEAD_TLS1_AAD_LEN - 1] = (unsigned char)len; + } + ctx->tls_payload_length = len; + + /* merge record sequence number as per RFC7905 */ + ctx->chacha.counter[1] = ctx->nonce[0]; + ctx->chacha.counter[2] = ctx->nonce[1] ^ CHACHA_U8TOU32(aad); + ctx->chacha.counter[3] = ctx->nonce[2] ^ CHACHA_U8TOU32(aad+4); + ctx->mac_inited = 0; + + return POLY1305_BLOCK_SIZE; /* tag length */ +} + +static int chacha_poly1305_tls_iv_set_fixed(PROV_CIPHER_CTX *bctx, + unsigned char *fixed, size_t flen) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + if (flen != CHACHA20_POLY1305_IVLEN) + return 0; + ctx->nonce[0] = ctx->chacha.counter[1] = CHACHA_U8TOU32(fixed); + ctx->nonce[1] = ctx->chacha.counter[2] = CHACHA_U8TOU32(fixed + 4); + ctx->nonce[2] = ctx->chacha.counter[3] = CHACHA_U8TOU32(fixed + 8); + return 1; +} + + +static int chacha20_poly1305_initkey(PROV_CIPHER_CTX *bctx, + const unsigned char *key, size_t keylen) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + ctx->len.aad = 0; + ctx->len.text = 0; + ctx->aad = 0; + ctx->mac_inited = 0; + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + + if (bctx->enc) + return chacha20_einit(&ctx->chacha, key, keylen, NULL, 0); + else + return chacha20_dinit(&ctx->chacha, key, keylen, NULL, 0); +} + +static int chacha20_poly1305_initiv(PROV_CIPHER_CTX *bctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + unsigned char tempiv[CHACHA_CTR_SIZE] = { 0 }; + int ret = 1; + + /* pad on the left */ + if (ctx->nonce_len <= CHACHA_CTR_SIZE) { + memcpy(tempiv + CHACHA_CTR_SIZE - ctx->nonce_len, bctx->oiv, + ctx->nonce_len); + + if (bctx->enc) + ret = chacha20_einit(&ctx->chacha, NULL, 0, tempiv, sizeof(tempiv)); + else + ret = chacha20_dinit(&ctx->chacha, NULL, 0, tempiv, sizeof(tempiv)); + ctx->nonce[0] = ctx->chacha.counter[1]; + ctx->nonce[1] = ctx->chacha.counter[2]; + ctx->nonce[2] = ctx->chacha.counter[3]; + bctx->iv_set = 1; + } + return ret; +} + +#if !defined(OPENSSL_SMALL_FOOTPRINT) + +# if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_AMD64) || defined(_M_X64)) +# define XOR128_HELPERS +void *xor128_encrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +void *xor128_decrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +static const unsigned char zero[4 * CHACHA_BLK_SIZE] = { 0 }; +# else +static const unsigned char zero[2 * CHACHA_BLK_SIZE] = { 0 }; +# endif + +static int chacha20_poly1305_tls_cipher(PROV_CIPHER_CTX *bctx, + unsigned char *out, + size_t *out_padlen, + const unsigned char *in, size_t len) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + POLY1305 *poly = &ctx->poly1305; + size_t tail, tohash_len, buf_len, plen = ctx->tls_payload_length; + unsigned char *buf, *tohash, *ctr, storage[sizeof(zero) + 32]; + + const union { + long one; + char little; + } is_endian = { 1 }; + + if (len != plen + POLY1305_BLOCK_SIZE) + return 0; + + buf = storage + ((0 - (size_t)storage) & 15); /* align */ + ctr = buf + CHACHA_BLK_SIZE; + tohash = buf + CHACHA_BLK_SIZE - POLY1305_BLOCK_SIZE; + +# ifdef XOR128_HELPERS + if (plen <= 3 * CHACHA_BLK_SIZE) { + ctx->chacha.counter[0] = 0; + buf_len = (plen + 2 * CHACHA_BLK_SIZE - 1) & (0 - CHACHA_BLK_SIZE); + ChaCha20_ctr32(buf, zero, buf_len, ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, buf); + ctx->chacha.partial_len = 0; + memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE); + tohash_len = POLY1305_BLOCK_SIZE; + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->len.text = plen; + + if (plen) { + if (ctx->enc) + ctr = xor128_encrypt_n_pad(out, in, ctr, plen); + else + ctr = xor128_decrypt_n_pad(out, in, ctr, plen); + + in += plen; + out += plen; + tohash_len = (size_t)(ctr - tohash); + } + } +# else + if (plen <= CHACHA_BLK_SIZE) { + size_t i; + + ctx->chacha.counter[0] = 0; + ChaCha20_ctr32(buf, zero, (buf_len = 2 * CHACHA_BLK_SIZE), + ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, buf); + ctx->chacha.partial_len = 0; + memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE); + tohash_len = POLY1305_BLOCK_SIZE; + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->len.text = plen; + + if (bctx->enc) { + for (i = 0; i < plen; i++) + out[i] = ctr[i] ^= in[i]; + } else { + for (i = 0; i < plen; i++) { + unsigned char c = in[i]; + + out[i] = ctr[i] ^ c; + ctr[i] = c; + } + } |