X-Git-Url: http://www.wagner.pp.ru/gitweb/?a=blobdiff_plain;f=gost_gost2015.c;h=8989ab1c50b42c423aa19c3ad862548567d805ee;hb=HEAD;hp=a47337509790550c0e33a907c08016b9eef34877;hpb=9cf021b8f3cc7b0f49ecd2f165bd1864903c90d0;p=openssl-gost%2Fengine.git diff --git a/gost_gost2015.c b/gost_gost2015.c index a473375..8989ab1 100644 --- a/gost_gost2015.c +++ b/gost_gost2015.c @@ -1,39 +1,47 @@ +/* + * Copyright (c) 2020 Dmitry Belyavskiy + * + * Contents licensed under the terms of the OpenSSL license + * See https://www.openssl.org/source/license.html for details + */ #include "gost_lcl.h" #include "gost_gost2015.h" +#include "gost_grasshopper_defines.h" +#include "gost_grasshopper_math.h" #include "e_gost_err.h" #include #include int gost2015_final_call(EVP_CIPHER_CTX *ctx, EVP_MD_CTX *omac_ctx, size_t mac_size, - unsigned char *encrypted_mac, - int (*do_cipher) (EVP_CIPHER_CTX *ctx, - unsigned char *out, - const unsigned char *in, - size_t inl)) + unsigned char *encrypted_mac, + int (*do_cipher) (EVP_CIPHER_CTX *ctx, + unsigned char *out, + const unsigned char *in, + size_t inl)) { - unsigned char calculated_mac[KUZNYECHIK_MAC_MAX_SIZE]; - memset(calculated_mac, 0, KUZNYECHIK_MAC_MAX_SIZE); + unsigned char calculated_mac[KUZNYECHIK_MAC_MAX_SIZE]; + memset(calculated_mac, 0, KUZNYECHIK_MAC_MAX_SIZE); - if (EVP_CIPHER_CTX_encrypting(ctx)) { - EVP_DigestSignFinal(omac_ctx, calculated_mac, &mac_size); + if (EVP_CIPHER_CTX_encrypting(ctx)) { + EVP_DigestSignFinal(omac_ctx, calculated_mac, &mac_size); - if (do_cipher(ctx, encrypted_mac, calculated_mac, mac_size) <= 0) { - return -1; - } - } else { - unsigned char expected_mac[KUZNYECHIK_MAC_MAX_SIZE]; + if (do_cipher(ctx, encrypted_mac, calculated_mac, mac_size) <= 0) { + return -1; + } + } else { + unsigned char expected_mac[KUZNYECHIK_MAC_MAX_SIZE]; - memset(expected_mac, 0, KUZNYECHIK_MAC_MAX_SIZE); - EVP_DigestSignFinal(omac_ctx, calculated_mac, &mac_size); + memset(expected_mac, 0, KUZNYECHIK_MAC_MAX_SIZE); + EVP_DigestSignFinal(omac_ctx, calculated_mac, &mac_size); - if (do_cipher(ctx, expected_mac, encrypted_mac, mac_size) <= 0) { - return -1; - } + if (do_cipher(ctx, expected_mac, encrypted_mac, mac_size) <= 0) { + return -1; + } - if (CRYPTO_memcmp(expected_mac, calculated_mac, mac_size) != 0) - return -1; - } - return 0; + if (CRYPTO_memcmp(expected_mac, calculated_mac, mac_size) != 0) + return -1; + } + return 0; } /* @@ -42,152 +50,444 @@ int gost2015_final_call(EVP_CIPHER_CTX *ctx, EVP_MD_CTX *omac_ctx, size_t mac_si #define MAX_GOST2015_UKM_SIZE 16 #define KDF_SEED_SIZE 8 int gost2015_get_asn1_params(const ASN1_TYPE *params, size_t ukm_size, - unsigned char *iv, size_t ukm_offset, unsigned char *kdf_seed) + unsigned char *iv, size_t ukm_offset, unsigned char *kdf_seed) { - int iv_len = 16; - GOST2015_CIPHER_PARAMS *gcp = NULL; + int iv_len = 16; + GOST2015_CIPHER_PARAMS *gcp = NULL; - unsigned char *p = NULL; + unsigned char *p = NULL; - memset(iv, 0, iv_len); + memset(iv, 0, iv_len); - /* Проверяем тип params */ - if (ASN1_TYPE_get(params) != V_ASN1_SEQUENCE) { - GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); - return 0; - } + /* Проверяем тип params */ + if (ASN1_TYPE_get(params) != V_ASN1_SEQUENCE) { + GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); + return 0; + } - p = params->value.sequence->data; - /* Извлекаем структуру параметров */ - gcp = d2i_GOST2015_CIPHER_PARAMS(NULL, (const unsigned char **)&p, params->value.sequence->length); - if (gcp == NULL) { - GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); - return 0; - } + p = params->value.sequence->data; + /* Извлекаем структуру параметров */ + gcp = d2i_GOST2015_CIPHER_PARAMS(NULL, (const unsigned char **)&p, params->value.sequence->length); + if (gcp == NULL) { + GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); + return 0; + } - /* Проверяем длину синхропосылки */ - if (gcp->ukm->length != (int)ukm_size) { - GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); - GOST2015_CIPHER_PARAMS_free(gcp); - return 0; - } + /* Проверяем длину синхропосылки */ + if (gcp->ukm->length != (int)ukm_size) { + GOSTerr(GOST_F_GOST2015_GET_ASN1_PARAMS, GOST_R_INVALID_CIPHER_PARAMS); + GOST2015_CIPHER_PARAMS_free(gcp); + return 0; + } - memcpy(iv, gcp->ukm->data, ukm_offset); - memcpy(kdf_seed, gcp->ukm->data+ukm_offset, KDF_SEED_SIZE); + memcpy(iv, gcp->ukm->data, ukm_offset); + memcpy(kdf_seed, gcp->ukm->data+ukm_offset, KDF_SEED_SIZE); - GOST2015_CIPHER_PARAMS_free(gcp); - return 1; + GOST2015_CIPHER_PARAMS_free(gcp); + return 1; } int gost2015_set_asn1_params(ASN1_TYPE *params, - const unsigned char *iv, size_t iv_size, const unsigned char *kdf_seed) + const unsigned char *iv, size_t iv_size, const unsigned char *kdf_seed) { - GOST2015_CIPHER_PARAMS *gcp = GOST2015_CIPHER_PARAMS_new(); - int ret = 0, len = 0; + GOST2015_CIPHER_PARAMS *gcp = GOST2015_CIPHER_PARAMS_new(); + int ret = 0, len = 0; - ASN1_OCTET_STRING *os = NULL; - unsigned char ukm_buf[MAX_GOST2015_UKM_SIZE]; - unsigned char *buf = NULL; + ASN1_OCTET_STRING *os = NULL; + unsigned char ukm_buf[MAX_GOST2015_UKM_SIZE]; + unsigned char *buf = NULL; - if (gcp == NULL) { - GOSTerr(GOST_F_GOST2015_SET_ASN1_PARAMS, ERR_R_MALLOC_FAILURE); - return 0; - } + if (gcp == NULL) { + GOSTerr(GOST_F_GOST2015_SET_ASN1_PARAMS, ERR_R_MALLOC_FAILURE); + return 0; + } - memcpy(ukm_buf, iv, iv_size); - memcpy(ukm_buf+iv_size, kdf_seed, KDF_SEED_SIZE); + memcpy(ukm_buf, iv, iv_size); + memcpy(ukm_buf+iv_size, kdf_seed, KDF_SEED_SIZE); - if (ASN1_STRING_set(gcp->ukm, ukm_buf, iv_size + KDF_SEED_SIZE) == 0) { - GOSTerr(GOST_F_GOST2015_SET_ASN1_PARAMS, ERR_R_MALLOC_FAILURE); - goto end; - } + if (ASN1_STRING_set(gcp->ukm, ukm_buf, iv_size + KDF_SEED_SIZE) == 0) { + GOSTerr(GOST_F_GOST2015_SET_ASN1_PARAMS, ERR_R_MALLOC_FAILURE); + goto end; + } - len = i2d_GOST2015_CIPHER_PARAMS(gcp, &buf); + len = i2d_GOST2015_CIPHER_PARAMS(gcp, &buf); - if (len <= 0 - || (os = ASN1_OCTET_STRING_new()) == NULL - || ASN1_OCTET_STRING_set(os, buf, len) == 0) { - goto end; + if (len <= 0 + || (os = ASN1_OCTET_STRING_new()) == NULL + || ASN1_OCTET_STRING_set(os, buf, len) == 0) { + goto end; } - ASN1_TYPE_set(params, V_ASN1_SEQUENCE, os); - ret = 1; + ASN1_TYPE_set(params, V_ASN1_SEQUENCE, os); + ret = 1; end: - OPENSSL_free(buf); - if (ret <= 0 && os) - ASN1_OCTET_STRING_free(os); + OPENSSL_free(buf); + if (ret <= 0 && os) + ASN1_OCTET_STRING_free(os); - GOST2015_CIPHER_PARAMS_free(gcp); - return ret; + GOST2015_CIPHER_PARAMS_free(gcp); + return ret; } -int gost2015_process_unprotected_attributes(STACK_OF(X509_ATTRIBUTE) *attrs, - int encryption, size_t mac_len, unsigned char *final_tag) +int gost2015_process_unprotected_attributes( + STACK_OF(X509_ATTRIBUTE) *attrs, + int encryption, size_t mac_len, unsigned char *final_tag) { - if (encryption == 0) /*Decrypting*/ { - ASN1_OCTET_STRING *osExpectedMac = X509at_get0_data_by_OBJ(attrs, - OBJ_txt2obj(OID_GOST_CMS_MAC, 1), -3, V_ASN1_OCTET_STRING); - - if (!osExpectedMac || osExpectedMac->length != (int)mac_len) - return -1; - - memcpy(final_tag, osExpectedMac->data, osExpectedMac->length); - } else { - if (attrs == NULL) - return -1; - return (X509at_add1_attr_by_OBJ(&attrs, OBJ_txt2obj(OID_GOST_CMS_MAC, 1), - V_ASN1_OCTET_STRING, final_tag, mac_len) == NULL) ? -1 : 1; - } - return 1; + if (encryption == 0) /*Decrypting*/ { + ASN1_OCTET_STRING *osExpectedMac = X509at_get0_data_by_OBJ(attrs, + OBJ_txt2obj(OID_GOST_CMS_MAC, 1), -3, V_ASN1_OCTET_STRING); + + if (!osExpectedMac || osExpectedMac->length != (int)mac_len) + return -1; + + memcpy(final_tag, osExpectedMac->data, osExpectedMac->length); + } else { + if (attrs == NULL) + return -1; + return (X509at_add1_attr_by_OBJ(&attrs, + OBJ_txt2obj(OID_GOST_CMS_MAC, 1), + V_ASN1_OCTET_STRING, final_tag, + mac_len) == NULL) ? -1 : 1; + } + return 1; } int gost2015_acpkm_omac_init(int nid, int enc, const unsigned char *inkey, EVP_MD_CTX *omac_ctx, unsigned char *outkey, unsigned char *kdf_seed) { - int ret = 0; - unsigned char keys[64]; - const EVP_MD *md = EVP_get_digestbynid(nid); - EVP_PKEY *mac_key; + int ret = 0; + unsigned char keys[64]; + const EVP_MD *md = EVP_get_digestbynid(nid); + EVP_PKEY *mac_key; - if (md == NULL) - return 0; + if (md == NULL) + return 0; - if (enc) { - if (RAND_bytes(kdf_seed, 8) != 1) - return 0; - } + if (enc) { + if (RAND_bytes(kdf_seed, 8) != 1) + return 0; + } - if (gost_kdftree2012_256(keys, 64, inkey, 32, (const unsigned char *)"kdf tree", 8, kdf_seed, 8, 1) <= 0) - return 0; + if (gost_kdftree2012_256(keys, 64, inkey, 32, + (const unsigned char *)"kdf tree", 8, kdf_seed, 8, 1) <= 0) + return 0; - mac_key = EVP_PKEY_new_mac_key(nid, NULL, keys+32, 32); + mac_key = EVP_PKEY_new_mac_key(nid, NULL, keys+32, 32); - if (mac_key == NULL) - goto end; + if (mac_key == NULL) + goto end; - if (EVP_DigestInit_ex(omac_ctx, md, NULL) <= 0 || - EVP_DigestSignInit(omac_ctx, NULL, md, NULL, mac_key) <= 0) - goto end; + if (EVP_DigestInit_ex(omac_ctx, md, NULL) <= 0 || + EVP_DigestSignInit(omac_ctx, NULL, md, NULL, mac_key) <= 0) + goto end; - memcpy(outkey, keys, 32); + memcpy(outkey, keys, 32); - ret = 1; + ret = 1; end: - EVP_PKEY_free(mac_key); - OPENSSL_cleanse(keys, sizeof(keys)); + EVP_PKEY_free(mac_key); + OPENSSL_cleanse(keys, sizeof(keys)); - return ret; + return ret; } int init_zero_kdf_seed(unsigned char *kdf_seed) { - int is_zero_kdfseed = 1, i; - for (i = 0; i < 8; i++) { - if (kdf_seed[i] != 0) - is_zero_kdfseed = 0; - } + int is_zero_kdfseed = 1, i; + for (i = 0; i < 8; i++) { + if (kdf_seed[i] != 0) + is_zero_kdfseed = 0; + } + + return is_zero_kdfseed ? RAND_bytes(kdf_seed, 8) : 1; +} + +void gost_mgm128_init(mgm128_context *ctx, void *key, block128_f block, mul128_f mul_gf, int blen) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->block = block; + ctx->mul_gf = mul_gf; + ctx->key = key; + ctx->blocklen = blen; + + /* some precalculations place here + * + */ +} + +int gost_mgm128_setiv(mgm128_context *ctx, const unsigned char *iv, + size_t len) +{ + ctx->len.u[0] = 0; /* AAD length */ + ctx->len.u[1] = 0; /* message length */ + ctx->ares = 0; + ctx->mres = 0; + + ctx->ACi.u[0] = 0; + ctx->ACi.u[1] = 0; + ctx->sum.u[0] = 0; + ctx->sum.u[1] = 0; + + memcpy(ctx->nonce.c, iv, ctx->blocklen); + ctx->nonce.c[0] &= 0x7f; /* IV - random vector, but 1st bit should be 0 */ + return 1; +} + +int gost_mgm128_aad(mgm128_context *ctx, const unsigned char *aad, + size_t len) +{ + size_t i; + unsigned int n; + uint64_t alen = ctx->len.u[0]; + block128_f block = ctx->block; + mul128_f mul_gf = ctx->mul_gf; + void *key = ctx->key; + int bl = ctx->blocklen; + + if (ctx->len.u[1]) { + GOSTerr(GOST_F_GOST_MGM128_AAD, + GOST_R_BAD_ORDER); + return -2; + } + + if (alen == 0) { + ctx->nonce.c[0] |= 0x80; + (*block) (ctx->nonce.c, ctx->Zi.c, key); // Z_1 = E_K(1 || nonce) + } + + alen += len; + if (alen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) || // < 2^(n/2) (len stores in bytes) + (sizeof(len) == 8 && alen < len)) { + GOSTerr(GOST_F_GOST_MGM128_AAD, + GOST_R_DATA_TOO_LARGE); + return -1; + } + ctx->len.u[0] = alen; + + n = ctx->ares; + if (n) { + /* Finalize partial_data */ + while (n && len) { + ctx->ACi.c[n] = *(aad++); + --len; + n = (n + 1) % bl; + } + if (n == 0) { + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) A_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + } else { + ctx->ares = n; + return 0; + } + } + while (len >= bl) { + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, (uint64_t *)aad); // H_i (x) A_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + aad += bl; + len -= bl; + } + if (len) { + n = (unsigned int)len; + for (i = 0; i < len; ++i) + ctx->ACi.c[i] = aad[i]; + } + + ctx->ares = n; + return 0; +} + +int gost_mgm128_encrypt(mgm128_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + size_t i; + unsigned int n, mres; + uint64_t alen = ctx->len.u[0]; + uint64_t mlen = ctx->len.u[1]; + block128_f block = ctx->block; + mul128_f mul_gf = ctx->mul_gf; + void *key = ctx->key; + int bl = ctx->blocklen; + + if (mlen == 0) { + if (alen == 0) { + ctx->nonce.c[0] |= 0x80; + (*block) (ctx->nonce.c, ctx->Zi.c, key); // Z_1 = E_K(1 || nonce) + } + ctx->nonce.c[0] &= 0x7f; + (*block) (ctx->nonce.c, ctx->Yi.c, key); // Y_1 = E_K(0 || nonce) + } + + mlen += len; + + if (mlen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) || // < 2^(n/2) (len stores in bytes) + (sizeof(len) == 8 && mlen < len) || + (mlen + alen) > ((ossl_uintmax_t)(1) << (bl * 4 - 3))) { + GOSTerr(GOST_F_GOST_MGM128_ENCRYPT, + GOST_R_DATA_TOO_LARGE); + return -1; + } + ctx->len.u[1] = mlen; + + mres = ctx->mres; + + if (ctx->ares) { + /* First call to encrypt finalizes AAD */ + memset(ctx->ACi.c + ctx->ares, 0, bl - ctx->ares); + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) A_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + + ctx->ares = 0; + } + + n = mres % bl; + // TODO: replace with full blocks processing + for (i = 0; i < len; ++i) { + if (n == 0) { + (*block) (ctx->Yi.c, ctx->EKi.c, key); // E_K(Y_i) + inc_counter(ctx->Yi.c + bl / 2, bl / 2); // Y_i = incr_r(Y_{i-1}) + } + ctx->ACi.c[n] = out[i] = in[i] ^ ctx->EKi.c[n]; // C_i = P_i (xor) E_K(Y_i) + mres = n = (n + 1) % bl; + if (n == 0) { + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) C_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + } + } + + ctx->mres = mres; + return 0; +} + +int gost_mgm128_decrypt(mgm128_context *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + size_t i; + unsigned int n, mres; + uint64_t alen = ctx->len.u[0]; + uint64_t mlen = ctx->len.u[1]; + block128_f block = ctx->block; + mul128_f mul_gf = ctx->mul_gf; + void *key = ctx->key; + int bl = ctx->blocklen; + + if (mlen == 0) { + ctx->nonce.c[0] &= 0x7f; + (*block) (ctx->nonce.c, ctx->Yi.c, key); // Y_1 = E_K(0 || nonce) + } + + mlen += len; + if (mlen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) || // < 2^(n/2) (len stores in bytes) + (sizeof(len) == 8 && mlen < len) || + (mlen + alen) > ((ossl_uintmax_t)(1) << (bl * 4 - 3))) { + GOSTerr(GOST_F_GOST_MGM128_DECRYPT, + GOST_R_DATA_TOO_LARGE); + return -1; + } + ctx->len.u[1] = mlen; + + mres = ctx->mres; + + if (ctx->ares) { + /* First call to encrypt finalizes AAD */ + memset(ctx->ACi.c + ctx->ares, 0, bl - ctx->ares); + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) A_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + + ctx->ares = 0; + } + + n = mres % bl; + // TODO: replace with full blocks processing + for (i = 0; i < len; ++i) { + uint8_t c; + if (n == 0) { + (*block) (ctx->Yi.c, ctx->EKi.c, key); // E_K(Y_i) + inc_counter(ctx->Yi.c + bl / 2, bl / 2); // Y_i = incr_r(Y_{i-1}) + } + ctx->ACi.c[n] = c = in[i]; + out[i] = c ^ ctx->EKi.c[n]; // P_i = C_i (xor) E_K(Y_i) + mres = n = (n + 1) % bl; + if (n == 0) { + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) C_i + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + } + } - return is_zero_kdfseed ? RAND_bytes(kdf_seed, 8) : 1; + ctx->mres = mres; + return 0; +} + +int gost_mgm128_finish(mgm128_context *ctx, const unsigned char *tag, + size_t len) +{ + uint64_t alen = ctx->len.u[0] << 3; + uint64_t clen = ctx->len.u[1] << 3; + block128_f block = ctx->block; + mul128_f mul_gf = ctx->mul_gf; + void *key = ctx->key; + int bl = ctx->blocklen; + + if (ctx->mres || ctx->ares) { + /* First call to encrypt finalizes AAD/ENC */ + memset(ctx->ACi.c + ctx->ares + ctx->mres, 0, bl - (ctx->ares + ctx->mres)); + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u); // H_i (x) [A_i or C_i] + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + inc_counter(ctx->Zi.c, bl / 2); // Z_{i+1} = incr_l(Z_i) + } + +#ifdef L_ENDIAN + alen = BSWAP64(alen); + clen = BSWAP64(clen); +#endif + if (bl == 16) { + ctx->len.u[0] = alen; + ctx->len.u[1] = clen; + } else { +#ifdef L_ENDIAN + ctx->len.u[0] = (alen >> 32) | clen; +#else + ctx->len.u[0] = (alen << 32) | clen; +#endif + ctx->len.u[1] = 0; + } + + (*block) (ctx->Zi.c, ctx->Hi.c, key); // H_i = E_K(Z_i) + mul_gf(ctx->mul.u, ctx->Hi.u, ctx->len.u); // H_i (x) (len(A) || len(C)) + grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u, // acc XOR + (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u); + (*block) (ctx->sum.c, ctx->tag.c, key); // E_K(sum) + + if (tag && len <= sizeof(ctx->tag)) + return CRYPTO_memcmp(ctx->tag.c, tag, len); // MSB_S(E_K(sum)) + else + return -1; +} + +void gost_mgm128_tag(mgm128_context *ctx, unsigned char *tag, size_t len) +{ + gost_mgm128_finish(ctx, NULL, 0); + memcpy(tag, ctx->tag.c, + len <= sizeof(ctx->tag.c) ? len : sizeof(ctx->tag.c)); }