From a6df1e0ca7c7b34c468d2d1c0fc50029f6ec711b Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Mon, 4 May 2020 14:14:19 +0300 Subject: [PATCH] KUZNYECHIK-CTR_ACPKM-OMAC implementation --- gost_eng.c | 4 + gost_gost2015.h | 1 + gost_grasshopper_cipher.c | 334 +++++++++++++++++++++++++++++--------- gost_grasshopper_cipher.h | 9 +- 4 files changed, 267 insertions(+), 81 deletions(-) diff --git a/gost_eng.c b/gost_eng.c index 249b5e4..0ae4c9b 100644 --- a/gost_eng.c +++ b/gost_eng.c @@ -158,6 +158,10 @@ static struct gost_cipher_minfo { NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm, cipher_gost_grasshopper_ctracpkm, }, + { + NID_kuznyechik_ctr_acpkm_omac, + cipher_gost_grasshopper_ctracpkm_omac, + }, { NID_magma_kexp15, cipher_magma_wrap, diff --git a/gost_gost2015.h b/gost_gost2015.h index 797fbca..4feed58 100644 --- a/gost_gost2015.h +++ b/gost_gost2015.h @@ -5,6 +5,7 @@ #define MAGMA_MAC_MAX_SIZE 8 #define KUZNYECHIK_MAC_MAX_SIZE 16 +#define OID_GOST_CMS_MAC "1.2.643.7.1.0.6.1.1" int gost2015_final_call(EVP_CIPHER_CTX *ctx, EVP_MD_CTX *omac_ctx, size_t mac_size, unsigned char *encrypted_mac, diff --git a/gost_grasshopper_cipher.c b/gost_grasshopper_cipher.c index 75a0408..9cef0c5 100644 --- a/gost_grasshopper_cipher.c +++ b/gost_grasshopper_cipher.c @@ -24,10 +24,11 @@ enum GRASSHOPPER_CIPHER_TYPE { GRASSHOPPER_CIPHER_CFB, GRASSHOPPER_CIPHER_CTR, GRASSHOPPER_CIPHER_CTRACPKM, + GRASSHOPPER_CIPHER_CTRACPKMOMAC, }; -static EVP_CIPHER *gost_grasshopper_ciphers[6] = { - NULL, NULL, NULL, NULL, NULL, NULL, +static EVP_CIPHER *gost_grasshopper_ciphers[7] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static GRASSHOPPER_INLINE void @@ -42,69 +43,87 @@ struct GRASSHOPPER_CIPHER_PARAMS { int ctx_size; int iv_size; bool padding; + int extra_flags; }; -static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[6] = { - { - NID_grasshopper_ecb, - gost_grasshopper_cipher_init_ecb, - gost_grasshopper_cipher_do_ecb, - NULL, - 16, - sizeof(gost_grasshopper_cipher_ctx), - 0, - true} - , - { - NID_grasshopper_cbc, - gost_grasshopper_cipher_init_cbc, - gost_grasshopper_cipher_do_cbc, - NULL, - 16, - sizeof(gost_grasshopper_cipher_ctx), - 16, - true} - , - { - NID_grasshopper_ofb, - gost_grasshopper_cipher_init_ofb, - gost_grasshopper_cipher_do_ofb, - NULL, - 1, - sizeof(gost_grasshopper_cipher_ctx), - 16, - false} - , - { - NID_grasshopper_cfb, - gost_grasshopper_cipher_init_cfb, - gost_grasshopper_cipher_do_cfb, - NULL, - 1, - sizeof(gost_grasshopper_cipher_ctx), - 16, - false} - , - { - NID_grasshopper_ctr, - gost_grasshopper_cipher_init_ctr, - gost_grasshopper_cipher_do_ctr, - gost_grasshopper_cipher_destroy_ctr, - 1, - sizeof(gost_grasshopper_cipher_ctx_ctr), - 8, - false} - , - { - NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm, - gost_grasshopper_cipher_init_ctracpkm, - gost_grasshopper_cipher_do_ctracpkm, - gost_grasshopper_cipher_destroy_ctr, - 1, - sizeof(gost_grasshopper_cipher_ctx_ctr), - 8, - false} - , +static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[7] = { + { + NID_grasshopper_ecb, + gost_grasshopper_cipher_init_ecb, + gost_grasshopper_cipher_do_ecb, + NULL, + 16, + sizeof(gost_grasshopper_cipher_ctx), + 0, + true, + 0 + }, + { + NID_grasshopper_cbc, + gost_grasshopper_cipher_init_cbc, + gost_grasshopper_cipher_do_cbc, + NULL, + 16, + sizeof(gost_grasshopper_cipher_ctx), + 16, + true, + 0 + }, + { + NID_grasshopper_ofb, + gost_grasshopper_cipher_init_ofb, + gost_grasshopper_cipher_do_ofb, + NULL, + 1, + sizeof(gost_grasshopper_cipher_ctx), + 16, + false, + 0 + }, + { + NID_grasshopper_cfb, + gost_grasshopper_cipher_init_cfb, + gost_grasshopper_cipher_do_cfb, + NULL, + 1, + sizeof(gost_grasshopper_cipher_ctx), + 16, + false, + 0 + }, + { + NID_grasshopper_ctr, + gost_grasshopper_cipher_init_ctr, + gost_grasshopper_cipher_do_ctr, + gost_grasshopper_cipher_destroy_ctr, + 1, + sizeof(gost_grasshopper_cipher_ctx_ctr), + 8, + false, + 0 + }, + { + NID_kuznyechik_ctr_acpkm, + gost_grasshopper_cipher_init_ctracpkm, + gost_grasshopper_cipher_do_ctracpkm, + gost_grasshopper_cipher_destroy_ctr, + 1, + sizeof(gost_grasshopper_cipher_ctx_ctr), + 8, + false, + 0 + }, + { + NID_kuznyechik_ctr_acpkm_omac, + gost_grasshopper_cipher_init_ctracpkm_omac, + gost_grasshopper_cipher_do_ctracpkm_omac, + gost_grasshopper_cipher_destroy_ctr, + 1, + sizeof(gost_grasshopper_cipher_ctx_ctr), + 8, + false, + EVP_CIPH_FLAG_CUSTOM_CIPHER|EVP_CIPH_FLAG_CIPHER_WITH_MAC|EVP_CIPH_CUSTOM_COPY + }, }; /* first 256 bit of D from draft-irtf-cfrg-re-keying-12 */ @@ -182,6 +201,9 @@ gost_grasshopper_cipher_destroy_ctr(gost_grasshopper_cipher_ctx * c) gost_grasshopper_cipher_ctx_ctr *ctx = (gost_grasshopper_cipher_ctx_ctr *) c; + if (ctx->omac_ctx) + EVP_MD_CTX_free(ctx->omac_ctx); + grasshopper_zero128(&ctx->partial_buffer); } @@ -278,6 +300,54 @@ GRASSHOPPER_INLINE int gost_grasshopper_cipher_init_ctracpkm(EVP_CIPHER_CTX return gost_grasshopper_cipher_init(ctx, key, iv, enc); } +GRASSHOPPER_INLINE int gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX + *ctx, const unsigned + char *key, const unsigned + char *iv, int enc) +{ + gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx); + + /* NB: setting type makes EVP do_cipher callback useless */ + c->c.type = GRASSHOPPER_CIPHER_CTRACPKMOMAC; + EVP_CIPHER_CTX_set_num(ctx, 0); + c->section_size = 4096; + + if (key) { + unsigned char keys[64]; + const EVP_MD *md = EVP_get_digestbynid(NID_kuznyechik_mac); + EVP_PKEY *mac_key; + + if (md == NULL) + return 0; + + if (enc) { + if (RAND_bytes(c->kdf_seed, 8) != 1) + return 0; + } + + if (gost_kdftree2012_256(keys, 64, key, 32, (const unsigned char *)"kdf tree", 8, c->kdf_seed, 8, 1) <= 0) + return 0; + + c->omac_ctx = EVP_MD_CTX_new(); + mac_key = EVP_PKEY_new_mac_key(NID_kuznyechik_mac, NULL, keys+32, 32); + + if (mac_key == NULL || c->omac_ctx == NULL) { + EVP_PKEY_free(mac_key); + return 0; + } + + if (EVP_DigestInit_ex(c->omac_ctx, md, NULL) <= 0 || + EVP_DigestSignInit(c->omac_ctx, NULL, md, NULL, mac_key) <= 0) { + EVP_PKEY_free(mac_key); + return 0; + } + EVP_PKEY_free(mac_key); + + return gost_grasshopper_cipher_init(ctx, keys, iv, enc); + } + return gost_grasshopper_cipher_init(ctx, key, iv, enc); +} + GRASSHOPPER_INLINE int gost_grasshopper_cipher_do(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, @@ -498,9 +568,31 @@ int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX *ctx, } EVP_CIPHER_CTX_set_num(ctx, num); - return 1; + return inl; } +int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX *ctx, + unsigned char *out, + const unsigned char *in, size_t inl) +{ + int result; + gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx); + /* As in and out can be the same pointer, process unencrypted here */ + if (EVP_CIPHER_CTX_encrypting(ctx)) + EVP_DigestSignUpdate(c->omac_ctx, in, inl); + + if (in == NULL && inl == 0) { /* Final call */ + return gost2015_final_call(ctx, c->omac_ctx, KUZNYECHIK_MAC_MAX_SIZE, c->tag, gost_grasshopper_cipher_do_ctracpkm); + } + + result = gost_grasshopper_cipher_do_ctracpkm(ctx, out, in, inl); + + /* As in and out can be the same pointer, process decrypted here */ + if (!EVP_CIPHER_CTX_encrypting(ctx)) + EVP_DigestSignUpdate(c->omac_ctx, out, inl); + + return result; +} /* * Fixed 128-bit IV implementation make shift regiser redundant. */ @@ -691,6 +783,9 @@ int gost_grasshopper_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params) if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CTR_MODE) { gost_grasshopper_cipher_ctx_ctr *ctr = EVP_CIPHER_CTX_get_cipher_data(ctx); + /* CMS implies 256kb section_size */ + ctr->section_size = 256*1024; + return gost2015_set_asn1_params(params, EVP_CIPHER_CTX_original_iv(ctx), 8, ctr->kdf_seed); } @@ -714,7 +809,7 @@ GRASSHOPPER_INLINE int gost_grasshopper_get_asn1_parameters(EVP_CIPHER_CTX memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, iv_len); memcpy((unsigned char *)EVP_CIPHER_CTX_original_iv(ctx), iv, iv_len); - /* CMS implies 256kb mesh_section_size */ + /* CMS implies 256kb section_size */ ctr->section_size = 256*1024; return 1; } @@ -736,7 +831,9 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, case EVP_CTRL_KEY_MESH:{ gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx); - if (c->c.type != GRASSHOPPER_CIPHER_CTRACPKM || !arg + if ((c->c.type != GRASSHOPPER_CIPHER_CTRACPKM && + c->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC) + || (arg == 0) || (arg % GRASSHOPPER_BLOCK_SIZE)) return -1; c->section_size = arg; @@ -796,6 +893,78 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, } return -1; #endif +#if 0 + case EVP_CTRL_AEAD_GET_TAG: + case EVP_CTRL_AEAD_SET_TAG: + { + int taglen = arg; + unsigned char *tag = ptr; + + gost_grasshopper_cipher_ctx *c = EVP_CIPHER_CTX_get_cipher_data(ctx); + if (c->c.type != GRASSHOPPER_CIPHER_MGM) + return -1; + + if (taglen > KUZNYECHIK_MAC_MAX_SIZE) { + CRYPTOCOMerr(CRYPTOCOM_F_GOST_GRASSHOPPER_CIPHER_CTL, + CRYPTOCOM_R_INVALID_TAG_LENGTH); + return -1; + } + + if (type == EVP_CTRL_AEAD_GET_TAG) + memcpy(tag, c->final_tag, taglen); + else + memcpy(c->final_tag, tag, taglen); + + return 1; + } +#endif + case EVP_CTRL_PROCESS_UNPROTECTED: + { + gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx); + ASN1_OBJECT *cmsmacobj = NULL; + if (c->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC) + return -1; + cmsmacobj = OBJ_txt2obj(OID_GOST_CMS_MAC, 1); + if (cmsmacobj == NULL) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, ERR_R_MALLOC_FAILURE); + return -1; + } + if (arg == 0) /*Decrypting*/ { + STACK_OF(X509_ATTRIBUTE) *x = ptr; + ASN1_OCTET_STRING *osExpectedMac = X509at_get0_data_by_OBJ(x, + cmsmacobj, -3, V_ASN1_OCTET_STRING); + ASN1_OBJECT_free(cmsmacobj); + + if (ptr == NULL || osExpectedMac ==NULL || osExpectedMac->length != KUZNYECHIK_MAC_MAX_SIZE) + return -1; + + memcpy(c->tag, osExpectedMac->data, osExpectedMac->length); + return 1; + } else { + STACK_OF(X509_ATTRIBUTE) *x = ptr; + return (X509at_add1_attr_by_OBJ(&x, cmsmacobj, + V_ASN1_OCTET_STRING, c->tag, KUZNYECHIK_MAC_MAX_SIZE) == NULL) ? -1 : 1; + } + } + return 1; + case EVP_CTRL_COPY: { + EVP_CIPHER_CTX *out = ptr; + + gost_grasshopper_cipher_ctx_ctr *out_cctx = EVP_CIPHER_CTX_get_cipher_data(out); + gost_grasshopper_cipher_ctx_ctr *in_cctx = EVP_CIPHER_CTX_get_cipher_data(ctx); + + if (in_cctx->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC) + return -1; + + if (in_cctx->omac_ctx == out_cctx->omac_ctx) { + out_cctx->omac_ctx = EVP_MD_CTX_new(); + if (out_cctx->omac_ctx == NULL) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, ERR_R_MALLOC_FAILURE); + return -1; + } + } + return EVP_MD_CTX_copy(out_cctx->omac_ctx, in_cctx->omac_ctx); + } default: GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND); @@ -813,20 +982,16 @@ GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_create(int } const int cipher_gost_grasshopper_setup(EVP_CIPHER *cipher, uint8_t mode, - int iv_size, bool padding) + int iv_size, bool padding, int extra_flags) { + unsigned long flags = (unsigned long)(mode + | ((!padding) ? EVP_CIPH_NO_PADDING : 0) + | ((iv_size > 0) ? EVP_CIPH_CUSTOM_IV : 0) + | EVP_CIPH_RAND_KEY | EVP_CIPH_ALWAYS_CALL_INIT + | extra_flags); + return EVP_CIPHER_meth_set_iv_length(cipher, iv_size) - && EVP_CIPHER_meth_set_flags(cipher, - (unsigned long)(mode | - ((!padding) ? - EVP_CIPH_NO_PADDING : - 0) | ((iv_size > - 0) ? - EVP_CIPH_CUSTOM_IV - : 0) | - EVP_CIPH_RAND_KEY | - EVP_CIPH_ALWAYS_CALL_INIT) - ) + && EVP_CIPHER_meth_set_flags(cipher, flags) && EVP_CIPHER_meth_set_cleanup(cipher, gost_grasshopper_cipher_cleanup) && EVP_CIPHER_meth_set_set_asn1_params(cipher, gost_grasshopper_set_asn1_parameters) @@ -846,7 +1011,7 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper(uint8_t mode, if (*cipher == NULL) { grasshopper_init_cipher_func init_cipher; - int nid, block_size, ctx_size, iv_size; + int nid, block_size, ctx_size, iv_size, extra_flags; bool padding; params = &gost_cipher_params[num]; @@ -857,13 +1022,14 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper(uint8_t mode, ctx_size = params->ctx_size; iv_size = params->iv_size; padding = params->padding; + extra_flags = params->extra_flags; *cipher = cipher_gost_grasshopper_create(nid, block_size); if (*cipher == NULL) { return NULL; } - if (!cipher_gost_grasshopper_setup(*cipher, mode, iv_size, padding) + if (!cipher_gost_grasshopper_setup(*cipher, mode, iv_size, padding, extra_flags) || !EVP_CIPHER_meth_set_init(*cipher, init_cipher) || !EVP_CIPHER_meth_set_impl_ctx_size(*cipher, ctx_size)) { EVP_CIPHER_meth_free(*cipher); @@ -905,6 +1071,12 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_ctracpkm() GRASSHOPPER_CIPHER_CTRACPKM); } +const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_ctracpkm_omac() +{ + return cipher_gost_grasshopper(EVP_CIPH_CTR_MODE, + GRASSHOPPER_CIPHER_CTRACPKMOMAC); +} + void cipher_gost_grasshopper_destroy(void) { EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_ECB]); @@ -919,4 +1091,6 @@ void cipher_gost_grasshopper_destroy(void) gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTR] = NULL; EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKM]); gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKM] = NULL; + EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKMOMAC]); + gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKMOMAC] = NULL; } diff --git a/gost_grasshopper_cipher.h b/gost_grasshopper_cipher.h index a0acba4..f0ad7b3 100644 --- a/gost_grasshopper_cipher.h +++ b/gost_grasshopper_cipher.h @@ -31,6 +31,8 @@ typedef struct { unsigned int section_size; /* After how much bytes mesh the key, if 0 never mesh and work like plain ctr. */ unsigned char kdf_seed[8]; + unsigned char tag[16]; + EVP_MD_CTX *omac_ctx; } gost_grasshopper_cipher_ctx_ctr; typedef int (* grasshopper_init_cipher_func)(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, @@ -57,6 +59,8 @@ int gost_grasshopper_cipher_init_ctr(EVP_CIPHER_CTX* ctx, const unsigned char* k int gost_grasshopper_cipher_init_ctracpkm(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); +int gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); + int gost_grasshopper_cipher_init(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); @@ -79,6 +83,8 @@ int gost_grasshopper_cipher_do_ctr(EVP_CIPHER_CTX* ctx, unsigned char* out, const unsigned char* in, size_t inl); int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX* ctx, unsigned char* out, const unsigned char* in, size_t inl); +int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX* ctx, unsigned char* out, + const unsigned char* in, size_t inl); int gost_grasshopper_cipher_cleanup(EVP_CIPHER_CTX* ctx); @@ -90,7 +96,7 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX* ctx, int type, int arg, void* pt EVP_CIPHER* cipher_gost_grasshopper_create(int cipher_type, int block_size); -const int cipher_gost_grasshopper_setup(EVP_CIPHER* cipher, uint8_t mode, int iv_size, bool padding); +const int cipher_gost_grasshopper_setup(EVP_CIPHER* cipher, uint8_t mode, int iv_size, bool padding, int extra_flags); const EVP_CIPHER* cipher_gost_grasshopper(uint8_t mode, uint8_t num); @@ -100,6 +106,7 @@ extern const EVP_CIPHER* cipher_gost_grasshopper_ofb(); extern const EVP_CIPHER* cipher_gost_grasshopper_cfb(); extern const EVP_CIPHER* cipher_gost_grasshopper_ctr(); extern const EVP_CIPHER* cipher_gost_grasshopper_ctracpkm(); +extern const EVP_CIPHER* cipher_gost_grasshopper_ctracpkm_omac(); void cipher_gost_grasshopper_destroy(void); -- 2.39.5