From 4d039c645d00d539a40cf52ff3e7e776b8ae792a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 8 Apr 2020 18:09:14 +0300 Subject: [PATCH 57/87] evp: support kuznyechik kexp15 keywrap algorithm Add support for kuznyechik kexp15 key wrapping algorithm (draft-smyshlyaev-tls12-gost-suites, Section 8.2.1). Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/evp/c_all.c | 1 + src/lib/libcrypto/evp/e_kuznyechik.c | 143 +++++++++++++++++++++++++++ src/lib/libcrypto/evp/evp.h | 1 + 3 files changed, 145 insertions(+) diff --git a/src/lib/libcrypto/evp/c_all.c b/src/lib/libcrypto/evp/c_all.c index 90d0f7e20..482cc4e06 100644 --- a/src/lib/libcrypto/evp/c_all.c +++ b/src/lib/libcrypto/evp/c_all.c @@ -244,6 +244,7 @@ OpenSSL_add_all_ciphers_internal(void) EVP_add_cipher(EVP_kuznyechik_ctr()); EVP_add_cipher(EVP_kuznyechik_ctr_acpkm()); EVP_add_cipher(EVP_kuznyechik_ctr_acpkm_omac()); + EVP_add_cipher(EVP_kuznyechik_kexp15_wrap()); EVP_add_cipher(EVP_kuznyechik_mgm()); #endif diff --git a/src/lib/libcrypto/evp/e_kuznyechik.c b/src/lib/libcrypto/evp/e_kuznyechik.c index c6b09173e..3c2a70aeb 100644 --- a/src/lib/libcrypto/evp/e_kuznyechik.c +++ b/src/lib/libcrypto/evp/e_kuznyechik.c @@ -42,6 +42,13 @@ typedef struct { unsigned char tag[16]; } EVP_KUZNYECHIK_CTR_ACPKM_OMAC_CTX; +typedef struct { + KUZNYECHIK_KEY ks; + CMAC_CTX *cmac; + int iv_set; + int key_set; +} EVP_KUZNYECHIK_KEXP15_WRAP_CTX; + static int kuznyechik_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc) @@ -342,6 +349,131 @@ kuznyechik_ctr_acpkm_omac_get_asn1_params(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params return ret; } +#define KEXP15_IV_OFFSET 24 +#define KEXP15_KUZNYECHIK_IV_PART 8 + +static int +kuznyechik_kexp15_wrap_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc) +{ + EVP_KUZNYECHIK_KEXP15_WRAP_CTX *c = ctx->cipher_data; + + if (iv) { + memset(ctx->iv, 0, sizeof(ctx->iv)); + memcpy(ctx->iv, iv + KEXP15_IV_OFFSET, KEXP15_KUZNYECHIK_IV_PART); + c->iv_set = 1; + if (c->key_set) + CMAC_Update(c->cmac, iv, KEXP15_KUZNYECHIK_IV_PART); + } + + if (key) { + c->key_set = 1; + const EVP_CIPHER *ciph = EVP_kuznyechik_cbc(); + int kl = EVP_CIPHER_key_length(ciph); + + if (!CMAC_Init(c->cmac, key, kl, ciph, NULL)) + return 0; + + if (iv != NULL) + CMAC_Update(c->cmac, iv, KEXP15_KUZNYECHIK_IV_PART); + else if (c->iv_set) + CMAC_Update(c->cmac, ctx->iv, KEXP15_KUZNYECHIK_IV_PART); + + Kuznyechik_set_key(&c->ks, key + kl, 1); + } + + return 1; +} + +static int +kuznyechik_kexp15_wrap_cleanup(EVP_CIPHER_CTX *ctx) +{ + EVP_KUZNYECHIK_KEXP15_WRAP_CTX *c = ctx->cipher_data; + + CMAC_CTX_free(c->cmac); + c->iv_set = 0; + c->key_set = 0; + + return 1; +} + +static int +kuznyechik_kexp15_wrap_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) +{ + EVP_KUZNYECHIK_KEXP15_WRAP_CTX *key = EVP_C_DATA(EVP_KUZNYECHIK_KEXP15_WRAP_CTX, ctx); + + switch (type) { + case EVP_CTRL_INIT: + key->cmac = CMAC_CTX_new(); + key->iv_set = 0; + key->key_set = 0; + return 1; + default: + return kuznyechik_ctl(ctx, type, arg, ptr); + } +} + +static int +kuznyechik_kexp15_wrap_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, + size_t len) +{ + EVP_KUZNYECHIK_KEXP15_WRAP_CTX *key = EVP_C_DATA(EVP_KUZNYECHIK_KEXP15_WRAP_CTX, ctx); + unsigned char tmp[EVP_MAX_BLOCK_LENGTH]; + size_t taglen = sizeof(tmp); + unsigned int bl = EVP_CIPHER_CTX_block_size(CMAC_CTX_get0_cipher_ctx(key->cmac)); + + if (in == NULL) + return 0; + + if (len % bl != 0) + return -1; + if (ctx->encrypt && len < bl) + return -1; + if (!ctx->encrypt && len < 2 *bl) + return -1; + if (out == NULL) { + if (ctx->encrypt) + return len + bl; + else + return len - bl; + } + + /* Do not reuse IV */ + key->iv_set = 0; + + if (ctx->encrypt) { + CMAC_Update(key->cmac, in, len); + CRYPTO_ctr128_encrypt(in, out, len, &key->ks, ctx->iv, ctx->buf, + &ctx->num, (block128_f)Kuznyechik_encrypt); + CMAC_Final(key->cmac, tmp, &taglen); + CRYPTO_ctr128_encrypt(tmp, out + len, taglen, &key->ks, ctx->iv, ctx->buf, + &ctx->num, (block128_f)Kuznyechik_encrypt); + return len + taglen; + } else { + CRYPTO_ctr128_encrypt(in, out, len - bl, &key->ks, ctx->iv, ctx->buf, + &ctx->num, (block128_f)Kuznyechik_encrypt); + CMAC_Update(key->cmac, out, len - bl); + CMAC_Final(key->cmac, tmp, &taglen); + CRYPTO_ctr128_encrypt(tmp, tmp, taglen, &key->ks, ctx->iv, ctx->buf, + &ctx->num, (block128_f)Kuznyechik_encrypt); + return timingsafe_memcmp(in + len - bl, tmp, bl) ? -1 : len - bl; + } +} + +static int +kuznyechik_kexp15_wrap_set_asn1_params(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params) +{ + /* FIXME: set key agreement OID, we need to pass it from upper layer */ + return 1; +} + +static int +kuznyechik_kexp15_wrap_get_asn1_params(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params) +{ + /* No useful information in ASN.1 params */ + return 1; +} + IMPLEMENT_BLOCK_CIPHER(kuznyechik, ks, Kuznyechik, EVP_KUZNYECHIK_CTX, NID_kuznyechik, 16, 32, 16, 128, 0, kuznyechik_init_key, NULL, EVP_CIPHER_set_asn1_iv, @@ -375,6 +507,17 @@ BLOCK_CIPHER_def1(kuznyechik, ctr_acpkm_omac, ctr_acpkm_omac, CTR, EVP_KUZNYECHI kuznyechik_ctr_acpkm_omac_get_asn1_params, kuznyechik_ctr_acpkm_omac_ctl) +#define NID_kuznyechik_kexp15_wrap NID_id_tc26_wrap_gostr3412_2015_kuznyechik_kexp15 + +BLOCK_CIPHER_def1(kuznyechik, kexp15_wrap, kexp15_wrap, WRAP, EVP_KUZNYECHIK_KEXP15_WRAP_CTX, + NID_kuznyechik, 1, 64, 32, + EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT, + kuznyechik_kexp15_wrap_init_key, + kuznyechik_kexp15_wrap_cleanup, + kuznyechik_kexp15_wrap_set_asn1_params, + kuznyechik_kexp15_wrap_get_asn1_params, + kuznyechik_kexp15_wrap_ctl) + #define EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN 16 typedef struct { diff --git a/src/lib/libcrypto/evp/evp.h b/src/lib/libcrypto/evp/evp.h index 48c7e7360..79ddcebc2 100644 --- a/src/lib/libcrypto/evp/evp.h +++ b/src/lib/libcrypto/evp/evp.h @@ -865,6 +865,7 @@ const EVP_CIPHER *EVP_kuznyechik_ofb(void); const EVP_CIPHER *EVP_kuznyechik_ctr(void); const EVP_CIPHER *EVP_kuznyechik_ctr_acpkm(void); const EVP_CIPHER *EVP_kuznyechik_ctr_acpkm_omac(void); +const EVP_CIPHER *EVP_kuznyechik_kexp15_wrap(void); const EVP_CIPHER *EVP_kuznyechik_mgm(void); #endif -- 2.17.1