From 7aaff5bb21f720d0a4902ead48296e3dcb422d3f Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Mon, 6 May 2019 22:41:55 +0300 Subject: [PATCH] Initial implementation of MGM mode for Kuznyechik. No decrypt yet, only big blocks supported. --- e_gost_err.c | 7 +- e_gost_err.h | 9 +- gost.txt | 5 +- gost_eng.c | 8 + gost_grasshopper_cipher.c | 439 +++++++++++++++++++++++++++++++++++++- gost_grasshopper_cipher.h | 25 ++- test_mgm.c | 268 +++++++++++++++++++++++ 7 files changed, 752 insertions(+), 9 deletions(-) create mode 100644 test_mgm.c diff --git a/e_gost_err.c b/e_gost_err.c index 00cdd87..632a99e 100644 --- a/e_gost_err.c +++ b/e_gost_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-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 @@ -31,6 +31,10 @@ static ERR_STRING_DATA GOST_str_functs[] = { {ERR_PACK(0, GOST_F_GOST_EC_VERIFY, 0), "gost_ec_verify"}, {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, 0), "gost_grasshopper_cipher_ctl"}, + {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM, 0), + "gost_grasshopper_cipher_do_mgm"}, + {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_CIPHER_INIT, 0), + "gost_grasshopper_cipher_init"}, {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_SET_ASN1_PARAMETERS, 0), "gost_grasshopper_set_asn1_parameters"}, {ERR_PACK(0, GOST_F_GOST_IMIT_CTRL, 0), "gost_imit_ctrl"}, @@ -116,6 +120,7 @@ static ERR_STRING_DATA GOST_str_reasons[] = { {ERR_PACK(0, 0, GOST_R_INVALID_MAC_PARAMS), "invalid mac params"}, {ERR_PACK(0, 0, GOST_R_INVALID_MAC_SIZE), "invalid mac size"}, {ERR_PACK(0, 0, GOST_R_INVALID_PARAMSET), "invalid paramset"}, + {ERR_PACK(0, 0, GOST_R_INVALID_TAG_LENGTH), "invalid tag length"}, {ERR_PACK(0, 0, GOST_R_KEY_IS_NOT_INITIALIZED), "key is not initialized"}, {ERR_PACK(0, 0, GOST_R_KEY_PARAMETERS_MISSING), "key parameters missing"}, {ERR_PACK(0, 0, GOST_R_MAC_KEY_NOT_SET), "mac key not set"}, diff --git a/e_gost_err.h b/e_gost_err.h index c5c9733..68e31ba 100644 --- a/e_gost_err.h +++ b/e_gost_err.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-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 @@ -11,6 +11,10 @@ #ifndef HEADER_GOSTERR_H # define HEADER_GOSTERR_H +# ifndef HEADER_SYMHACKS_H +# include +# endif + # define GOSTerr(f, r) ERR_GOST_error((f), (r), OPENSSL_FILE, OPENSSL_LINE) @@ -39,6 +43,8 @@ void ERR_GOST_error(int function, int reason, char *file, int line); # define GOST_F_GOST_EC_SIGN 109 # define GOST_F_GOST_EC_VERIFY 110 # define GOST_F_GOST_GRASSHOPPER_CIPHER_CTL 111 +# define GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM 155 +# define GOST_F_GOST_GRASSHOPPER_CIPHER_INIT 154 # define GOST_F_GOST_GRASSHOPPER_SET_ASN1_PARAMETERS 112 # define GOST_F_GOST_IMIT_CTRL 113 # define GOST_F_GOST_IMIT_FINAL 114 @@ -109,6 +115,7 @@ void ERR_GOST_error(int function, int reason, char *file, int line); # define GOST_R_INVALID_MAC_PARAMS 116 # define GOST_R_INVALID_MAC_SIZE 117 # define GOST_R_INVALID_PARAMSET 118 +# define GOST_R_INVALID_TAG_LENGTH 137 # define GOST_R_KEY_IS_NOT_INITIALIZED 119 # define GOST_R_KEY_PARAMETERS_MISSING 120 # define GOST_R_MAC_KEY_NOT_SET 121 diff --git a/gost.txt b/gost.txt index 15d986b..f4e936b 100644 --- a/gost.txt +++ b/gost.txt @@ -1,4 +1,4 @@ -# Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 1999-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 @@ -18,6 +18,8 @@ GOST_F_GOST_EC_KEYGEN:108:gost_ec_keygen GOST_F_GOST_EC_SIGN:109:gost_ec_sign GOST_F_GOST_EC_VERIFY:110:gost_ec_verify GOST_F_GOST_GRASSHOPPER_CIPHER_CTL:111:gost_grasshopper_cipher_ctl +GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM:155:gost_grasshopper_cipher_do_mgm +GOST_F_GOST_GRASSHOPPER_CIPHER_INIT:154:gost_grasshopper_cipher_init GOST_F_GOST_GRASSHOPPER_SET_ASN1_PARAMETERS:112:\ gost_grasshopper_set_asn1_parameters GOST_F_GOST_IMIT_CTRL:113:gost_imit_ctrl @@ -88,6 +90,7 @@ GOST_R_INVALID_MAC_KEY_SIZE:115:invalid mac key size GOST_R_INVALID_MAC_PARAMS:116:invalid mac params GOST_R_INVALID_MAC_SIZE:117:invalid mac size GOST_R_INVALID_PARAMSET:118:invalid paramset +GOST_R_INVALID_TAG_LENGTH:137:invalid tag length GOST_R_KEY_IS_NOT_INITIALIZED:119:key is not initialized GOST_R_KEY_PARAMETERS_MISSING:120:key parameters missing GOST_R_MAC_KEY_NOT_SET:121:mac key not set diff --git a/gost_eng.c b/gost_eng.c index 69f9975..1565d64 100644 --- a/gost_eng.c +++ b/gost_eng.c @@ -50,6 +50,7 @@ static int gost_cipher_nids[] = { NID_magma_cbc, NID_magma_ctr, NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm, + NID_kuznyechik_mgm, 0 }; @@ -280,6 +281,9 @@ static int bind_gost(ENGINE* e, const char* id) { || !EVP_add_cipher(cipher_gost_grasshopper_ofb()) || !EVP_add_cipher(cipher_gost_grasshopper_ctr()) || !EVP_add_cipher(cipher_gost_grasshopper_ctracpkm()) +#ifdef NID_kuznyechik_mgm + || !EVP_add_cipher(cipher_gost_grasshopper_mgm()) +#endif || !EVP_add_cipher(cipher_magma_cbc()) || !EVP_add_cipher(cipher_magma_ctr()) || !EVP_add_digest(digest_gost()) @@ -370,6 +374,10 @@ static int gost_ciphers(ENGINE* e, const EVP_CIPHER** cipher, *cipher = cipher_gost_grasshopper_ctr(); } else if (nid == NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm) { *cipher = cipher_gost_grasshopper_ctracpkm(); +#ifdef NID_kuznyechik_mgm + } else if (nid == NID_kuznyechik_mgm) { + *cipher = cipher_gost_grasshopper_mgm(); +#endif } else if (nid == NID_magma_cbc) { *cipher = cipher_magma_cbc(); } else if (nid == NID_magma_ctr) { diff --git a/gost_grasshopper_cipher.c b/gost_grasshopper_cipher.c index 7ae50f5..14b09dd 100644 --- a/gost_grasshopper_cipher.c +++ b/gost_grasshopper_cipher.c @@ -11,7 +11,9 @@ #include #include #include + #include +#include #include "gost_lcl.h" #include "e_gost_err.h" @@ -23,21 +25,25 @@ enum GRASSHOPPER_CIPHER_TYPE { GRASSHOPPER_CIPHER_CFB, GRASSHOPPER_CIPHER_CTR, GRASSHOPPER_CIPHER_CTRACPKM, + GRASSHOPPER_CIPHER_MGM, }; -static EVP_CIPHER *gost_grasshopper_ciphers[6] = { +static EVP_CIPHER *gost_grasshopper_ciphers[7] = { [GRASSHOPPER_CIPHER_ECB] = NULL, [GRASSHOPPER_CIPHER_CBC] = NULL, [GRASSHOPPER_CIPHER_OFB] = NULL, [GRASSHOPPER_CIPHER_CFB] = NULL, [GRASSHOPPER_CIPHER_CTR] = NULL, [GRASSHOPPER_CIPHER_CTRACPKM] = NULL, + [GRASSHOPPER_CIPHER_MGM] = NULL, }; static GRASSHOPPER_INLINE void gost_grasshopper_cipher_destroy_ofb(gost_grasshopper_cipher_ctx * c); static GRASSHOPPER_INLINE void gost_grasshopper_cipher_destroy_ctr(gost_grasshopper_cipher_ctx * c); +static GRASSHOPPER_INLINE void +gost_grasshopper_cipher_destroy_mgm(gost_grasshopper_cipher_ctx * c); struct GRASSHOPPER_CIPHER_PARAMS { int nid; @@ -50,7 +56,7 @@ struct GRASSHOPPER_CIPHER_PARAMS { bool padding; }; -static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[6] = { +static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[7] = { [GRASSHOPPER_CIPHER_ECB] = { NID_grasshopper_ecb, gost_grasshopper_cipher_init_ecb, @@ -114,6 +120,29 @@ static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[6] = { 16, false} , +#ifdef NID_kuznyechik_mgm + [GRASSHOPPER_CIPHER_MGM] = { + NID_kuznyechik_mgm, + gost_grasshopper_cipher_init_mgm, + gost_grasshopper_cipher_do_mgm, + gost_grasshopper_cipher_destroy_mgm, + 1, + sizeof(gost_grasshopper_cipher_ctx_mgm), + 16, + false} + , +#else + [GRASSHOPPER_CIPHER_MGM] = { + NID_undef, + NULL, + NULL, + NULL, + 0, + 0, + 0, + false} + , +#endif }; /* first 256 bit of D from draft-irtf-cfrg-re-keying-12 */ @@ -203,6 +232,15 @@ gost_grasshopper_cipher_destroy_ctr(gost_grasshopper_cipher_ctx * c) grasshopper_zero128(&ctx->partial_buffer); } +static GRASSHOPPER_INLINE void +gost_grasshopper_cipher_destroy_mgm(gost_grasshopper_cipher_ctx * c) +{ + gost_grasshopper_cipher_ctx_mgm *ctx = + (gost_grasshopper_cipher_ctx_mgm *) c; + + grasshopper_zero128(&ctx->partial_buffer); +} + int gost_grasshopper_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc) @@ -219,6 +257,17 @@ int gost_grasshopper_cipher_init(EVP_CIPHER_CTX *ctx, } if (iv != NULL) { + if (c->type == GRASSHOPPER_CIPHER_MGM) { + gost_grasshopper_cipher_ctx_mgm *m = (gost_grasshopper_cipher_ctx_mgm *)c; + + /* 1st bit should be 0*/ + if (iv[0] > 127) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_INIT, GOST_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(m->mgm_iv, iv, 16); + *(unsigned char *)(m->mgm_iv) += 128; + } memcpy((unsigned char *)EVP_CIPHER_CTX_original_iv(ctx), iv, EVP_CIPHER_CTX_iv_length(ctx)); } @@ -300,6 +349,21 @@ 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_mgm(EVP_CIPHER_CTX *ctx, const unsigned char + *key, const unsigned char + *iv, int enc) +{ + gost_grasshopper_cipher_ctx_mgm *c = EVP_CIPHER_CTX_get_cipher_data(ctx); + + c->c.type = GRASSHOPPER_CIPHER_MGM; + c->taglen = 16; + EVP_CIPHER_CTX_set_num(ctx, 0); + + grasshopper_zero128(&c->partial_buffer); + + 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, @@ -523,6 +587,330 @@ int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX *ctx, return 1; } +/* ----------------------------------------------------------------------------------------------- */ +/*! Функция реализует операцию умножения двух элементов конечного поля \f$ \mathbb F_{2^{128}}\f$, + порожденного неприводимым многочленом + \f$ f(x) = x^{128} + x^7 + x^2 + x + 1 \in \mathbb F_2[x]\f$. Для умножения используется + простейшая реализация, основанная на приведении по модулю после каждого шага алгоритма. */ +/* ----------------------------------------------------------------------------------------------- */ +static void gf128_mul_uint64(uint64_t *z, uint64_t *x, uint64_t *y) +{ + int i = 0, n = 0; + uint64_t t, s0, s1; + + BUF_reverse((unsigned char *)x, NULL, 16); + BUF_reverse((unsigned char *)y, NULL, 16); + +#ifdef L_ENDIAN + s0 = x[0]; + s1 = x[1]; +#else + s0 = bswap_64(x[0]); + s1 = bswap_64(x[1]); +#endif + + memset(z, 0, sizeof(uint64_t)*2); + + /* lower half */ +#ifdef L_ENDIAN + t = y[0]; +#else + t = bswap_64(y[0]); +#endif + + for( i = 0; i < 64; i++ ) { + if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; } + t >>= 1; + n = s1 >> 63; + s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1; + if(n) s0 ^= 0x87; + } + + /* upper half */ +#ifdef L_ENDIAN + t = y[1]; +#else + t = bswap_64(y[1]); +#endif + + for( i = 0; i < 63; i++ ) { + if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; } + t >>= 1; + n = s1 >> 63; + s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1; + if( n ) s0 ^= 0x87; + } + + if( t&0x1 ) { + z[0] ^= s0; + z[1] ^= s1; + } +#ifndef L_ENDIAN + z[0] = bswap_64(z[0]); + z[1] = bswap_64(z[1]); +#endif + BUF_reverse((unsigned char *)z, NULL, 16); +} + +static void hexdump(FILE *f, const char *title, const unsigned char *s, int l) +{ + int n = 0; + + fprintf(f, "%s", title); + for (; n < l; ++n) { + if ((n % 16) == 0) + fprintf(f, "\n%04x", n); + fprintf(f, " %02x", s[n]); + } + fprintf(f, "\n"); +} + +int gost_grasshopper_cipher_do_mgm(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + gost_grasshopper_cipher_ctx_mgm *c = (gost_grasshopper_cipher_ctx_mgm *) + EVP_CIPHER_CTX_get_cipher_data(ctx); + unsigned char *iv = EVP_CIPHER_CTX_iv_noconst(ctx); + const unsigned char *current_in = in; + unsigned char *current_out = out; + grasshopper_w128_t *currentInputBlock; + grasshopper_w128_t *currentOutputBlock; + unsigned int n = EVP_CIPHER_CTX_num(ctx); + size_t lasted; + size_t i; + + size_t blocks = inl / GRASSHOPPER_BLOCK_SIZE; + int rest_len = n % GRASSHOPPER_BLOCK_SIZE; + grasshopper_w128_t h; + + grasshopper_w128_t *iv_buffer = (grasshopper_w128_t *) iv; + grasshopper_w128_t tmp; + +/* ======== Here we deal with associated data =========== */ + if (out == NULL && c->mgm_state == mgm_associated_data) { + if (n == 0) { + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &c->partial_buffer, &c->c.buffer); + memcpy(c->mgm_iv, &c->partial_buffer, GRASSHOPPER_BLOCK_SIZE); + hexdump(stderr, "Tag", c->tag, 16); + } + + if (rest_len != 0) { + /* Finalize partial_data */ + if (inl + rest_len < GRASSHOPPER_BLOCK_SIZE) { + memcpy(c->mgm_partial_buffer.b+rest_len, current_in, inl); + n += inl; + EVP_CIPHER_CTX_set_num(ctx, n); + return 1; + } else { + memcpy(c->mgm_partial_buffer.b+rest_len, current_in, GRASSHOPPER_BLOCK_SIZE - rest_len); + + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + inc_counter(c->mgm_iv->b, 8); + + hexdump(stderr, "Hnext", h.b, 16); + hexdump(stderr, "Adata", c->mgm_partial_buffer.b, 16); + /* Galois multiply Hi * Ai */ + gf128_mul_uint64(tmp.q, h.q, c->mgm_partial_buffer.q); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + + current_in += GRASSHOPPER_BLOCK_SIZE - rest_len; + inl -= (GRASSHOPPER_BLOCK_SIZE - rest_len); + n += GRASSHOPPER_BLOCK_SIZE - rest_len; + } + } + + while (inl >= GRASSHOPPER_BLOCK_SIZE) { + currentInputBlock = (grasshopper_w128_t *) current_in; + + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + inc_counter(c->mgm_iv->b, 8); + + hexdump(stderr, "Hnext", h.b, 16); + hexdump(stderr, "Adata", currentInputBlock->b, 16); + /* Galois multiply */ + gf128_mul_uint64(tmp.q, h.q, currentInputBlock->q); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + + current_in += GRASSHOPPER_BLOCK_SIZE; + inl -= GRASSHOPPER_BLOCK_SIZE; + n += GRASSHOPPER_BLOCK_SIZE; + } + + if (inl > 0) + { + memcpy(c->mgm_partial_buffer.b, current_in, inl); + n += inl; + } + + EVP_CIPHER_CTX_set_num(ctx, n); + return 1; + } + + if (out == NULL && in != NULL && inl !=0 && c->mgm_state == mgm_main_data) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM, GOST_R_BAD_ORDER); + return 0; + } + + if (out != NULL && c->mgm_state == mgm_associated_data) { + memset(c->mgm_partial_buffer.b+rest_len, 0, GRASSHOPPER_BLOCK_SIZE - rest_len); + + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + inc_counter(c->mgm_iv->b, 8); + + hexdump(stderr, "Hnext", h.b, 16); + hexdump(stderr, "Padded Adata", c->mgm_partial_buffer.b, 16); + /* Galois multiply Hi * Ai */ + gf128_mul_uint64(tmp.q, h.q, c->mgm_partial_buffer.q); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + + /* We finish processing associated data */ + /* Pad rest of mgm_partial_buffer */ + /* Process last block */ + c->ad_length = n; + n = 0; + EVP_CIPHER_CTX_set_num(ctx, 0); + c->mgm_state = mgm_main_data; + + fprintf(stderr, "============= Deal with main data\n"); + } + +/* ======== Here we deal with main data =========== */ + if (n == 0) { + /* actual IV derived from nonce */ + grasshopper_encrypt_block(&c->c.encrypt_round_keys, iv_buffer, + &c->partial_buffer, &c->c.buffer); + memcpy(iv, c->partial_buffer.b, GRASSHOPPER_BLOCK_SIZE); + //hexdump(stderr, "Y1", iv, 16); + } + + while (rest_len && inl) { + *(current_out++) = *(current_in++) ^ c->partial_buffer.b[rest_len]; + --inl; + n++; + rest_len++; + if (rest_len == GRASSHOPPER_BLOCK_SIZE) + { + rest_len = 0; + } + } + EVP_CIPHER_CTX_set_num(ctx, n); + + // full parts + for (i = 0; i < blocks; i++) { + currentInputBlock = (grasshopper_w128_t *) current_in; + currentOutputBlock = (grasshopper_w128_t *) current_out; + grasshopper_encrypt_block(&c->c.encrypt_round_keys, iv_buffer, + &c->partial_buffer, &c->c.buffer); + grasshopper_plus128(&tmp, &c->partial_buffer, currentInputBlock); + grasshopper_copy128(currentOutputBlock, &tmp); + //hexdump(stderr, "Ciphertext", currentOutputBlock->b, 16); + + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + inc_counter(c->mgm_iv->b, 8); + hexdump(stderr, "Hnext", h.b, 16); + hexdump(stderr, "Ciphertext", currentOutputBlock->b, 16); + /* Galois multiply Hi * Ai */ + gf128_mul_uint64(tmp.q, h.q, currentOutputBlock->q); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + + ctr128_inc(iv_buffer->b); + current_in += GRASSHOPPER_BLOCK_SIZE; + current_out += GRASSHOPPER_BLOCK_SIZE; + n += GRASSHOPPER_BLOCK_SIZE; + } + + // last part + lasted = inl - blocks * GRASSHOPPER_BLOCK_SIZE; + if (lasted > 0) { + currentInputBlock = (grasshopper_w128_t *) current_in; + currentOutputBlock = (grasshopper_w128_t *) current_out; + grasshopper_encrypt_block(&c->c.encrypt_round_keys, iv_buffer, + &c->partial_buffer, &c->c.buffer); + for (i = 0; i < lasted; i++) { + currentOutputBlock->b[i] = + c->partial_buffer.b[i] ^ currentInputBlock->b[i]; + } + grasshopper_copy128(&c->partial_buffer, currentOutputBlock); + EVP_CIPHER_CTX_set_num(ctx, n+i); + ctr128_inc(iv_buffer->b); + } + + /* Final step */ + if (in == NULL && inl == 0) + { + unsigned char len_buf[16]; + uint64_t a_len = 0, p_len = 0; + + if (rest_len != 0) + { + memset(c->partial_buffer.b+rest_len, 0, GRASSHOPPER_BLOCK_SIZE - rest_len); + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + inc_counter(c->mgm_iv->b, 8); + hexdump(stderr, "Hnext", h.b, 16); + hexdump(stderr, "Padded ciphertext", c->partial_buffer.b, 16); + /* Galois multiply Hi * Ai */ + gf128_mul_uint64(tmp.q, h.q, c->partial_buffer.q); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + } + + a_len = c->ad_length << 3; + p_len = (c->mgm_state == mgm_associated_data) ? 0 : n << 3; + +#ifdef L_ENDIAN + a_len = bswap_64(a_len); + p_len = bswap_64(p_len); +#endif + memset(len_buf, 0, 16); + + memcpy(len_buf, &a_len, sizeof(a_len)); + memcpy(len_buf+sizeof(a_len), &p_len, sizeof(p_len)); + grasshopper_encrypt_block(&c->c.encrypt_round_keys, c->mgm_iv, + &h, &c->c.buffer); + + hexdump(stderr, "Hlast", h.b, 16); + hexdump(stderr, "Lenbuf", len_buf, 16); + /* Galois multiply Hi * Ai */ + gf128_mul_uint64(tmp.q, h.q, (uint64_t *)len_buf); + + /* XOR to c->tag */ + grasshopper_plus128(&h, (grasshopper_w128_t *)c->tag, &tmp); + grasshopper_copy128((grasshopper_w128_t *)c->tag, &h); + hexdump(stderr, "Tag", c->tag, 16); + + /* Final tag calculation */ + grasshopper_encrypt_block(&c->c.encrypt_round_keys, (grasshopper_w128_t *)c->tag, + (grasshopper_w128_t *)c->final_tag, &c->c.buffer); + } + + return 1; +} + /* * Fixed 128-bit IV implementation make shift regiser redundant. */ @@ -815,6 +1203,33 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, } return -1; #endif + case EVP_CTRL_AEAD_GET_TAG: + case EVP_CTRL_AEAD_SET_TAG: + { + gost_grasshopper_cipher_ctx_mgm *mgm_ctx = NULL; + gost_grasshopper_cipher_ctx *c = NULL; + int taglen = arg; + unsigned char* tag = ptr; + + mgm_ctx = (gost_grasshopper_cipher_ctx_mgm *) + EVP_CIPHER_CTX_get_cipher_data(ctx); + c = (gost_grasshopper_cipher_ctx *)mgm_ctx; + + if (c->type != GRASSHOPPER_CIPHER_MGM) + return -1; + + if (taglen > 16) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, GOST_R_INVALID_TAG_LENGTH); + return -1; + } + + if (type == EVP_CTRL_AEAD_GET_TAG) + memcpy(tag, mgm_ctx->final_tag, taglen); + else + memcpy(mgm_ctx->final_tag, tag, taglen); + + return 1; + } default: GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND); @@ -832,7 +1247,7 @@ 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) { return EVP_CIPHER_meth_set_iv_length(cipher, iv_size) && EVP_CIPHER_meth_set_flags(cipher, @@ -844,7 +1259,7 @@ const int cipher_gost_grasshopper_setup(EVP_CIPHER *cipher, uint8_t mode, EVP_CIPH_CUSTOM_IV : 0) | EVP_CIPH_RAND_KEY | - EVP_CIPH_ALWAYS_CALL_INIT) + EVP_CIPH_ALWAYS_CALL_INIT | extra_flags) ) && EVP_CIPHER_meth_set_cleanup(cipher, gost_grasshopper_cipher_cleanup) && EVP_CIPHER_meth_set_set_asn1_params(cipher, @@ -873,12 +1288,15 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper(uint8_t mode, int iv_size = params->iv_size; bool padding = params->padding; + int extra_flags = (num == GRASSHOPPER_CIPHER_MGM) ? + EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_FLAG_AEAD_CIPHER : 0; + *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); @@ -920,6 +1338,15 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_ctracpkm() GRASSHOPPER_CIPHER_CTRACPKM); } +const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_mgm() +{ +#ifndef NID_kuznyechik_mgm + return NULL; +#else + return cipher_gost_grasshopper(EVP_CIPH_CTR_MODE, GRASSHOPPER_CIPHER_MGM); +#endif +} + void cipher_gost_grasshopper_destroy(void) { EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_ECB]); @@ -934,4 +1361,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_MGM]); + gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_MGM] = NULL; } diff --git a/gost_grasshopper_cipher.h b/gost_grasshopper_cipher.h index 31dc021..350075e 100644 --- a/gost_grasshopper_cipher.h +++ b/gost_grasshopper_cipher.h @@ -37,6 +37,24 @@ typedef struct { if 0 never mesh and work like plain ctr. */ } gost_grasshopper_cipher_ctx_ctr; +typedef enum { + mgm_associated_data = 0, + mgm_main_data, +} mgm_state; + +typedef struct { + gost_grasshopper_cipher_ctx c; + grasshopper_w128_t partial_buffer; + + mgm_state mgm_state; /* associated_data/plain text */ + grasshopper_w128_t mgm_iv[16]; /* nonce */ + grasshopper_w128_t mgm_partial_buffer; /* Rest of associated data */ + size_t ad_length; + size_t taglen; /* MAC length*/ + unsigned char tag[16]; /* MAC - intermediate state */ + unsigned char final_tag[16]; /* MAC - final state*/ +} gost_grasshopper_cipher_ctx_mgm; + typedef int (* grasshopper_init_cipher_func)(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); @@ -61,6 +79,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_mgm(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); @@ -83,6 +103,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_mgm(EVP_CIPHER_CTX* ctx, unsigned char* out, + const unsigned char* in, size_t inl); int gost_grasshopper_cipher_cleanup(EVP_CIPHER_CTX* ctx); @@ -94,7 +116,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); @@ -104,6 +126,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_mgm(); void cipher_gost_grasshopper_destroy(void); diff --git a/test_mgm.c b/test_mgm.c new file mode 100644 index 0000000..d6b5a74 --- /dev/null +++ b/test_mgm.c @@ -0,0 +1,268 @@ +# include +# include +# include +# include +#include +#include + +static void hexdump(FILE *f, const char *title, const unsigned char *s, int l) +{ + int n = 0; + + fprintf(f, "%s", title); + for (; n < l; ++n) { + if ((n % 16) == 0) + fprintf(f, "\n%04x", n); + fprintf(f, " %02x", s[n]); + } + fprintf(f, "\n"); +} + +void grasshopper_mgm_big() +{ + const unsigned char gh_key[] = { +0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, +0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + }; + + const unsigned char gh_nonce[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88 + }; + + const unsigned char gh_adata[] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xEA, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 + }; + + const unsigned char gh_pdata[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, + 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11, + 0xAA, 0xBB, 0xCC + }; + + const unsigned char etalon_tag[] = { + 0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E, 0x29, 0xFC, 0xDB, 0x4C + }; + + unsigned char tag[16]; + unsigned char gh_cdata[sizeof(gh_pdata)]; + unsigned char *pcdata = gh_cdata; + + int asize = sizeof(gh_adata); + int psize = sizeof(gh_pdata); + int fsize = 0; + + const EVP_CIPHER *ciph; + EVP_CIPHER_CTX *enc = NULL; + + int i; + + ciph = EVP_get_cipherbynid(NID_kuznyechik_mgm); + if (ciph == NULL) { + fprintf(stderr, "Could not obtain cipher"); + return; + } + + enc = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce); + + EVP_EncryptUpdate(enc, NULL, &asize, gh_adata, sizeof(gh_adata)); + EVP_EncryptUpdate(enc, gh_cdata, &psize, gh_pdata, sizeof(gh_pdata)); + EVP_EncryptFinal_ex(enc, NULL, &fsize); + + EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag); + + if (memcmp (tag, etalon_tag, 16)) { + fprintf(stderr, "Shit happens!\n"); + hexdump(stderr, "Etalon tag", etalon_tag, 16); + hexdump(stderr, "Got tag", tag, 16); + return; + } + fprintf(stderr, "OK - big chunks!\n"); + EVP_CIPHER_CTX_free(enc); + + fprintf(stderr, "================ \n"); +} + +void grasshopper_mgm_small() +{ + const unsigned char gh_key[] = { +0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, +0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + }; + + const unsigned char gh_nonce[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88 + }; + + const unsigned char gh_adata[] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0xEA, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 + }; + + const unsigned char gh_pdata[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, + 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11, + 0xAA, 0xBB, 0xCC + }; + + const unsigned char etalon_tag[] = { + 0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E, 0x29, 0xFC, 0xDB, 0x4C + }; + + unsigned char tag[16]; + unsigned char gh_cdata[sizeof(gh_pdata)]; + unsigned char *pcdata = gh_cdata; + + int asize = sizeof(gh_adata); + int psize = sizeof(gh_pdata); + int fsize = 0; + + const EVP_CIPHER *ciph; + EVP_CIPHER_CTX *enc = NULL; + + int i; + + ciph = EVP_get_cipherbynid(NID_kuznyechik_mgm); + if (ciph == NULL) { + fprintf(stderr, "Could not obtain cipher"); + return; + } + + enc = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce); + + for (i = 0; i < sizeof(gh_adata); i++) + { + asize = 1; + EVP_EncryptUpdate(enc, NULL, &asize, gh_adata+i, 1); + } + + for (i = 0; i < sizeof(gh_pdata); i++) + { + psize = 1; + EVP_EncryptUpdate(enc, pcdata, &psize, gh_pdata+i, 1); + pcdata += psize; + } + + EVP_EncryptFinal_ex(enc, NULL, &fsize); + + EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag); + + if (memcmp (tag, etalon_tag, 16)) { + fprintf(stderr, "Shit happens!\n"); + hexdump(stderr, "Etalon tag", etalon_tag, 16); + hexdump(stderr, "Got tag", tag, 16); + return; + } + fprintf(stderr, "OK - small chunks!\n"); + EVP_CIPHER_CTX_free(enc); + +} + +#define L_ENDIAN 1 +static void gf128_mul_uint64(uint64_t *z, uint64_t *x, uint64_t *y) +{ + int i = 0, n = 0; + uint64_t t, s0, s1; + + BUF_reverse((unsigned char *)x, NULL, 16); + BUF_reverse((unsigned char *)y, NULL, 16); + +#ifdef L_ENDIAN + s0 = x[0]; + s1 = x[1]; +#else + s0 = bswap_64(x[0]); + s1 = bswap_64(x[1]); +#endif + + memset(z, 0, sizeof(uint64_t)*2); + + /* lower half */ +#ifdef L_ENDIAN + t = y[0]; +#else + t = bswap_64(y[0]); +#endif + + for( i = 0; i < 64; i++ ) { + if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; } + t >>= 1; + n = s1 >> 63; + s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1; + if(n) s0 ^= 0x87; + } + + /* upper half */ +#ifdef L_ENDIAN + t = y[1]; +#else + t = bswap_64(y[1]); +#endif + + for( i = 0; i < 63; i++ ) { + if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; } + t >>= 1; + n = s1 >> 63; + s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1; + if( n ) s0 ^= 0x87; + } + + if( t&0x1 ) { + z[0] ^= s0; + z[1] ^= s1; + } +#ifndef L_ENDIAN + z[0] = bswap_64(z[0]); + z[1] = bswap_64(z[1]); +#endif + BUF_reverse((unsigned char *)z, NULL, 16); +} + +void gf_mul(void) +{ + unsigned char H[16] = { + 0x8D, 0xB1, 0x87, 0xD6, 0x53, 0x83, 0x0E, 0xA4, 0xBC, 0x44, 0x64, 0x76, 0x95, 0x2C, 0x30, 0x0B + }; + + unsigned char A[16] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + + unsigned char etalon[16] = { + 0x4C, 0xF4, 0x27, 0xF4, 0xAD, 0xB7, 0x5C, 0xF4, 0xC0, 0xDA, 0x39, 0xD5, 0xAB, 0x48, 0xCF, 0x38 + }; + + unsigned char result_bd[16]; + gf128_mul_uint64((uint64_t *)result_bd, (uint64_t *)H, (uint64_t *)A); + + + hexdump(stderr, "Etalon", etalon, 16); + + if (memcmp (etalon, result_bd, 16)) { + fprintf(stderr, "Shit happens - BD!\n"); + hexdump(stderr, "Result - BD", result_bd, 16); + } else + fprintf(stderr, "OK - BD!\n"); + + return; +} + +int main(void) +{ + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); + + gf_mul(); + grasshopper_mgm_big(); + grasshopper_mgm_small(); + ERR_print_errors_fp(stderr); + + return 0; +} -- 2.39.5