X-Git-Url: http://wagner.pp.ru/gitweb/?a=blobdiff_plain;f=gost_grasshopper_cipher.c;fp=gost_grasshopper_cipher.c;h=da67eb4c88eaeaa9d7af47780820fdb690e8b73d;hb=41bff5cad003c9239623e556e69a281b2a8e80b1;hp=df58097c585dc370a8cd0443873d91c9b2704af6;hpb=140770d1dfcda28d8cf5b0e53d58b04f1fde0c6a;p=openssl-gost%2Fengine.git diff --git a/gost_grasshopper_cipher.c b/gost_grasshopper_cipher.c index df58097..da67eb4 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. */ @@ -814,6 +1202,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); @@ -831,7 +1246,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, @@ -843,7 +1258,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, @@ -872,12 +1287,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); @@ -919,6 +1337,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]); @@ -933,4 +1360,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; }