From bdeea3d404c165fc266a1be7278db3865652b509 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 25 Mar 2020 21:56:51 +0300 Subject: [PATCH 45/87] gost: add kuznyechik-mgm support Add EVP_AEAD interface for supporting kuznyechik cipher in MGM mode. Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/Symbols.list | 1 + src/lib/libcrypto/evp/e_kuznyechik.c | 161 +++++++++++++++++++ src/lib/libcrypto/evp/evp.h | 5 + src/regress/lib/libcrypto/aead/aeadtest.c | 6 + src/regress/lib/libcrypto/aead/aeadtests.txt | 9 ++ 5 files changed, 182 insertions(+) diff --git a/src/lib/libcrypto/Symbols.list b/src/lib/libcrypto/Symbols.list index 287c86d2c..aff725bd4 100644 --- a/src/lib/libcrypto/Symbols.list +++ b/src/lib/libcrypto/Symbols.list @@ -1618,6 +1618,7 @@ EVP_add_digest EVP_aead_aes_128_gcm EVP_aead_aes_256_gcm EVP_aead_chacha20_poly1305 +EVP_aead_kuznyechik_mgm EVP_aead_xchacha20_poly1305 EVP_aes_128_cbc EVP_aes_128_cbc_hmac_sha1 diff --git a/src/lib/libcrypto/evp/e_kuznyechik.c b/src/lib/libcrypto/evp/e_kuznyechik.c index 5fd53aff6..88ecef8e5 100644 --- a/src/lib/libcrypto/evp/e_kuznyechik.c +++ b/src/lib/libcrypto/evp/e_kuznyechik.c @@ -26,6 +26,7 @@ #include #include #include "evp_locl.h" +#include "modes_lcl.h" typedef struct { KUZNYECHIK_KEY ks; @@ -199,4 +200,164 @@ BLOCK_CIPHER_def1(kuznyechik, ctr_acpkm, ctr_acpkm, CTR, EVP_KUZNYECHIK_CTX, kuznyechik_ctr_acpkm_get_asn1_params, kuznyechik_acpkm_ctl) +#define EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN 16 + +struct aead_kuznyechik_mgm_ctx { + KUZNYECHIK_KEY ks; + MGM128_CONTEXT mgm; + unsigned char tag_len; +}; + +static void +kuznyechik_mgm_set_key(KUZNYECHIK_KEY *kuznyechik_key, MGM128_CONTEXT *mgm_ctx, + const unsigned char *key, size_t key_len) +{ + Kuznyechik_set_key(kuznyechik_key, key, 1); + CRYPTO_mgm128_init(mgm_ctx, kuznyechik_key, (block128_f)Kuznyechik_encrypt); +} + +static int +aead_kuznyechik_mgm_init(EVP_AEAD_CTX *ctx, const unsigned char *key, size_t key_len, + size_t tag_len) +{ + struct aead_kuznyechik_mgm_ctx *mgm_ctx; + const size_t key_bits = key_len * 8; + + /* EVP_AEAD_CTX_init should catch this. */ + if (key_bits != 256) { + EVPerror(EVP_R_BAD_KEY_LENGTH); + return 0; + } + + if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) + tag_len = EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN; + + if (tag_len > EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN) { + EVPerror(EVP_R_TAG_TOO_LARGE); + return 0; + } + + if ((mgm_ctx = calloc(1, sizeof(struct aead_kuznyechik_mgm_ctx))) == NULL) + return 0; + + kuznyechik_mgm_set_key(&mgm_ctx->ks, &mgm_ctx->mgm, key, key_len); + + mgm_ctx->tag_len = tag_len; + ctx->aead_state = mgm_ctx; + + return 1; +} + +static void +aead_kuznyechik_mgm_cleanup(EVP_AEAD_CTX *ctx) +{ + struct aead_kuznyechik_mgm_ctx *mgm_ctx = ctx->aead_state; + + freezero(mgm_ctx, sizeof(*mgm_ctx)); +} + +static int +aead_kuznyechik_mgm_seal(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, + size_t max_out_len, const unsigned char *nonce, size_t nonce_len, + const unsigned char *in, size_t in_len, const unsigned char *ad, + size_t ad_len) +{ + const struct aead_kuznyechik_mgm_ctx *mgm_ctx = ctx->aead_state; + MGM128_CONTEXT mgm; + size_t bulk = 0; + + if (max_out_len < in_len + mgm_ctx->tag_len) { + EVPerror(EVP_R_BUFFER_TOO_SMALL); + return 0; + } + + if (nonce_len != MGM128_NONCE_LEN) { + EVPerror(EVP_R_IV_TOO_LARGE); + return 0; + } + + memcpy(&mgm, &mgm_ctx->mgm, sizeof(mgm)); + CRYPTO_mgm128_setiv(&mgm, nonce); + + if (ad_len > 0 && CRYPTO_mgm128_aad(&mgm, ad, ad_len)) + return 0; + + if (CRYPTO_mgm128_encrypt(&mgm, in + bulk, out + bulk, + in_len - bulk)) + return 0; + + CRYPTO_mgm128_tag(&mgm, out + in_len, mgm_ctx->tag_len); + *out_len = in_len + mgm_ctx->tag_len; + + return 1; +} + +static int +aead_kuznyechik_mgm_open(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, + size_t max_out_len, const unsigned char *nonce, size_t nonce_len, + const unsigned char *in, size_t in_len, const unsigned char *ad, + size_t ad_len) +{ + const struct aead_kuznyechik_mgm_ctx *mgm_ctx = ctx->aead_state; + unsigned char tag[EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN]; + MGM128_CONTEXT mgm; + size_t plaintext_len; + size_t bulk = 0; + + if (in_len < mgm_ctx->tag_len) { + EVPerror(EVP_R_BAD_DECRYPT); + return 0; + } + + plaintext_len = in_len - mgm_ctx->tag_len; + + if (max_out_len < plaintext_len) { + EVPerror(EVP_R_BUFFER_TOO_SMALL); + return 0; + } + + if (nonce_len != MGM128_NONCE_LEN) { + EVPerror(EVP_R_IV_TOO_LARGE); + return 0; + } + + memcpy(&mgm, &mgm_ctx->mgm, sizeof(mgm)); + CRYPTO_mgm128_setiv(&mgm, nonce); + + if (CRYPTO_mgm128_aad(&mgm, ad, ad_len)) + return 0; + + if (CRYPTO_mgm128_decrypt(&mgm, in + bulk, out + bulk, + in_len - bulk - mgm_ctx->tag_len)) + return 0; + + CRYPTO_mgm128_tag(&mgm, tag, mgm_ctx->tag_len); + if (timingsafe_memcmp(tag, in + plaintext_len, mgm_ctx->tag_len) != 0) { + EVPerror(EVP_R_BAD_DECRYPT); + return 0; + } + + *out_len = plaintext_len; + + return 1; +} + +static const EVP_AEAD aead_kuznyechik_mgm = { + .key_len = 32, + .nonce_len = 16, + .overhead = EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN, + .max_tag_len = EVP_AEAD_KUZNYECHIK_MGM_TAG_LEN, + + .init = aead_kuznyechik_mgm_init, + .cleanup = aead_kuznyechik_mgm_cleanup, + .seal = aead_kuznyechik_mgm_seal, + .open = aead_kuznyechik_mgm_open, +}; + +const EVP_AEAD * +EVP_aead_kuznyechik_mgm(void) +{ + return &aead_kuznyechik_mgm; +} + #endif diff --git a/src/lib/libcrypto/evp/evp.h b/src/lib/libcrypto/evp/evp.h index 1ef416041..3725dd17c 100644 --- a/src/lib/libcrypto/evp/evp.h +++ b/src/lib/libcrypto/evp/evp.h @@ -1289,6 +1289,11 @@ const EVP_AEAD *EVP_aead_chacha20_poly1305(void); const EVP_AEAD *EVP_aead_xchacha20_poly1305(void); #endif +#if !defined(OPENSSL_NO_GOST) +/* EVP_aead_kuznyechik_mgm is Kuznyechik in Multilinear Galois Mode. */ +const EVP_AEAD *EVP_aead_kuznyechik_mgm(void); +#endif + /* EVP_AEAD_key_length returns the length of the keys used. */ size_t EVP_AEAD_key_length(const EVP_AEAD *aead); diff --git a/src/regress/lib/libcrypto/aead/aeadtest.c b/src/regress/lib/libcrypto/aead/aeadtest.c index 1b144c261..6f843e31c 100644 --- a/src/regress/lib/libcrypto/aead/aeadtest.c +++ b/src/regress/lib/libcrypto/aead/aeadtest.c @@ -143,6 +143,12 @@ aead_from_name(const EVP_AEAD **aead, const char *name) *aead = EVP_aead_xchacha20_poly1305(); #else fprintf(stderr, "No xchacha20-poly1305 support.\n"); +#endif + } else if (strcmp(name, "kuznyechik-mgm") == 0) { +#ifndef OPENSSL_NO_GOST + *aead = EVP_aead_kuznyechik_mgm(); +#else + fprintf(stderr, "No kuznyechik-MGM support.\n"); #endif } else { fprintf(stderr, "Unknown AEAD: %s\n", name); diff --git a/src/regress/lib/libcrypto/aead/aeadtests.txt b/src/regress/lib/libcrypto/aead/aeadtests.txt index 4ca47303b..d2d4e54f7 100644 --- a/src/regress/lib/libcrypto/aead/aeadtests.txt +++ b/src/regress/lib/libcrypto/aead/aeadtests.txt @@ -84,3 +84,12 @@ AD: 50515253c0c1c2c3c4c5c6c7 CT: bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb731c7f1b0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b4522f8c9ba40db5d945b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff921f9664c97637da9768812f615c68b13b52e TAG: c0875924c1c7987947deafd8780acf49 +# draft-smyshlyaev-mgm-17 +AEAD: kuznyechik-mgm +KEY: 8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef +NONCE: 1122334455667700ffeeddccbbaa9988 +IN: 1122334455667700ffeeddccbbaa998800112233445566778899aabbcceeff0a112233445566778899aabbcceeff0a002233445566778899aabbcceeff0a0011aabbcc +AD: 0202020202020202010101010101010104040404040404040303030303030303ea0505050505050505 +CT: a9757b8147956e9055b8a33de89f42fc8075d2212bf9fd5bd3f7069aadc16b39497ab15915a6ba85936b5d0ea9f6851cc60c14d4d3f883d0ab94420695c76deb2c7552 +TAG: cf5d656f40c34f5c46e8bb0e29fcdb4c + -- 2.17.1