+ 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)
+ }
+ }