/* * Copyright (C) 2021 - This file is part of libecc project * * Authors: * Ryad BENADJILA * Arnaud EBALARD * * This software is licensed under a dual BSD and GPL v2 license. * See LICENSE file at the root folder of the project. */ #include "gostr34_11_94.h" /* The 8 4-bit GOST block cipher encryption SBOX */ static const u8 gostr34_11_94_sbox_norm[8][16] = { { 4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3 }, { 14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9 }, { 5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11 }, { 7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3 }, { 6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2 }, { 4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14 }, { 13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12 }, { 1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12 } }; static const u8 gostr34_11_94_sbox_rfc4357[8][16] = { { 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15}, { 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8}, { 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13}, { 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3}, { 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5}, { 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3}, { 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11}, { 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12} }; /* Endianness handling */ ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_arch_is_big_endian(void) { const u16 val = 0x0102; const u8 *buf = (const u8 *)(&val); return (buf[0] == 0x01); } /* A and P linear transformations */ static inline void gostr34_11_94_A(const u64 Y[GOSTR34_11_94_STATE_SIZE], u64 Y_[GOSTR34_11_94_STATE_SIZE]) { u64 y1, y2, y3, y4; y1 = Y[3]; y2 = Y[2]; y3 = Y[1]; y4 = Y[0]; Y_[0] = (y1 ^ y2); Y_[1] = y4; Y_[2] = y3; Y_[3] = y2; return; } static inline void gostr34_11_94_P(const u64 Y[GOSTR34_11_94_STATE_SIZE], u64 Y_[GOSTR34_11_94_STATE_SIZE]) { unsigned int i, k; const u8 *y = (const u8*)Y; u8 *y_ = (u8*)Y_; for(i = 0; i < 4; i++){ for(k = 1; k < 9; k++){ unsigned int phi_idx = (8 * GOSTR34_11_94_STATE_SIZE) - (i + (4 * (k - 1))); unsigned int phi = ((8 * i) + k); y_[phi_idx - 1] = y[phi - 1]; } } return; } /* GOSTR34_11_94 key generation constants */ static const u64 gostr34_11_94_C[3][GOSTR34_11_94_STATE_SIZE] = { { 0, 0, 0, 0 }, { 0xff000000ffff00ffULL, 0x00ffff00ff0000ffULL, 0xff00ff00ff00ff00ULL, 0x00ff00ff00ff00ffULL }, { 0, 0, 0, 0 }, }; /* GOSTR34_11_94 key generation */ ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_key_generation(const u64 H[GOSTR34_11_94_STATE_SIZE], const u64 M[GOSTR34_11_94_STATE_SIZE], u64 K[4][GOSTR34_11_94_STATE_SIZE]) { /* U, V, W */ u64 U[GOSTR34_11_94_STATE_SIZE], V[GOSTR34_11_94_STATE_SIZE], W[GOSTR34_11_94_STATE_SIZE]; unsigned int i, j; int ret; /* U = H */ ret = local_memcpy(U, H, sizeof(U)); EG(ret, err); /* V = M */ ret = local_memcpy(V, M, sizeof(V)); EG(ret, err); /* W = U ^ V */ for(j = 0; j < GOSTR34_11_94_STATE_SIZE; j++){ W[j] = (U[j] ^ V[j]); } /* K1 = P(W) */ gostr34_11_94_P(W, K[0]); for(i = 1; i < 4; i++){ /* U = A(U) ^ C */ gostr34_11_94_A(U, U); for(j = 0; j < GOSTR34_11_94_STATE_SIZE; j++){ u64 C; GET_UINT64_LE(C, (const u8*)&gostr34_11_94_C[i - 1][j], 0); U[j] = (u64)(U[j] ^ C); } /* V = A(A(V)) */ gostr34_11_94_A(V, V); gostr34_11_94_A(V, V); /* W = U ^ V */ for(j = 0; j < GOSTR34_11_94_STATE_SIZE; j++){ W[j] = (u64)(U[j] ^ V[j]); } /* Ki = P(W) */ gostr34_11_94_P(W, K[i]); } ret = 0; err: return ret; } /* GOSTR34_11_94 state encryption */ ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_block_encryption(const u64 K[GOSTR34_11_94_STATE_SIZE], const u64 P, u64 *E, const u8 sbox[8][16]) { int ret; unsigned int round, i; u32 R_i, L_i, R_i1 = 0, L_i1 = 0; const u8 *p = (const u8*)&P; u8 *e = (u8*)E; MUST_HAVE((K != NULL) && (sbox != NULL) && (E != NULL), ret, err); /* The encryption is a Feistel network */ GET_UINT32_BE(L_i, p, 0); GET_UINT32_BE(R_i, p, 4); for(round = 0; round < 32; round++){ u32 sk; const u8 *k = (const u8*)K; u8 *r_i1 = (u8 *)&R_i1; /* Key schedule */ if(round < 24){ GET_UINT32_LE(sk, k, (4 * (round % 8))); } else{ GET_UINT32_LE(sk, k, (4 * (7 - (round % 8)))); } /*** Feistel round ***/ R_i1 = (u32)(R_i + sk); /* add round key */ /* SBox layer */ for(i = 0; i < 4; i++){ unsigned int sb_idx; if(gostr34_11_94_arch_is_big_endian()){ sb_idx = (2 * (3 - i)); } else{ sb_idx = (2 * i); } r_i1[i] = (u8)((sbox[sb_idx + 1][(r_i1[i] & 0xf0) >> 4] << 4) | (sbox[sb_idx][(r_i1[i] & 0x0f)])); } /* Rotation by 11 and XOR with L */ R_i1 = (u32)(ROTL_GOSTR34_11_94(R_i1, 11) ^ L_i); /* Feistel */ L_i1 = R_i; /* Next round */ R_i = R_i1; L_i = L_i1; } /* Output */ PUT_UINT32_LE(L_i1, e, 0); PUT_UINT32_LE(R_i1, e, 4); ret = 0; err: return ret; } ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_state_encryption(const u64 K[4][GOSTR34_11_94_STATE_SIZE], const u64 H[GOSTR34_11_94_STATE_SIZE], u64 S[GOSTR34_11_94_STATE_SIZE], const u8 sbox[8][16]) { int ret; MUST_HAVE((GOSTR34_11_94_STATE_SIZE == 4), ret, err); /* Return S = s4 s3 s2 s1 */ /* s1 = E(h1, K1) */ ret = gostr34_11_94_block_encryption(K[0], H[3], &S[0], sbox); EG(ret, err); /* s2 = E(h2, K2) */ ret = gostr34_11_94_block_encryption(K[1], H[2], &S[1], sbox); EG(ret, err); /* s3 = E(h3, K3) */ ret = gostr34_11_94_block_encryption(K[2], H[1], &S[2], sbox); EG(ret, err); /* s4 = E(h4, K4) */ ret = gostr34_11_94_block_encryption(K[3], H[0], &S[3], sbox); EG(ret, err); ret = 0; err: return ret; } /* * NOTE: we use a somehow "artificial" union here in order to deal with * possible alignment issues in the gostr34_11_94_state_psi function * (as we have to interpret an array of 4 u64 into an array of 16 u16 * in order to apply our Psi function). */ typedef union { u64 A[GOSTR34_11_94_STATE_SIZE]; u16 B[16]; } gostr34_11_94_union; /* GOSTR34_11_94 output transformation */ ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_state_psi(const u64 G[GOSTR34_11_94_STATE_SIZE], u64 G_[GOSTR34_11_94_STATE_SIZE]) { int ret; unsigned int i; /* Use our unions in order to deal with alignment issues * (see the rationale above). */ gostr34_11_94_union G_copy; gostr34_11_94_union *g = &G_copy; gostr34_11_94_union *g_ = (gostr34_11_94_union*)G_; /* Better safe than sorry ... */ MUST_HAVE((sizeof(gostr34_11_94_union) == (sizeof(u64) * GOSTR34_11_94_STATE_SIZE)), ret, err); /* Copy input */ ret = local_memcpy(g, G, sizeof(gostr34_11_94_union)); EG(ret, err); /* ψ(Γ) = (γ0 ⊕ γ1 ⊕ γ2 ⊕ γ3 ⊕ γ12 ⊕ γ15) γ15 γ14 · · · γ1 * where Γ is split into sixteen 16-bit words, i.e. Γ = γ15 γ14 · · · γ0. */ for(i = 0; i < 15; i++){ g_->B[i] = g->B[i + 1]; } g_->B[15] = (u16)((g->B[0]) ^ (g->B[1]) ^ (g->B[2]) ^ (g->B[3]) ^ (g->B[12]) ^ (g->B[15])); ret = 0; err: return ret; } ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_state_output_transform(const u64 H[GOSTR34_11_94_STATE_SIZE], const u64 S[GOSTR34_11_94_STATE_SIZE], const u64 M[GOSTR34_11_94_STATE_SIZE], u64 H_[GOSTR34_11_94_STATE_SIZE]) { unsigned int i; int ret; /* Compute psi^12 of S */ ret = local_memcpy(H_, S, GOSTR34_11_94_STATE_SIZE * sizeof(u64)); EG(ret, err); for(i = 0; i < 12; i++){ ret = gostr34_11_94_state_psi(H_, H_); EG(ret, err); } /* Compute M xor psi^12 */ for(i = 0; i < GOSTR34_11_94_STATE_SIZE; i++){ u64 m; if(gostr34_11_94_arch_is_big_endian()){ GET_UINT64_LE(m, (const u8*)&M[GOSTR34_11_94_STATE_SIZE - i - 1], 0); } else{ GET_UINT64_BE(m, (const u8*)&M[GOSTR34_11_94_STATE_SIZE - i - 1], 0); } H_[i] = (u64)(H_[i] ^ m); } ret = gostr34_11_94_state_psi(H_, H_); EG(ret, err); /* Xor it with H */ for(i = 0; i < GOSTR34_11_94_STATE_SIZE; i++){ u64 h; if(gostr34_11_94_arch_is_big_endian()){ GET_UINT64_LE(h, (const u8*)&H[GOSTR34_11_94_STATE_SIZE - i - 1], 0); } else{ GET_UINT64_BE(h, (const u8*)&H[GOSTR34_11_94_STATE_SIZE - i - 1], 0); } H_[i] = (u64)(H_[i] ^ h); } /* Now compute psi^61 */ for(i = 0; i < 61; i++){ ret = gostr34_11_94_state_psi(H_, H_); EG(ret, err); } ret = 0; err: return ret; } /* GOSTR34_11_94 256-bit words summing (a simple adder with carry in constant time) */ static inline void gostr34_11_94_256bit_sum(const u64 A[GOSTR34_11_94_STATE_SIZE], const u64 B[GOSTR34_11_94_STATE_SIZE], u64 C[GOSTR34_11_94_STATE_SIZE]) { unsigned int i; u64 tmp, carry1, carry2, _carry; _carry = 0; for(i = 0; i < GOSTR34_11_94_STATE_SIZE; i++){ u64 a, b, c; unsigned int idx = (GOSTR34_11_94_STATE_SIZE - i - 1); GET_UINT64_BE(a, (const u8*)(&A[idx]), 0); GET_UINT64_BE(b, (const u8*)(&B[idx]), 0); tmp = (u64)(a + b); carry1 = (u64)(tmp < a); c = (u64)(tmp + _carry); carry2 = (u64)(c < tmp); _carry = (u64)(carry1 | carry2); PUT_UINT64_BE(c, (u8*)(&C[idx]), 0); } return; } /* GOSTR34_11_94 core processing. Returns 0 on success, -1 on error. */ ATTRIBUTE_WARN_UNUSED_RET static inline int gostr34_11_94_process(gostr34_11_94_context *ctx, const u8 data[GOSTR34_11_94_BLOCK_SIZE]) { int ret; unsigned int i; u64 K[4][GOSTR34_11_94_STATE_SIZE]; u64 H[GOSTR34_11_94_STATE_SIZE], S[GOSTR34_11_94_STATE_SIZE], M[GOSTR34_11_94_STATE_SIZE]; MUST_HAVE((data != NULL), ret, err); GOSTR34_11_94_HASH_CHECK_INITIALIZED(ctx, ret, err); /* Get our local data in little endian format */ for(i = 0; i < GOSTR34_11_94_BLOCK_SIZE; i++){ ((u8*)M)[i] = data[GOSTR34_11_94_BLOCK_SIZE - i - 1]; } /* Get the saved state */ for(i = 0; i < GOSTR34_11_94_BLOCK_SIZE; i++){ ((u8*)H)[i] = ((u8*)ctx->gostr34_11_94_state)[GOSTR34_11_94_BLOCK_SIZE - i - 1]; } /* Key generation */ ret = gostr34_11_94_key_generation(H, M, K); EG(ret, err); /* State encryption */ switch(ctx->gostr34_11_94_t){ case GOST34_11_94_NORM:{ ret = gostr34_11_94_state_encryption((const u64 (*)[4])K, H, S, gostr34_11_94_sbox_norm); EG(ret, err); break; } case GOST34_11_94_RFC4357:{ ret = gostr34_11_94_state_encryption((const u64 (*)[4])K, H, S, gostr34_11_94_sbox_rfc4357); EG(ret, err); break; } default:{ ret = -1; goto err; } } /* Output transformation */ ret = gostr34_11_94_state_output_transform(H, S, M, ctx->gostr34_11_94_state); EG(ret, err); /* Update the internal sum */ gostr34_11_94_256bit_sum(ctx->gostr34_11_94_sum, M, ctx->gostr34_11_94_sum); ret = 0; err: return ret; } /* Init hash function. Returns 0 on success, -1 on error. */ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_init(gostr34_11_94_context *ctx) { int ret; MUST_HAVE((ctx != NULL), ret, err); /* Sanity check on size */ MUST_HAVE((GOSTR34_11_94_DIGEST_SIZE <= MAX_DIGEST_SIZE), ret, err); ctx->gostr34_11_94_total = 0; ctx->gostr34_11_94_state[0] = 0; ctx->gostr34_11_94_state[1] = 0; ctx->gostr34_11_94_state[2] = 0; ctx->gostr34_11_94_state[3] = 0; ret = local_memset(ctx->gostr34_11_94_sum, 0, sizeof(ctx->gostr34_11_94_sum)); EG(ret, err); /* Our default GOST34_11_94 type is GOST34_11_94_NORM */ ctx->gostr34_11_94_t = GOST34_11_94_NORM; /* Tell that we are initialized */ ctx->magic = GOSTR34_11_94_HASH_MAGIC; ret = 0; err: return ret; } /* Function to modify the initial IV as it is not imposed by the RFCs */ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_set_iv(gostr34_11_94_context *ctx, const u64 iv[GOSTR34_11_94_STATE_SIZE]) { int ret; MUST_HAVE((iv != NULL), ret, err); GOSTR34_11_94_HASH_CHECK_INITIALIZED(ctx, ret, err); /* We cannot change the IV after the first update */ MUST_HAVE((ctx->gostr34_11_94_total == 0), ret, err); ctx->gostr34_11_94_state[0] = iv[0]; ctx->gostr34_11_94_state[1] = iv[1]; ctx->gostr34_11_94_state[2] = iv[2]; ctx->gostr34_11_94_state[3] = iv[3]; ret = 0; err: return ret; } /* Function to modify the GOST type (that will dictate the underlying SBOX to use for block encryption) */ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_set_type(gostr34_11_94_context *ctx, gostr34_11_94_type type) { int ret; GOSTR34_11_94_HASH_CHECK_INITIALIZED(ctx, ret, err); /* We cannot change the algorithm type after the first update */ MUST_HAVE((ctx->gostr34_11_94_total == 0), ret, err); if((type != GOST34_11_94_NORM) && (type != GOST34_11_94_RFC4357)){ ret = -1; goto err; } ctx->gostr34_11_94_t = type; ret = 0; err: return ret; } ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_update(gostr34_11_94_context *ctx, const u8 *input, u32 ilen) { const u8 *data_ptr = input; u32 remain_ilen = ilen; u16 fill; u8 left; int ret; MUST_HAVE((input != NULL) || (ilen == 0), ret, err); GOSTR34_11_94_HASH_CHECK_INITIALIZED(ctx, ret, err); /* Nothing to process, return */ if (ilen == 0) { ret = 0; goto err; } /* Get what's left in our local buffer */ left = (ctx->gostr34_11_94_total & 0x3F); fill = (u16)(GOSTR34_11_94_BLOCK_SIZE - left); ctx->gostr34_11_94_total += ilen; if ((left > 0) && (remain_ilen >= fill)) { /* Copy data at the end of the buffer */ ret = local_memcpy(ctx->gostr34_11_94_buffer + left, data_ptr, fill); EG(ret, err); ret = gostr34_11_94_process(ctx, ctx->gostr34_11_94_buffer); EG(ret, err); data_ptr += fill; remain_ilen -= fill; left = 0; } while (remain_ilen >= GOSTR34_11_94_BLOCK_SIZE) { ret = gostr34_11_94_process(ctx, data_ptr); EG(ret, err); data_ptr += GOSTR34_11_94_BLOCK_SIZE; remain_ilen -= GOSTR34_11_94_BLOCK_SIZE; } if (remain_ilen > 0) { ret = local_memcpy(ctx->gostr34_11_94_buffer + left, data_ptr, remain_ilen); EG(ret, err); } ret = 0; err: return ret; } /* Finalize. Returns 0 on success, -1 on error.*/ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_final(gostr34_11_94_context *ctx, u8 output[GOSTR34_11_94_DIGEST_SIZE]) { unsigned int block_present = 0; u8 last_padded_block[2 * GOSTR34_11_94_BLOCK_SIZE]; int ret; MUST_HAVE((output != NULL), ret, err); GOSTR34_11_94_HASH_CHECK_INITIALIZED(ctx, ret, err); /* This is our final step, so we proceed with the padding if necessary */ /* Fill in our last block with zeroes */ ret = local_memset(last_padded_block, 0, sizeof(last_padded_block)); EG(ret, err); block_present = ctx->gostr34_11_94_total % GOSTR34_11_94_BLOCK_SIZE; /* Copy what's left in our temporary context buffer */ ret = local_memcpy(last_padded_block, ctx->gostr34_11_94_buffer, block_present); EG(ret, err); /* Put in the second block the size in bits of the message in bits in little endian */ PUT_UINT64_LE(8 * ctx->gostr34_11_94_total, last_padded_block, GOSTR34_11_94_BLOCK_SIZE); if(block_present != 0){ /* Process padding block if necessary */ ret = gostr34_11_94_process(ctx, last_padded_block); EG(ret, err); } /* Copy our sum in the beginning of the block */ if(gostr34_11_94_arch_is_big_endian()){ PUT_UINT64_LE(ctx->gostr34_11_94_sum[3], last_padded_block, 0); PUT_UINT64_LE(ctx->gostr34_11_94_sum[2], last_padded_block, 8); PUT_UINT64_LE(ctx->gostr34_11_94_sum[1], last_padded_block, 16); PUT_UINT64_LE(ctx->gostr34_11_94_sum[0], last_padded_block, 24); } else{ PUT_UINT64_BE(ctx->gostr34_11_94_sum[3], last_padded_block, 0); PUT_UINT64_BE(ctx->gostr34_11_94_sum[2], last_padded_block, 8); PUT_UINT64_BE(ctx->gostr34_11_94_sum[1], last_padded_block, 16); PUT_UINT64_BE(ctx->gostr34_11_94_sum[0], last_padded_block, 24); } /* Process the "size" in bits block */ ret = gostr34_11_94_process(ctx, last_padded_block + GOSTR34_11_94_BLOCK_SIZE); EG(ret, err); /* Process the message blocks sum */ ret = gostr34_11_94_process(ctx, last_padded_block); EG(ret, err); /* Output the hash result */ if(gostr34_11_94_arch_is_big_endian()){ PUT_UINT64_BE(ctx->gostr34_11_94_state[0], output, 0); PUT_UINT64_BE(ctx->gostr34_11_94_state[1], output, 8); PUT_UINT64_BE(ctx->gostr34_11_94_state[2], output, 16); PUT_UINT64_BE(ctx->gostr34_11_94_state[3], output, 24); } else{ PUT_UINT64_LE(ctx->gostr34_11_94_state[0], output, 0); PUT_UINT64_LE(ctx->gostr34_11_94_state[1], output, 8); PUT_UINT64_LE(ctx->gostr34_11_94_state[2], output, 16); PUT_UINT64_LE(ctx->gostr34_11_94_state[3], output, 24); } /* Tell that we are uninitialized */ ctx->magic = WORD(0); ret = 0; err: return ret; } /* * Scattered version performing init/update/finalize on a vector of buffers * 'inputs' with the length of each buffer passed via 'ilens'. The function * loops on pointers in 'inputs' until it finds a NULL pointer. The function * returns 0 on success, -1 on error. */ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_scattered(const u8 **inputs, const u32 *ilens, u8 output[GOSTR34_11_94_DIGEST_SIZE], gostr34_11_94_type type) { gostr34_11_94_context ctx; int ret, pos = 0; MUST_HAVE((inputs != NULL) && (ilens != NULL) && (output != NULL), ret, err); ret = gostr34_11_94_init(&ctx); EG(ret, err); ret = gostr34_11_94_set_type(&ctx, type); EG(ret, err); while (inputs[pos] != NULL) { ret = gostr34_11_94_update(&ctx, inputs[pos], ilens[pos]); EG(ret, err); pos += 1; } ret = gostr34_11_94_final(&ctx, output); err: return ret; } ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_scattered_norm(const u8 **inputs, const u32 *ilens, u8 output[GOSTR34_11_94_DIGEST_SIZE]) { return gostr34_11_94_scattered(inputs, ilens, output, GOST34_11_94_NORM); } ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_scattered_rfc4357(const u8 **inputs, const u32 *ilens, u8 output[GOSTR34_11_94_DIGEST_SIZE]) { return gostr34_11_94_scattered(inputs, ilens, output, GOST34_11_94_RFC4357); } /* * Single call version performing init/update/final on given input. * Returns 0 on success, -1 on error. */ ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94(const u8 *input, u32 ilen, u8 output[GOSTR34_11_94_DIGEST_SIZE], gostr34_11_94_type type) { gostr34_11_94_context ctx; int ret; ret = gostr34_11_94_init(&ctx); EG(ret, err); ret = gostr34_11_94_set_type(&ctx, type); EG(ret, err); ret = gostr34_11_94_update(&ctx, input, ilen); EG(ret, err); ret = gostr34_11_94_final(&ctx, output); err: return ret; } ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_norm(const u8 *input, u32 ilen, u8 output[GOSTR34_11_94_DIGEST_SIZE]) { return gostr34_11_94(input, ilen, output, GOST34_11_94_NORM); } ATTRIBUTE_WARN_UNUSED_RET int gostr34_11_94_rfc4357(const u8 *input, u32 ilen, u8 output[GOSTR34_11_94_DIGEST_SIZE]) { return gostr34_11_94(input, ilen, output, GOST34_11_94_RFC4357); }