diff options
author | igrkir <i.kirillov@kryptonite.ru> | 2022-09-23 15:32:21 +0300 |
---|---|---|
committer | Dmitry Belyavskiy <beldmit@users.noreply.github.com> | 2022-12-02 18:56:55 +0100 |
commit | 39dc6de6a9474e10560ebfb0a9cecc05867b9c7b (patch) | |
tree | 6fa646e6b18d5f43afe56fa9d8db64b042b710c0 | |
parent | 97b3db1ebf985b73718faaae6c425782d526d44f (diff) | |
download | gost-engine-39dc6de6a9474e10560ebfb0a9cecc05867b9c7b.zip gost-engine-39dc6de6a9474e10560ebfb0a9cecc05867b9c7b.tar.gz gost-engine-39dc6de6a9474e10560ebfb0a9cecc05867b9c7b.tar.bz2 |
Implementation of the MGM mode for magma/kuznyechik
first implemented in ossl_patched branch
uses dynamic NIDs
Signed-off-by: Sergei Ianovich <sergei.ianovich@ya.ru>
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | e_gost_err.c | 12 | ||||
-rw-r--r-- | e_gost_err.h | 9 | ||||
-rw-r--r-- | gost.txt | 16 | ||||
-rw-r--r-- | gost_crypt.c | 277 | ||||
-rw-r--r-- | gost_ec_keyx.c | 2 | ||||
-rw-r--r-- | gost_eng.c | 4 | ||||
-rw-r--r-- | gost_gost2015.c | 287 | ||||
-rw-r--r-- | gost_gost2015.h | 65 | ||||
-rw-r--r-- | gost_grasshopper_cipher.c | 291 | ||||
-rw-r--r-- | gost_grasshopper_cipher.h | 13 | ||||
-rw-r--r-- | gost_lcl.h | 5 | ||||
-rw-r--r-- | tcl_tests/engine.try | 2 | ||||
-rw-r--r-- | test/00-engine.t | 2 | ||||
-rw-r--r-- | test_mgm.c | 267 |
15 files changed, 1247 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index b4e6aea..d21dfc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,6 +295,12 @@ add_executable(test_gost89 test_gost89.c) target_link_libraries(test_gost89 gost_core gost_err) add_test(NAME gost89 COMMAND test_gost89) +add_executable(test_mgm test_mgm.c) +target_link_libraries(test_mgm gost_core gost_err) +add_test(NAME mgm COMMAND test_mgm) +set_tests_properties(mgm + PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_ENGINE}") + if(NOT SKIP_PERL_TESTS) execute_process(COMMAND perl -MTest2::V0 -e "" ERROR_QUIET RESULT_VARIABLE MISSING_TEST2_V0) @@ -328,6 +334,7 @@ set(BINARY_TESTS_TARGETS test_keyexpimp test_gost89 test_tls + test_mgm ) set_property(TARGET ${BINARY_TESTS_TARGETS} APPEND PROPERTY COMPILE_DEFINITIONS ENGINE_DIR="${OUTPUT_DIRECTORY}") diff --git a/e_gost_err.c b/e_gost_err.c index c6d221b..6251b78 100644 --- a/e_gost_err.c +++ b/e_gost_err.c @@ -54,8 +54,12 @@ static ERR_STRING_DATA GOST_str_functs[] = { "gost_grasshopper_cipher_ctl"}, {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_CIPHER_DO_CTRACPKM_OMAC, 0), "gost_grasshopper_cipher_do_ctracpkm_omac"}, + {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_CTRACPKM_OMAC, 0), "gost_grasshopper_cipher_init_ctracpkm_omac"}, + {ERR_PACK(0, GOST_F_GOST_GRASSHOPPER_MGM_CTRL, 0), + "gost_grasshopper_mgm_ctrl"}, {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"}, @@ -64,6 +68,12 @@ static ERR_STRING_DATA GOST_str_functs[] = { {ERR_PACK(0, GOST_F_GOST_KDFTREE2012_256, 0), "gost_kdftree2012_256"}, {ERR_PACK(0, GOST_F_GOST_KEXP15, 0), "gost_kexp15"}, {ERR_PACK(0, GOST_F_GOST_KIMP15, 0), "gost_kimp15"}, + {ERR_PACK(0, GOST_F_GOST_MAGMA_CIPHER_DO_MGM, 0), + "gost_magma_cipher_do_mgm"}, + {ERR_PACK(0, GOST_F_GOST_MAGMA_MGM_CTRL, 0), "gost_magma_mgm_ctrl"}, + {ERR_PACK(0, GOST_F_GOST_MGM128_AAD, 0), "gost_mgm128_aad"}, + {ERR_PACK(0, GOST_F_GOST_MGM128_DECRYPT, 0), "gost_mgm128_decrypt"}, + {ERR_PACK(0, GOST_F_GOST_MGM128_ENCRYPT, 0), "gost_mgm128_encrypt"}, {ERR_PACK(0, GOST_F_MAGMA_CIPHER_CTL, 0), "magma_cipher_ctl"}, {ERR_PACK(0, GOST_F_MAGMA_CIPHER_CTL_ACPKM_OMAC, 0), "magma_cipher_ctl_acpkm_omac"}, @@ -129,6 +139,7 @@ static ERR_STRING_DATA GOST_str_reasons[] = { "cannot unpack ephemeral key"}, {ERR_PACK(0, 0, GOST_R_CIPHER_NOT_FOUND), "cipher not found"}, {ERR_PACK(0, 0, GOST_R_CTRL_CALL_FAILED), "ctrl call failed"}, + {ERR_PACK(0, 0, GOST_R_DATA_TOO_LARGE), "data too large"}, {ERR_PACK(0, 0, GOST_R_ERROR_COMPUTING_EXPORT_KEYS), "error computing export keys"}, {ERR_PACK(0, 0, GOST_R_ERROR_COMPUTING_SHARED_KEY), @@ -153,6 +164,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 6f6ea1b..6c24180 100644 --- a/e_gost_err.h +++ b/e_gost_err.h @@ -48,7 +48,9 @@ void ERR_GOST_error(int function, int reason, char *file, int line); # define GOST_F_GOST_ENCODE_CMS_PARAMS 161 # define GOST_F_GOST_GRASSHOPPER_CIPHER_CTL 111 # define GOST_F_GOST_GRASSHOPPER_CIPHER_DO_CTRACPKM_OMAC 160 +# define GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM 166 # define GOST_F_GOST_GRASSHOPPER_CIPHER_INIT_CTRACPKM_OMAC 162 +# define GOST_F_GOST_GRASSHOPPER_MGM_CTRL 167 # define GOST_F_GOST_GRASSHOPPER_SET_ASN1_PARAMETERS 112 # define GOST_F_GOST_IMIT_CTRL 113 # define GOST_F_GOST_IMIT_FINAL 114 @@ -56,6 +58,11 @@ void ERR_GOST_error(int function, int reason, char *file, int line); # define GOST_F_GOST_KDFTREE2012_256 149 # define GOST_F_GOST_KEXP15 143 # define GOST_F_GOST_KIMP15 148 +# define GOST_F_GOST_MAGMA_CIPHER_DO_MGM 168 +# define GOST_F_GOST_MAGMA_MGM_CTRL 169 +# define GOST_F_GOST_MGM128_AAD 170 +# define GOST_F_GOST_MGM128_DECRYPT 171 +# define GOST_F_GOST_MGM128_ENCRYPT 172 # define GOST_F_MAGMA_CIPHER_CTL 163 # define GOST_F_MAGMA_CIPHER_CTL_ACPKM_OMAC 164 # define GOST_F_MAGMA_CIPHER_INIT_CTR_ACPKM_OMAC 165 @@ -107,6 +114,7 @@ void ERR_GOST_error(int function, int reason, char *file, int line); # define GOST_R_CANNOT_UNPACK_EPHEMERAL_KEY 136 # define GOST_R_CIPHER_NOT_FOUND 103 # define GOST_R_CTRL_CALL_FAILED 104 +# define GOST_R_DATA_TOO_LARGE 141 # define GOST_R_ERROR_COMPUTING_EXPORT_KEYS 135 # define GOST_R_ERROR_COMPUTING_SHARED_KEY 105 # define GOST_R_ERROR_DECODING_PUBLIC_KEY 138 @@ -126,6 +134,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 142 # define GOST_R_KEY_IS_NOT_INITIALIZED 119 # define GOST_R_KEY_PARAMETERS_MISSING 120 # define GOST_R_MAC_KEY_NOT_SET 121 @@ -22,9 +22,14 @@ GOST_F_GOST_EC_COMPUTE_PUBLIC:107:gost_ec_compute_public 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_ENCODE_CMS_PARAMS:161:gost_encode_cms_params GOST_F_GOST_GRASSHOPPER_CIPHER_CTL:111:gost_grasshopper_cipher_ctl GOST_F_GOST_GRASSHOPPER_CIPHER_DO_CTRACPKM_OMAC:160:\ gost_grasshopper_cipher_do_ctracpkm_omac +GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM:166:gost_grasshopper_cipher_do_mgm +GOST_F_GOST_GRASSHOPPER_CIPHER_INIT_CTRACPKM_OMAC:162:\ + gost_grasshopper_cipher_init_ctracpkm_omac +GOST_F_GOST_GRASSHOPPER_MGM_CTRL:167:gost_grasshopper_mgm_ctrl GOST_F_GOST_GRASSHOPPER_SET_ASN1_PARAMETERS:112:\ gost_grasshopper_set_asn1_parameters GOST_F_GOST_IMIT_CTRL:113:gost_imit_ctrl @@ -33,6 +38,14 @@ GOST_F_GOST_IMIT_UPDATE:115:gost_imit_update GOST_F_GOST_KDFTREE2012_256:149:gost_kdftree2012_256 GOST_F_GOST_KEXP15:143:gost_kexp15 GOST_F_GOST_KIMP15:148:gost_kimp15 +GOST_F_GOST_MAGMA_CIPHER_DO_MGM:168:gost_magma_cipher_do_mgm +GOST_F_GOST_MAGMA_MGM_CTRL:169:gost_magma_mgm_ctrl +GOST_F_GOST_MGM128_AAD:170:gost_mgm128_aad +GOST_F_GOST_MGM128_DECRYPT:171:gost_mgm128_decrypt +GOST_F_GOST_MGM128_ENCRYPT:172:gost_mgm128_encrypt +GOST_F_MAGMA_CIPHER_CTL:163:magma_cipher_ctl +GOST_F_MAGMA_CIPHER_CTL_ACPKM_OMAC:164:magma_cipher_ctl_acpkm_omac +GOST_F_MAGMA_CIPHER_INIT_CTR_ACPKM_OMAC:165:magma_cipher_init_ctr_acpkm_omac GOST_F_OMAC_ACPKM_IMIT_CTRL:144:omac_acpkm_imit_ctrl GOST_F_OMAC_ACPKM_IMIT_FINAL:145:omac_acpkm_imit_final GOST_F_OMAC_ACPKM_IMIT_UPDATE:146:omac_acpkm_imit_update @@ -80,6 +93,7 @@ GOST_R_CANNOT_PACK_EPHEMERAL_KEY:102:cannot pack ephemeral key GOST_R_CANNOT_UNPACK_EPHEMERAL_KEY:136:cannot unpack ephemeral key GOST_R_CIPHER_NOT_FOUND:103:cipher not found GOST_R_CTRL_CALL_FAILED:104:ctrl call failed +GOST_R_DATA_TOO_LARGE:141:data too large GOST_R_ERROR_COMPUTING_EXPORT_KEYS:135:error computing export keys GOST_R_ERROR_COMPUTING_SHARED_KEY:105:error computing shared key GOST_R_ERROR_DECODING_PUBLIC_KEY:138:error decoding public key @@ -88,6 +102,7 @@ GOST_R_ERROR_POINT_MUL:107:error point mul GOST_R_ERROR_SETTING_PEER_KEY:139:error setting peer key GOST_R_INCOMPATIBLE_ALGORITHMS:108:incompatible algorithms GOST_R_INCOMPATIBLE_PEER_KEY:109:incompatible peer key +GOST_R_INVALID_BUFFER_SIZE:140:invalid buffer size GOST_R_INVALID_CIPHER:134:invalid cipher GOST_R_INVALID_CIPHER_PARAMS:110:invalid cipher params GOST_R_INVALID_CIPHER_PARAM_OID:111:invalid cipher param oid @@ -98,6 +113,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:142: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_crypt.c b/gost_crypt.c index 0aa2ecf..516e598 100644 --- a/gost_crypt.c +++ b/gost_crypt.c @@ -43,6 +43,7 @@ static int gost_cipher_do_cnt(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl); /* Cleanup function */ static int gost_cipher_cleanup(EVP_CIPHER_CTX *); +static int gost_magma_mgm_cleanup(EVP_CIPHER_CTX *c); /* set/get cipher parameters */ static int gost89_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params); static int gost89_get_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params); @@ -53,6 +54,8 @@ static int magma_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc); static int magma_cipher_init_ctr_acpkm_omac(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc); +static int gost_magma_cipher_init_mgm(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc); /* Handles block of data in CBC mode */ static int magma_cipher_do_ecb(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl); @@ -63,13 +66,15 @@ static int magma_cipher_do_ctr(EVP_CIPHER_CTX *ctx, unsigned char *out, static int magma_cipher_do_ctr_acpkm_omac(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl); - +static int gost_magma_cipher_do_mgm(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len); /* set/get cipher parameters */ static int magma_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params); static int magma_get_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params); /* Control function */ static int magma_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); static int magma_cipher_ctl_acpkm_omac(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); +static int gost_magma_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr); /* * Single level template accessor. @@ -92,15 +97,15 @@ EVP_CIPHER *GOST_init_cipher(GOST_cipher *c) int flags = c->flags | TPL_VAL(c, flags); int block_size = TPL(c, block_size); switch (flags & EVP_CIPH_MODE) { - case EVP_CIPH_CTR_MODE: - case EVP_CIPH_CFB_MODE: - case EVP_CIPH_OFB_MODE: - OPENSSL_assert(block_size == 1); - OPENSSL_assert(flags & EVP_CIPH_NO_PADDING); - break; - default: + case EVP_CIPH_CBC_MODE: + case EVP_CIPH_ECB_MODE: + case EVP_CIPH_WRAP_MODE: OPENSSL_assert(block_size != 1); OPENSSL_assert(!(flags & EVP_CIPH_NO_PADDING)); + break; + default: + OPENSSL_assert(block_size == 1); + OPENSSL_assert(flags & EVP_CIPH_NO_PADDING); } if (TPL(c, iv_len)) @@ -246,6 +251,32 @@ GOST_cipher magma_ecb_cipher = { .do_cipher = magma_cipher_do_ecb, }; + GOST_cipher magma_mgm_cipher = { + .nid = NID_undef, + .template = &magma_template_cipher, + .block_size = 1, + .iv_len = 8, + .flags = EVP_CIPH_NO_PADDING | + EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER | + EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER, + .init = gost_magma_cipher_init_mgm, + .do_cipher = gost_magma_cipher_do_mgm, + .ctrl = gost_magma_mgm_ctrl, + .cleanup = gost_magma_mgm_cleanup, + .ctx_size = sizeof(gost_mgm_ctx) + }; + +static void magma_NID_callback (int nid) +{ + magma_mgm_cipher.nid = nid; +} + +GOST_NID_JOB magma_mgm_NID = { + .sn = SN_magma_mgm, + .ln = SN_magma_mgm, + .callback = magma_NID_callback, +}; + GOST_cipher magma_cbc_cipher = { .nid = NID_magma_cbc, .template = &gost_template_cipher, @@ -518,6 +549,110 @@ static int magma_cipher_init_ctr_acpkm_omac(EVP_CIPHER_CTX *ctx, const unsigned return magma_cipher_init(ctx, key, iv, enc); } +void gost_magma_encrypt_wrap(unsigned char *in, unsigned char *out, + struct ossl_gost_cipher_ctx *c) { + int i; + unsigned char b[8]; + unsigned char d[8]; + for (i = 0; i < 8; i++) { + b[7 - i] = in[i]; + } + gostcrypt(&(c->cctx), b, d); + for (i = 0; i < 8; i++) { + out[7 - i] = d[i]; + } +} + +/* ----------------------------------------------------------------------------------------------- */ +/*! Функция реализует операцию умножения двух элементов конечного поля \f$ \mathbb F_{2^{64}}\f$, + порожденного неприводимым многочленом + \f$ f(x) = x^{64} + x^4 + x^3 + x + 1 \in \mathbb F_2[x]\f$. Для умножения используется + простейшая реализация, основанная на приведении по модулю после каждого шага алгоритма. */ +/* ----------------------------------------------------------------------------------------------- */ +static void gf64_mul (uint64_t *result, uint64_t *arg1, uint64_t *arg2) +{ + int i = 0; + register uint64_t t, X0; + uint64_t Z0 = 0; + +#ifdef L_ENDIAN + X0 = BSWAP64(*arg1); +#else + X0 = *arg1; +#endif + +#ifdef L_ENDIAN + t = BSWAP64(*(arg2)); +#else + t = *(arg2); +#endif + + for (i = 0; i < 63; i++) { + if (t & 0x1) { + Z0 ^= X0; + } + t >>= 1; + if (X0 & 0x8000000000000000) { + X0 <<= 1; + X0 ^= 0x1b; + } + else { + X0 <<= 1; + } + } + + if (t & 0x1) { + Z0 ^= X0; + } + +#ifdef L_ENDIAN + *(result) = BSWAP64(Z0); +#else + *(result) = Z0; +#endif +} + +static int gost_magma_cipher_init_mgm(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx); + int bl; + + if (!iv && !key) + return 1; + if (key) { + bl = EVP_CIPHER_CTX_iv_length(ctx); + if (!gost_cipher_set_param(&mctx->ks.g_ks, NID_id_tc26_gost_28147_param_Z)) + return 0; + magma_key(&(mctx->ks.g_ks.cctx), key); + gost_mgm128_init(&mctx->mgm, &mctx->ks, + (block128_f) gost_magma_encrypt_wrap, gf64_mul, bl); + + /* + * If we have an iv can set it directly, otherwise use saved IV. + */ + if (iv == NULL && mctx->iv_set) + iv = mctx->iv; + if (iv) { + if (gost_mgm128_setiv(&mctx->mgm, iv, mctx->ivlen) != 1) + return 0; + mctx->iv_set = 1; + } + mctx->key_set = 1; + } else { + /* If key set use IV, otherwise copy */ + if (mctx->key_set) { + if (gost_mgm128_setiv(&mctx->mgm, iv, mctx->ivlen) != 1) + return 0; + } + else + memcpy(mctx->iv, iv, mctx->ivlen); + mctx->iv_set = 1; + } + return 1; +} + /* * Wrapper around gostcrypt function from gost89.c which perform key meshing * when nesseccary @@ -761,6 +896,58 @@ static int magma_cipher_do_ctr_acpkm_omac(EVP_CIPHER_CTX *ctx, unsigned char *ou return inl; } + +static int gost_magma_cipher_do_mgm(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx); + int enc = EVP_CIPHER_CTX_encrypting(ctx); + + /* If not set up, return error */ + if (!mctx->key_set) { + GOSTerr(GOST_F_GOST_MAGMA_CIPHER_DO_MGM, + GOST_R_BAD_ORDER); + return -1; + } + + if (!mctx->iv_set) { + GOSTerr(GOST_F_GOST_MAGMA_CIPHER_DO_MGM, + GOST_R_BAD_ORDER); + return -1; + } + if (in) { + if (out == NULL) { + if (gost_mgm128_aad(&mctx->mgm, in, len)) + return -1; + } else if (enc) { + if (gost_mgm128_encrypt(&mctx->mgm, in, out, len)) + return -1; + } else { + if (gost_mgm128_decrypt(&mctx->mgm, in, out, len)) + return -1; + } + return len; + } else { + if (!enc) { + if (mctx->taglen < 0) + return -1; + if (gost_mgm128_finish(&mctx->mgm, + EVP_CIPHER_CTX_buf_noconst(ctx), + mctx->taglen) != 0) + return -1; + mctx->iv_set = 0; + return 0; + } + gost_mgm128_tag(&mctx->mgm, EVP_CIPHER_CTX_buf_noconst(ctx), 8); + mctx->taglen = 8; + /* Don't reuse the IV */ + mctx->iv_set = 0; + return 0; + } + +} + /* GOST encryption in CFB mode */ static int gost_cipher_do_cfb(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) @@ -890,6 +1077,78 @@ static int gost_cipher_cleanup(EVP_CIPHER_CTX *ctx) return 1; } +static int gost_magma_mgm_cleanup(EVP_CIPHER_CTX *c) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(c); + if (mctx == NULL) + return 0; + gost_destroy(&mctx->ks.g_ks.cctx); + OPENSSL_cleanse(&mctx->mgm, sizeof(mctx->mgm)); + EVP_CIPHER_CTX_set_app_data(c, NULL); + return 1; +} + +static int gost_magma_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(c); + unsigned char *buf, *iv; + int ivlen, enc; + + switch (type) { + case EVP_CTRL_INIT: + ivlen = EVP_CIPHER_iv_length(EVP_CIPHER_CTX_cipher(c)); + iv = EVP_CIPHER_CTX_iv_noconst(c); + mctx->key_set = 0; + mctx->iv_set = 0; + mctx->ivlen = ivlen; + mctx->iv = iv; + mctx->taglen = -1; + return 1; + + case EVP_CTRL_GET_IVLEN: + *(int *)ptr = mctx->ivlen; + return 1; + + case EVP_CTRL_AEAD_SET_IVLEN: + if (arg <= 0) + return 0; + if ((arg > EVP_MAX_IV_LENGTH) && (arg > mctx->ivlen)) { + // TODO: Allocate memory for IV or set error + return 0; + } + mctx->ivlen = arg; + return 1; + + case EVP_CTRL_AEAD_SET_TAG: + buf = EVP_CIPHER_CTX_buf_noconst(c); + enc = EVP_CIPHER_CTX_encrypting(c); + if (arg <= 0 || arg != 8 || enc) { + GOSTerr(GOST_F_GOST_MAGMA_MGM_CTRL, + GOST_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(buf, ptr, arg); + mctx->taglen = arg; + return 1; + + case EVP_CTRL_AEAD_GET_TAG: + buf = EVP_CIPHER_CTX_buf_noconst(c); + enc = EVP_CIPHER_CTX_encrypting(c); + if (arg <= 0 || arg > 8 || !enc || mctx->taglen < 0) { + GOSTerr(GOST_F_GOST_MAGMA_MGM_CTRL, + GOST_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(ptr, buf, arg); + return 1; + + default: + return -1; + } +} + /* Control function for gost cipher */ static int gost_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) { @@ -1001,7 +1260,7 @@ static int magma_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) case EVP_CTRL_RAND_KEY: if (RAND_priv_bytes ((unsigned char *)ptr, EVP_CIPHER_CTX_key_length(ctx)) <= 0) { - GOSTerr(GOST_F_GOST_CIPHER_CTL, GOST_R_RNG_ERROR); + GOSTerr(GOST_F_MAGMA_CIPHER_CTL, GOST_R_RNG_ERROR); return -1; } break; diff --git a/gost_ec_keyx.c b/gost_ec_keyx.c index 1dc0a29..8839c4a 100644 --- a/gost_ec_keyx.c +++ b/gost_ec_keyx.c @@ -769,7 +769,7 @@ int pkey_gost_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, } if (key != NULL && *key_len < 32) { - GOSTerr(GOST_F_PKEY_GOST2018_ENCRYPT, GOST_R_INVALID_BUFFER_SIZE); + GOSTerr(GOST_F_PKEY_GOST_DECRYPT, GOST_R_INVALID_BUFFER_SIZE); return 0; } @@ -93,10 +93,12 @@ GOST_cipher *gost_cipher_array[] = { &grasshopper_ofb_cipher, &grasshopper_ctr_cipher, &magma_ecb_cipher, + &grasshopper_mgm_cipher, &magma_cbc_cipher, &magma_ctr_cipher, &magma_ctr_acpkm_cipher, &magma_ctr_acpkm_omac_cipher, + &magma_mgm_cipher, &grasshopper_ctr_acpkm_cipher, &grasshopper_ctr_acpkm_omac_cipher, &magma_kexp15_cipher, @@ -329,6 +331,8 @@ static int gost_engine_destroy(ENGINE* e) { */ static GOST_NID_JOB *missing_NIDs[] = { + &kuznyechik_mgm_NID, + &magma_mgm_NID, }; static int create_NIDs() { diff --git a/gost_gost2015.c b/gost_gost2015.c index 1ffa428..7d207d0 100644 --- a/gost_gost2015.c +++ b/gost_gost2015.c @@ -6,6 +6,8 @@ */ #include "gost_lcl.h" #include "gost_gost2015.h" +#include "gost_grasshopper_defines.h" +#include "gost_grasshopper_math.h" #include "e_gost_err.h" #include <string.h> #include <openssl/rand.h> @@ -201,3 +203,288 @@ int init_zero_kdf_seed(unsigned char *kdf_seed) 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) + } + } + + 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 { + // TODO: check for big-endian + ctx->len.u[0] = (alen >> 32) | clen; + 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)); +} diff --git a/gost_gost2015.h b/gost_gost2015.h index 663d315..37b94d0 100644 --- a/gost_gost2015.h +++ b/gost_gost2015.h @@ -7,13 +7,58 @@ #ifndef GOST_GOST2015_H #define GOST_GOST2015_H +#include "gost_grasshopper_cipher.h" + #include <openssl/evp.h> #include <openssl/x509.h> +#include <openssl/modes.h> #define MAGMA_MAC_MAX_SIZE 8 #define KUZNYECHIK_MAC_MAX_SIZE 16 #define OID_GOST_CMS_MAC "1.2.643.7.1.0.6.1.1" +#define SN_magma_mgm "magma-mgm" + +#define BSWAP64(x) \ + (((x & 0xFF00000000000000ULL) >> 56) | \ + ((x & 0x00FF000000000000ULL) >> 40) | \ + ((x & 0x0000FF0000000000ULL) >> 24) | \ + ((x & 0x000000FF00000000ULL) >> 8) | \ + ((x & 0x00000000FF000000ULL) << 8) | \ + ((x & 0x0000000000FF0000ULL) << 24) | \ + ((x & 0x000000000000FF00ULL) << 40) | \ + ((x & 0x00000000000000FFULL) << 56)) + +typedef void (*mul128_f) (uint64_t *result, uint64_t *arg1, uint64_t *arg2); + +typedef struct { + union { + uint64_t u[2]; + uint32_t d[4]; + uint8_t c[16]; + } nonce, Yi, Zi, EKi, Hi, len, ACi, mul, sum, tag; + + unsigned int mres, ares; + block128_f block; + mul128_f mul_gf; + int blocklen; + void *key; +} mgm128_context; + +typedef struct { + union { + struct ossl_gost_cipher_ctx g_ks; + gost_grasshopper_cipher_ctx gh_ks; + } ks; + int key_set; + int iv_set; + mgm128_context mgm; + unsigned char *iv; + int ivlen; + int taglen; + int tlstree_mode; +} gost_mgm_ctx; + 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, @@ -36,4 +81,24 @@ int gost2015_acpkm_omac_init(int nid, int enc, const unsigned char *inkey, unsigned char *outkey, unsigned char *kdf_seed); int init_zero_kdf_seed(unsigned char *kdf_seed); + +/* enc/dec mgm mode */ + +void gost_mgm128_init(mgm128_context *ctx, void *key, block128_f block, + mul128_f mul_gf, int blen); + +int gost_mgm128_setiv(mgm128_context *ctx, const unsigned char *iv, size_t len); + +int gost_mgm128_aad(mgm128_context *ctx, const unsigned char *aad, size_t len); + +int gost_mgm128_encrypt(mgm128_context *ctx, const unsigned char *in, + unsigned char *out, size_t len); + +int gost_mgm128_decrypt(mgm128_context *ctx, const unsigned char *in, + unsigned char *out, size_t len); + +int gost_mgm128_finish(mgm128_context *ctx, const unsigned char *tag, size_t len); + +void gost_mgm128_tag(mgm128_context *ctx, unsigned char *tag, size_t len); + #endif diff --git a/gost_grasshopper_cipher.c b/gost_grasshopper_cipher.c index 001746b..ef06e2d 100644 --- a/gost_grasshopper_cipher.c +++ b/gost_grasshopper_cipher.c @@ -26,6 +26,7 @@ enum GRASSHOPPER_CIPHER_TYPE { GRASSHOPPER_CIPHER_CTR, GRASSHOPPER_CIPHER_CTRACPKM, GRASSHOPPER_CIPHER_CTRACPKMOMAC, + GRASSHOPPER_CIPHER_MGM, }; static GOST_cipher grasshopper_template_cipher = { @@ -124,6 +125,32 @@ GOST_cipher grasshopper_ctr_acpkm_omac_cipher = { .ctx_size = sizeof(gost_grasshopper_cipher_ctx_ctr), }; +GOST_cipher grasshopper_mgm_cipher = { + .nid = NID_undef, + .template = &grasshopper_template_cipher, + .block_size = 1, + .iv_len = 16, + .flags = EVP_CIPH_NO_PADDING | + EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER | + EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER, + .cleanup = gost_grasshopper_mgm_cleanup, + .ctrl = gost_grasshopper_mgm_ctrl, + .init = gost_grasshopper_cipher_init_mgm, + .do_cipher = gost_grasshopper_cipher_do_mgm, + .ctx_size = sizeof(gost_mgm_ctx) +}; + +static void kuznyechik_NID_callback (int nid) +{ + grasshopper_mgm_cipher.nid = nid; +} + +GOST_NID_JOB kuznyechik_mgm_NID = { + .sn = SN_kuznyechik_mgm, + .ln = SN_kuznyechik_mgm, + .callback = kuznyechik_NID_callback, +}; + /* first 256 bit of D from draft-irtf-cfrg-re-keying-12 */ static const unsigned char ACPKM_D_2018[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 64 bit */ @@ -344,6 +371,143 @@ gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX return gost_grasshopper_cipher_init(ctx, key, iv, enc); } +// TODO: const *in, const *c +void gost_grasshopper_encrypt_wrap(unsigned char *in, unsigned char *out, + gost_grasshopper_cipher_ctx *c) { + grasshopper_encrypt_block(&c->encrypt_round_keys, + (grasshopper_w128_t *) in, + (grasshopper_w128_t *) out, + &c->buffer); +} + + + +/* ----------------------------------------------------------------------------------------------- */ +/*! Функция реализует операцию умножения двух элементов конечного поля \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 *result, uint64_t *arg1, uint64_t *arg2) +{ + int i = 0; + register uint64_t t, X0, X1; + uint64_t Z0 = 0, Z1 = 0; + +#ifdef L_ENDIAN + X0 = BSWAP64(*(arg1 + 1)); + X1 = BSWAP64(*arg1); +#else + X0 = *(arg1 + 1); + X1 = *arg1; +#endif + + //first 64 bits of arg1 +#ifdef L_ENDIAN + t = BSWAP64(*(arg2 + 1)); +#else + t = *(arg2 + 1); +#endif + + for (i = 0; i < 64; i++) { + if (t & 0x1) { + Z0 ^= X0; + Z1 ^= X1; + } + t >>= 1; + if (X1 & 0x8000000000000000) { + X1 <<= 1; + X1 ^= X0>>63; + X0 <<= 1; + X0 ^= 0x87; + } + else { + X1 <<= 1; + X1 ^= X0>>63; + X0 <<= 1; + } + } + + //second 64 bits of arg2 +#ifdef L_ENDIAN + t = BSWAP64(*arg2); +#else + t = *arg2; +#endif + + for (i = 0; i < 63; i++) { + if (t & 0x1) { + Z0 ^= X0; + Z1 ^= X1; + } + t >>= 1; + if (X1 & 0x8000000000000000) { + X1 <<= 1; + X1 ^= X0>>63; + X0 <<= 1; + X0 ^= 0x87; + } + else { + X1 <<= 1; + X1 ^= X0>>63; + X0 <<= 1; + } + } + + if (t & 0x1) { + Z0 ^= X0; + Z1 ^= X1; + } + +#ifdef L_ENDIAN + result[0] = BSWAP64(Z1); + result[1] = BSWAP64(Z0); +#else + result[0] = Z1; + result[1] = Z0; +#endif +} + +static GRASSHOPPER_INLINE int +gost_grasshopper_cipher_init_mgm(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx); + int bl; + + if (!iv && !key) + return 1; + if (key) { + bl = EVP_CIPHER_CTX_iv_length(ctx); + gost_grasshopper_cipher_key(&mctx->ks.gh_ks, key); + gost_mgm128_init(&mctx->mgm, &mctx->ks, + (block128_f) gost_grasshopper_encrypt_wrap, gf128_mul_uint64, bl); + + /* + * If we have an iv can set it directly, otherwise use saved IV. + */ + if (iv == NULL && mctx->iv_set) + iv = mctx->iv; + if (iv) { + if (gost_mgm128_setiv(&mctx->mgm, iv, mctx->ivlen) != 1) + return 0; + mctx->iv_set = 1; + } + mctx->key_set = 1; + } else { + /* If key set use IV, otherwise copy */ + if (mctx->key_set) { + if (gost_mgm128_setiv(&mctx->mgm, iv, mctx->ivlen) != 1) + return 0; + } + else + memcpy(mctx->iv, iv, mctx->ivlen); + mctx->iv_set = 1; + } + return 1; +} + static int gost_grasshopper_cipher_do_ecb(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) { @@ -582,6 +746,60 @@ static int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX *ctx, return result; } + + + +static int gost_grasshopper_cipher_do_mgm(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx); + int enc = EVP_CIPHER_CTX_encrypting(ctx); + + /* If not set up, return error */ + if (!mctx->key_set) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM, + GOST_R_BAD_ORDER); + return -1; + } + + if (!mctx->iv_set) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_DO_MGM, + GOST_R_BAD_ORDER); + return -1; + } + if (in) { + if (out == NULL) { + if (gost_mgm128_aad(&mctx->mgm, in, len)) + return -1; + } else if (enc) { + if (gost_mgm128_encrypt(&mctx->mgm, in, out, len)) + return -1; + } else { + if (gost_mgm128_decrypt(&mctx->mgm, in, out, len)) + return -1; + } + return len; + } else { + if (!enc) { + if (mctx->taglen < 0) + return -1; + if (gost_mgm128_finish(&mctx->mgm, + EVP_CIPHER_CTX_buf_noconst(ctx), + mctx->taglen) != 0) + return -1; + mctx->iv_set = 0; + return 0; + } + gost_mgm128_tag(&mctx->mgm, EVP_CIPHER_CTX_buf_noconst(ctx), 16); + mctx->taglen = 16; + /* Don't reuse the IV */ + mctx->iv_set = 0; + return 0; + } + +} + /* * Fixed 128-bit IV implementation make shift regiser redundant. */ @@ -799,6 +1017,79 @@ gost_grasshopper_get_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params) return 0; } +static int gost_grasshopper_mgm_cleanup(EVP_CIPHER_CTX *c) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(c); + if (mctx == NULL) + return 0; + gost_grasshopper_cipher_destroy(&mctx->ks.gh_ks); + OPENSSL_cleanse(&mctx->mgm, sizeof(mctx->mgm)); + if (mctx->iv != EVP_CIPHER_CTX_iv_noconst(c)) + OPENSSL_free(mctx->iv); + return 1; +} + +static int gost_grasshopper_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) +{ + gost_mgm_ctx *mctx = + (gost_mgm_ctx *)EVP_CIPHER_CTX_get_cipher_data(c); + unsigned char *buf, *iv; + int ivlen, enc; + + switch (type) { + case EVP_CTRL_INIT: + ivlen = EVP_CIPHER_iv_length(EVP_CIPHER_CTX_cipher(c)); + iv = EVP_CIPHER_CTX_iv_noconst(c); + mctx->key_set = 0; + mctx->iv_set = 0; + mctx->ivlen = ivlen; + mctx->iv = iv; + mctx->taglen = -1; + return 1; + + case EVP_CTRL_GET_IVLEN: + *(int *)ptr = mctx->ivlen; + return 1; + + case EVP_CTRL_AEAD_SET_IVLEN: + if (arg <= 0) + return 0; + if ((arg > EVP_MAX_IV_LENGTH) && (arg > mctx->ivlen)) { + // TODO: Allocate memory for IV or set error + return 0; + } + mctx->ivlen = arg; + return 1; + + case EVP_CTRL_AEAD_SET_TAG: + buf = EVP_CIPHER_CTX_buf_noconst(c); + enc = EVP_CIPHER_CTX_encrypting(c); + if (arg <= 0 || arg != 16 || enc) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_MGM_CTRL, + GOST_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(buf, ptr, arg); + mctx->taglen = arg; + return 1; + + case EVP_CTRL_AEAD_GET_TAG: + buf = EVP_CIPHER_CTX_buf_noconst(c); + enc = EVP_CIPHER_CTX_encrypting(c); + if (arg <= 0 || arg > 16 || !enc || mctx->taglen < 0) { + GOSTerr(GOST_F_GOST_GRASSHOPPER_MGM_CTRL, + GOST_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(ptr, buf, arg); + return 1; + + default: + return -1; + } +} + static int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) { switch (type) { diff --git a/gost_grasshopper_cipher.h b/gost_grasshopper_cipher.h index ecaf1c7..a3a6470 100644 --- a/gost_grasshopper_cipher.h +++ b/gost_grasshopper_cipher.h @@ -6,12 +6,15 @@ #ifndef GOST_GRASSHOPPER_CIPHER_H #define GOST_GRASSHOPPER_CIPHER_H +#define SN_kuznyechik_mgm "kuznyechik-mgm" + #if defined(__cplusplus) extern "C" { #endif #include "gost_grasshopper_defines.h" +#include "gost_lcl.h" #include <openssl/evp.h> // not thread safe @@ -60,6 +63,9 @@ static int gost_grasshopper_cipher_init_ctracpkm(EVP_CIPHER_CTX* ctx, static int gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); +static int gost_grasshopper_cipher_init_mgm(EVP_CIPHER_CTX* ctx, + const unsigned char* key, const unsigned char* iv, int enc); + static int gost_grasshopper_cipher_init(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc); @@ -87,14 +93,21 @@ static int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX* ctx, unsigned cha static int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX* ctx, unsigned char* out, const unsigned char* in, size_t inl); +static int gost_grasshopper_cipher_do_mgm(EVP_CIPHER_CTX* ctx, unsigned char* out, + const unsigned char* in, size_t inl); + static int gost_grasshopper_cipher_cleanup(EVP_CIPHER_CTX* ctx); +static int gost_grasshopper_mgm_cleanup(EVP_CIPHER_CTX *c); + static int gost_grasshopper_set_asn1_parameters(EVP_CIPHER_CTX* ctx, ASN1_TYPE* params); static int gost_grasshopper_get_asn1_parameters(EVP_CIPHER_CTX* ctx, ASN1_TYPE* params); static int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX* ctx, int type, int arg, void* ptr); +static int gost_grasshopper_mgm_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); + const EVP_CIPHER* cipher_gost_grasshopper_ctracpkm(); #if defined(__cplusplus) @@ -352,11 +352,13 @@ extern GOST_cipher magma_ctr_acpkm_cipher; extern GOST_cipher magma_ctr_acpkm_omac_cipher; extern GOST_cipher magma_ecb_cipher; extern GOST_cipher magma_cbc_cipher; +extern GOST_cipher magma_mgm_cipher; extern GOST_cipher grasshopper_ecb_cipher; extern GOST_cipher grasshopper_cbc_cipher; extern GOST_cipher grasshopper_cfb_cipher; extern GOST_cipher grasshopper_ofb_cipher; extern GOST_cipher grasshopper_ctr_cipher; +extern GOST_cipher grasshopper_mgm_cipher; extern GOST_cipher grasshopper_ctr_acpkm_cipher; extern GOST_cipher grasshopper_ctr_acpkm_omac_cipher; extern GOST_cipher magma_kexp15_cipher; @@ -411,5 +413,8 @@ struct gost_nid_job { typedef struct gost_nid_job GOST_NID_JOB; +extern GOST_NID_JOB magma_mgm_NID; +extern GOST_NID_JOB kuznyechik_mgm_NID; + #endif /* vim: set expandtab cinoptions=\:0,l1,t0,g0,(0 sw=4 : */ diff --git a/tcl_tests/engine.try b/tcl_tests/engine.try index 9dcdad7..83d9ab9 100644 --- a/tcl_tests/engine.try +++ b/tcl_tests/engine.try @@ -6,7 +6,7 @@ start_tests "Тесты на команду engine" switch -exact [engine_name] { "ccore" {set list " \[RAND, gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, id-tc26-cipher-gostr3412-2015-magma-ctracpkm, magma-ctr, magma-ofb, magma-ecb, magma-cbc, magma-cfb, grasshopper-ecb, grasshopper-cbc, grasshopper-ofb, grasshopper-cfb, grasshopper-ctr, id-tc26-cipher-gostr3412-2015-kuznyechik-ctracpkm, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12\]\n"} - "open" {set list "(gost) Reference implementation of GOST engine\n \[gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, kuznyechik-ecb, kuznyechik-cbc, kuznyechik-cfb, kuznyechik-ofb, kuznyechik-ctr, magma-ecb, magma-cbc, magma-ctr, magma-ctr-acpkm, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm, kuznyechik-ctr-acpkm-omac, magma-kexp15, kuznyechik-kexp15, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, magma-mac, kuznyechik-mac, kuznyechik-ctr-acpkm-omac, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12, magma-mac, kuznyechik-mac, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm-omac\]\n"} + "open" {set list "(gost) Reference implementation of GOST engine\n \[gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, kuznyechik-ecb, kuznyechik-cbc, kuznyechik-cfb, kuznyechik-ofb, kuznyechik-ctr, magma-ecb, kuznyechik-mgm, magma-cbc, magma-ctr, magma-ctr-acpkm, magma-ctr-acpkm-omac, magma-mgm, kuznyechik-ctr-acpkm, kuznyechik-ctr-acpkm-omac, magma-kexp15, kuznyechik-kexp15, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, magma-mac, kuznyechik-mac, kuznyechik-ctr-acpkm-omac, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12, magma-mac, kuznyechik-mac, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm-omac]\n"} } diff --git a/test/00-engine.t b/test/00-engine.t index c61cbcb..0904a21 100644 --- a/test/00-engine.t +++ b/test/00-engine.t @@ -40,7 +40,7 @@ if ( -f $engine . ".info") { $engine_info= <<EOINF; (gost) Reference implementation of GOST engine - [gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, kuznyechik-ecb, kuznyechik-cbc, kuznyechik-cfb, kuznyechik-ofb, kuznyechik-ctr, magma-ecb, magma-cbc, magma-ctr, magma-ctr-acpkm, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm, kuznyechik-ctr-acpkm-omac, magma-kexp15, kuznyechik-kexp15, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, magma-mac, kuznyechik-mac, kuznyechik-ctr-acpkm-omac, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12, magma-mac, kuznyechik-mac, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm-omac] + [gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, kuznyechik-ecb, kuznyechik-cbc, kuznyechik-cfb, kuznyechik-ofb, kuznyechik-ctr, magma-ecb, kuznyechik-mgm, magma-cbc, magma-ctr, magma-ctr-acpkm, magma-ctr-acpkm-omac, magma-mgm, kuznyechik-ctr-acpkm, kuznyechik-ctr-acpkm-omac, magma-kexp15, kuznyechik-kexp15, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, magma-mac, kuznyechik-mac, kuznyechik-ctr-acpkm-omac, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12, magma-mac, kuznyechik-mac, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm-omac] EOINF } diff --git a/test_mgm.c b/test_mgm.c new file mode 100644 index 0000000..b61a8e3 --- /dev/null +++ b/test_mgm.c @@ -0,0 +1,267 @@ +/* + * Test "Recommendations for standardization R 1323565.1.026 - 2019" + * authentivated encryption block cipher operation modes + * + * Copyright (C) 2019-2020 Vitaly Chikunov <vt@altlinux.org>. All Rights Reserved. + * Copyright (c) 2020-2021 JSC "NPK "Kryptonite" <i.kirillov@kryptonite.ru>. + * Code based on test_ciphers.c from master branch. + * + * Contents licensed under the terms of the OpenSSL license + * See https://www.openssl.org/source/license.html for details + */ + +#include <openssl/engine.h> +#include <openssl/evp.h> +#include <string.h> + +#include "gost_grasshopper_cipher.h" +#include "gost_gost2015.h" +#if defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#elif defined __linux__ +# include <alloca.h> +#endif + +#define T(e) ({ \ + if (!(e)) {\ + ERR_print_errors_fp(stderr);\ + OpenSSLDie(__FILE__, __LINE__, #e);\ + } \ +}) + +#define TEST_ASSERT(e) {if ((test = (e))) \ + printf("Test FAILED\n"); \ + else \ + printf("Test passed\n");} + + +/* MGM-Encrypt/MGM-decrypt test data from "R 1323565.1.026-2019" */ + +const unsigned char gh_key[32] = { + 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[16] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88 +}; + +const unsigned char gh_adata[41] = { + 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[67] = { + 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 gh_e_cdata[67] = { + 0xA9, 0x75, 0x7B, 0x81, 0x47, 0x95, 0x6E, 0x90, 0x55, 0xB8, 0xA3, 0x3D, 0xE8, 0x9F, 0x42, 0xFC, + 0x80, 0x75, 0xD2, 0x21, 0x2B, 0xF9, 0xFD, 0x5B, 0xD3, 0xF7, 0x06, 0x9A, 0xAD, 0xC1, 0x6B, 0x39, + 0x49, 0x7A, 0xB1, 0x59, 0x15, 0xA6, 0xBA, 0x85, 0x93, 0x6B, 0x5D, 0x0E, 0xA9, 0xF6, 0x85, 0x1C, + 0xC6, 0x0C, 0x14, 0xD4, 0xD3, 0xF8, 0x83, 0xD0, 0xAB, 0x94, 0x42, 0x06, 0x95, 0xC7, 0x6D, 0xEB, + 0x2C, 0x75, 0x52 +}; + +const unsigned char gh_e_tag[16] = { + 0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E, 0x29, 0xFC, 0xDB, 0x4C +}; + +const unsigned char mg_key[32] = { + 0xFF, 0xee, 0xDD, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xff +}; + +const unsigned char mg_nonce[8] = { + 0x12, 0xDE, 0xF0, 0x6B, 0x3C, 0x13, 0x0A, 0x59 +}; + +const unsigned char mg_adata[41] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xea +}; + +const unsigned char mg_pdata[67] = { + 0xFF, 0xee, 0xDD, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xee, 0xFF, 0x0A, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x99, 0xaa, 0xbb, 0xcc, 0xee, 0xFF, 0x0a, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0xaa, 0xbb, 0xcc, 0xee, 0xFF, 0x0a, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, 0xcc +}; + +const unsigned char mg_e_cdata[67] = { + 0xc7, 0x95, 0x06, 0x6c, 0x5f, 0x9e, 0xa0, 0x3b, 0x85, 0x11, 0x33, 0x42, 0x45, 0x91, 0x85, 0xae, + 0x1f, 0x2e, 0x00, 0xd6, 0xbf, 0x2b, 0x78, 0x5d, 0x94, 0x04, 0x70, 0xb8, 0xbb, 0x9c, 0x8e, 0x7d, + 0x9a, 0x5d, 0xd3, 0x73, 0x1f, 0x7d, 0xdc, 0x70, 0xec, 0x27, 0xcb, 0x0a, 0xce, 0x6f, 0xa5, 0x76, + 0x70, 0xf6, 0x5c, 0x64, 0x6a, 0xbb, 0x75, 0xd5, 0x47, 0xaa, 0x37, 0xc3, 0xbc, 0xb5, 0xc3, 0x4e, + 0x03, 0xbb, 0x9c +}; + +const unsigned char mg_e_tag[8] = { + 0xa7, 0x92, 0x80, 0x69, 0xaa, 0x10, 0xfd, 0x10 +}; + + +static struct testcase { + const char *sn; + const unsigned char *key; + const unsigned char *nonce; + size_t nonce_len; + const unsigned char *aad; + size_t aad_len; + const unsigned char *plaintext; + size_t ptext_len; + const unsigned char *expected; + const unsigned char *expected_tag; +} testcases[] = { + { + .sn = SN_kuznyechik_mgm, + .key = gh_key, + .nonce = gh_nonce, + .nonce_len = sizeof(gh_nonce), + .aad = gh_adata, + .aad_len = sizeof(gh_adata), + .plaintext = gh_pdata, + .ptext_len = sizeof(gh_pdata), + .expected = gh_e_cdata, + .expected_tag = gh_e_tag + }, + { + .sn = SN_magma_mgm, + .key = mg_key, + .nonce = mg_nonce, + .nonce_len = sizeof(mg_nonce), + .aad = mg_adata, + .aad_len = sizeof(mg_adata), + .plaintext = mg_pdata, + .ptext_len = sizeof(mg_pdata), + .expected = mg_e_cdata, + .expected_tag = mg_e_tag + }, + { 0 } +}; + +static int test_block(const EVP_CIPHER *ciph, const char *name, const unsigned char *nonce, size_t nlen, + const unsigned char *aad, size_t alen, const unsigned char *ptext, size_t plen, + const unsigned char *exp_ctext, const unsigned char *exp_tag, + const unsigned char * key, int small) +{ + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + unsigned char *c = alloca(plen); + int tag_len = nlen; + unsigned char *t = alloca(tag_len); + int outlen1, outlen2, tmplen; + int ret = 0, rv, test, i; + + OPENSSL_assert(ctx); + printf("Encryption test %s [%s]: ", name, small ? "small chunks" : "big chunks"); + + // test encrypt + EVP_CIPHER_CTX_init(ctx); + EVP_EncryptInit_ex(ctx, ciph, NULL, NULL, NULL); // Set cipher type and mode + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nlen, NULL); // Set IV length + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); // Initialise key and IV + memset(c, 0, plen); + if (!small) { + // test big chunks + EVP_EncryptUpdate(ctx, NULL, &outlen1, aad, alen); // Zero or more calls to specify any AAD + EVP_EncryptUpdate(ctx, c, &outlen2, ptext, plen); // Encrypt plaintext + } else { + // test small chunks + outlen1 = outlen2 = 0; + unsigned char *p; + for (i = 0; i < alen; i++) { + EVP_EncryptUpdate(ctx, NULL, &tmplen, aad + i, 1); + outlen1 += tmplen; + } + for (i = 0, p = c; i < plen; i++) { + EVP_EncryptUpdate(ctx, p, &tmplen, ptext + i, 1); + p += tmplen; + outlen2 += tmplen; + } + } + EVP_EncryptFinal_ex(ctx, c, &tmplen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, t); + EVP_CIPHER_CTX_cleanup(ctx); + + TEST_ASSERT(outlen1 != alen || outlen2 != plen || + memcmp(c, exp_ctext, plen) || + memcmp(t, exp_tag, tag_len)); + ret |= test; + + + // test decrtypt + printf("Decryption test %s [%s]: ", name, small ? "small chunks" : "big chunks"); + EVP_CIPHER_CTX_init(ctx); + EVP_DecryptInit_ex(ctx, ciph, NULL, NULL, NULL); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nlen, NULL); + EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); + memset(c, 0, plen); + if (!small) { + // test big chunks + EVP_DecryptUpdate(ctx, NULL, &outlen1, aad, alen); + EVP_DecryptUpdate(ctx, c, &outlen2, exp_ctext, plen); + } else { + // test small chunks + outlen1 = outlen2 = 0; + unsigned char *p; + for (i = 0; i < alen; i++) { + EVP_DecryptUpdate(ctx, NULL, &tmplen, aad + i, 1); + outlen1 += tmplen; + } + for (i = 0, p = c; i < plen; i++) { + EVP_DecryptUpdate(ctx, p, &tmplen, exp_ctext + i, 1); + p += tmplen; + outlen2 += tmplen; + } + } + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, (void *)exp_tag); + rv = EVP_DecryptFinal_ex(ctx, c, &tmplen); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + + TEST_ASSERT(outlen1 != alen || outlen2 != plen || + memcmp(c, ptext, plen) || rv != 1); + ret |= test; + + return ret; +} + +int main(void) +{ + int ret = 0; + const struct testcase *t; + + OPENSSL_add_all_algorithms_conf(); + + for (t = testcases; t->sn; t++) { + int small; + const EVP_CIPHER *ciph = EVP_get_cipherbyname(t->sn); + const char *name; + if (!ciph) { + printf("failed to load %s\n", t->sn); + return 1; + } + name = EVP_CIPHER_name(ciph); + + printf("Tests for %s\n", name); + for (small = 0; small <= 1; small++) + ret |= test_block(ciph, name, t->nonce, t->nonce_len, + t->aad, t->aad_len, t->plaintext, t->ptext_len, + t->expected, t->expected_tag, t->key, small); + } + + if (ret) { + printf("Some tests FAILED!\n"); + } else { + printf("All tests passed!\n"); + } + return ret; +} |