aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--e_gost_err.c12
-rw-r--r--e_gost_err.h9
-rw-r--r--gost.txt16
-rw-r--r--gost_crypt.c277
-rw-r--r--gost_ec_keyx.c2
-rw-r--r--gost_eng.c4
-rw-r--r--gost_gost2015.c287
-rw-r--r--gost_gost2015.h65
-rw-r--r--gost_grasshopper_cipher.c291
-rw-r--r--gost_grasshopper_cipher.h13
-rw-r--r--gost_lcl.h5
-rw-r--r--tcl_tests/engine.try2
-rw-r--r--test/00-engine.t2
-rw-r--r--test_mgm.c267
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
diff --git a/gost.txt b/gost.txt
index 1ee757d..05800e6 100644
--- a/gost.txt
+++ b/gost.txt
@@ -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;
}
diff --git a/gost_eng.c b/gost_eng.c
index f38a6fc..7ca5523 100644
--- a/gost_eng.c
+++ b/gost_eng.c
@@ -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)
diff --git a/gost_lcl.h b/gost_lcl.h
index d8b8943..ffa8c76 100644
--- a/gost_lcl.h
+++ b/gost_lcl.h
@@ -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;
+}