From b7dd5628589230bf30bcd88d4d21cce70adb464b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 1 Apr 2020 17:04:15 +0300 Subject: [PATCH 42/87] gost: add support for new GOST key transport data format Add support for new GOST key transport data format used by CTR-OMAC ciphersuites. Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/gost/gost.h | 5 + src/lib/libcrypto/gost/gost_asn1.c | 59 ++++ src/lib/libcrypto/gost/gost_asn1.h | 12 + src/lib/libcrypto/gost/gost_kdf.c | 174 +++++++++++ src/lib/libcrypto/gost/gost_locl.h | 17 ++ src/lib/libcrypto/gost/gostr341001.c | 10 +- src/lib/libcrypto/gost/gostr341001_pmeth.c | 318 +++++++++++++++++++-- 7 files changed, 565 insertions(+), 30 deletions(-) create mode 100644 src/lib/libcrypto/gost/gost_kdf.c diff --git a/src/lib/libcrypto/gost/gost.h b/src/lib/libcrypto/gost/gost.h index e84a7625b..6a2b60670 100644 --- a/src/lib/libcrypto/gost/gost.h +++ b/src/lib/libcrypto/gost/gost.h @@ -228,10 +228,15 @@ size_t GOST_KEY_get_size(const GOST_KEY * r); #define EVP_PKEY_CTRL_GOST_SIG_FORMAT (EVP_PKEY_ALG_CTRL+2) #define EVP_PKEY_CTRL_GOST_SET_DIGEST (EVP_PKEY_ALG_CTRL+3) #define EVP_PKEY_CTRL_GOST_GET_DIGEST (EVP_PKEY_ALG_CTRL+4) +#define EVP_PKEY_CTRL_GOST_ENC_FORMAT (EVP_PKEY_ALG_CTRL+5) #define GOST_SIG_FORMAT_SR_BE 0 #define GOST_SIG_FORMAT_RS_LE 1 +#define GOST_ENC_FORMAT_4490 0 /* RFC 4490, TLS CNT-IMIT */ +#define GOST_ENC_FORMAT_PSKEY_MAGMA 1 /* CMS, TLS CTR-OMAC, Magma-encoded */ +#define GOST_ENC_FORMAT_PSKEY_KUZNYECHIK 2 /* CMS, TLS CTR-OMAC, Kuznyechik-encoded */ + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. diff --git a/src/lib/libcrypto/gost/gost_asn1.c b/src/lib/libcrypto/gost/gost_asn1.c index 14e46afab..f56486d2a 100644 --- a/src/lib/libcrypto/gost/gost_asn1.c +++ b/src/lib/libcrypto/gost/gost_asn1.c @@ -474,4 +474,63 @@ gost3412_ctr_acpkm_get_asn1_params(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params, unsig return 1; } +static const ASN1_TEMPLATE GOST_KEY_TRANSPORT_PSKEY_seq_tt[] = { + { + .flags = 0, + .tag = 0, + .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, key_exp), + .field_name = "key_exp", + .item = &ASN1_OCTET_STRING_it, + }, + { + .flags = 0, + .tag = 0, + .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, ephem_key), + .field_name = "ephem_key", + .item = &X509_PUBKEY_it, + }, + { + .flags = ASN1_TFLG_OPTIONAL, + .tag = 0, + .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, ukm), + .field_name = "ukm", + .item = &ASN1_OCTET_STRING_it, + }, +}; + +const ASN1_ITEM GOST_KEY_TRANSPORT_PSKEY_it = { + .itype = ASN1_ITYPE_NDEF_SEQUENCE, + .utype = V_ASN1_SEQUENCE, + .templates = GOST_KEY_TRANSPORT_PSKEY_seq_tt, + .tcount = sizeof(GOST_KEY_TRANSPORT_PSKEY_seq_tt) / sizeof(ASN1_TEMPLATE), + .funcs = NULL, + .size = sizeof(GOST_KEY_TRANSPORT_PSKEY), + .sname = "GOST_KEY_TRANSPORT_PSKEY", +}; + +GOST_KEY_TRANSPORT_PSKEY * +d2i_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY **a, const unsigned char **in, long len) +{ + return (GOST_KEY_TRANSPORT_PSKEY *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, + &GOST_KEY_TRANSPORT_PSKEY_it); +} + +int +i2d_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY *a, unsigned char **out) +{ + return ASN1_item_i2d((ASN1_VALUE *)a, out, &GOST_KEY_TRANSPORT_PSKEY_it); +} + +GOST_KEY_TRANSPORT_PSKEY * +GOST_KEY_TRANSPORT_PSKEY_new(void) +{ + return (GOST_KEY_TRANSPORT_PSKEY *)ASN1_item_new(&GOST_KEY_TRANSPORT_PSKEY_it); +} + +void +GOST_KEY_TRANSPORT_PSKEY_free(GOST_KEY_TRANSPORT_PSKEY *a) +{ + ASN1_item_free((ASN1_VALUE *)a, &GOST_KEY_TRANSPORT_PSKEY_it); +} + #endif diff --git a/src/lib/libcrypto/gost/gost_asn1.h b/src/lib/libcrypto/gost/gost_asn1.h index 5af16e00e..1ae5e6706 100644 --- a/src/lib/libcrypto/gost/gost_asn1.h +++ b/src/lib/libcrypto/gost/gost_asn1.h @@ -123,6 +123,18 @@ GOST3412_ENCRYPTION_PARAMS *d2i_GOST3412_ENCRYPTION_PARAMS(GOST3412_ENCRYPTION_P int i2d_GOST3412_ENCRYPTION_PARAMS(GOST3412_ENCRYPTION_PARAMS *a, unsigned char **out); extern const ASN1_ITEM GOST3412_ENCRYPTION_PARAMS_it; +typedef struct { + ASN1_OCTET_STRING *key_exp; + X509_PUBKEY *ephem_key; + ASN1_OCTET_STRING *ukm; +} GOST_KEY_TRANSPORT_PSKEY; + +GOST_KEY_TRANSPORT_PSKEY *GOST_KEY_TRANSPORT_PSKEY_new(void); +void GOST_KEY_TRANSPORT_PSKEY_free(GOST_KEY_TRANSPORT_PSKEY *a); +GOST_KEY_TRANSPORT_PSKEY *d2i_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY **a, const unsigned char **in, long len); +int i2d_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY *a, unsigned char **out); +extern const ASN1_ITEM GOST_KEY_TRANSPORT_PSKEY_it; + __END_HIDDEN_DECLS #endif diff --git a/src/lib/libcrypto/gost/gost_kdf.c b/src/lib/libcrypto/gost/gost_kdf.c new file mode 100644 index 000000000..be497aea0 --- /dev/null +++ b/src/lib/libcrypto/gost/gost_kdf.c @@ -0,0 +1,174 @@ +/* $OpenBSD: gost_kdf.c,v 1.4 2017/01/29 17:49:23 beck Exp $ */ +/* + * Copyright (c) 2020 Dmitry Baryshkov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#ifndef OPENSSL_NO_GOST +#include +#include +#include +#include +#include + +#include "gost_locl.h" + +int +gost_kexp15(const EVP_CIPHER *ctr_cipher, const EVP_CIPHER *cmac_cipher, + const unsigned char *key, unsigned int key_length, + const unsigned char *key_mac, + const unsigned char *key_enc, + const unsigned char *iv, + unsigned char *out, size_t *out_length) +{ + + CMAC_CTX *cmac_ctx = CMAC_CTX_new(); + EVP_CIPHER_CTX ctx; + unsigned char cmac[EVP_MAX_BLOCK_LENGTH]; + size_t cmac_length = sizeof(cmac); + unsigned int len = *out_length; + unsigned int tmp; + + if (CMAC_Init(cmac_ctx, key_mac, EVP_CIPHER_key_length(cmac_cipher), cmac_cipher, NULL) <= 0 || + CMAC_Update(cmac_ctx, iv, ctr_cipher->iv_len) <= 0 || + CMAC_Update(cmac_ctx, key, key_length) <= 0 || + CMAC_Final(cmac_ctx, cmac, &cmac_length) <= 0) { + CMAC_CTX_free(cmac_ctx); + return 0; + } + + CMAC_CTX_free(cmac_ctx); + + EVP_CIPHER_CTX_init(&ctx); + if (!EVP_EncryptInit_ex(&ctx, ctr_cipher, NULL, key_enc, iv) || + !EVP_CIPHER_CTX_set_padding(&ctx, 0) || + !EVP_EncryptUpdate(&ctx, out, &len, key, key_length)) { + EVP_CIPHER_CTX_cleanup(&ctx); + return 0; + } + + tmp = *out_length - len; + if (!EVP_EncryptUpdate(&ctx, out + len, &tmp, cmac, cmac_length)) { + EVP_CIPHER_CTX_cleanup(&ctx); + return 0; + } + + len += tmp; + tmp = *out_length - len; + if (!EVP_EncryptFinal_ex(&ctx, out + len, &tmp)) { + EVP_CIPHER_CTX_cleanup(&ctx); + return 0; + } + EVP_CIPHER_CTX_cleanup(&ctx); + + len += tmp; + *out_length = len; + + return 1; +} + +int +gost_kimp15(const EVP_CIPHER *ctr_cipher, const EVP_CIPHER *cmac_cipher, + const unsigned char *sexp, unsigned int sexp_length, + const unsigned char *key_mac, + const unsigned char *key_enc, + const unsigned char *iv, + unsigned char *out, size_t *out_length) +{ + CMAC_CTX *cmac_ctx = CMAC_CTX_new(); + EVP_CIPHER_CTX ctx; + unsigned char cmac[EVP_MAX_BLOCK_LENGTH]; + size_t cmac_length; + unsigned char tmp[EVP_MAX_KEY_LENGTH + EVP_MAX_BLOCK_LENGTH]; + unsigned int len = sizeof(tmp); + unsigned int len2; + + cmac_length = EVP_CIPHER_block_size(cmac_cipher); + if (*out_length > EVP_MAX_KEY_LENGTH || sexp_length < cmac_length || sexp_length != 32 + cmac_length) { + EVPerror(EVP_R_BAD_BLOCK_LENGTH); + return 0; + } + + EVP_CIPHER_CTX_init(&ctx); + if (!EVP_DecryptInit_ex(&ctx, ctr_cipher, NULL, key_enc, iv) || + !EVP_CIPHER_CTX_set_padding(&ctx, 0) || + !EVP_DecryptUpdate(&ctx, tmp, &len, sexp, sexp_length)) { + EVP_CIPHER_CTX_cleanup(&ctx); + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + return 0; + } + + len2 = sizeof(tmp) - len; + if (!EVP_DecryptFinal_ex(&ctx, tmp + len, &len2)) { + EVP_CIPHER_CTX_cleanup(&ctx); + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + return 0; + } + EVP_CIPHER_CTX_cleanup(&ctx); + len += len2; + + len2 = sexp_length - cmac_length; + if (CMAC_Init(cmac_ctx, key_mac, EVP_CIPHER_key_length(cmac_cipher), cmac_cipher, NULL) <= 0 || + CMAC_Update(cmac_ctx, iv, ctr_cipher->iv_len) <= 0 || + CMAC_Update(cmac_ctx, tmp, sexp_length - cmac_length) <= 0 || + CMAC_Final(cmac_ctx, cmac, &cmac_length) <= 0) { + CMAC_CTX_free(cmac_ctx); + GOSTerror(GOST_R_SIGNATURE_MISMATCH); + return 0; + } + + CMAC_CTX_free(cmac_ctx); + + if (timingsafe_memcmp(cmac, tmp + len2, cmac_length)) { + GOSTerror(GOST_R_SIGNATURE_MISMATCH); + return 0; + } + + memcpy(out, tmp, len2); + + return 1; +} + +int +gost_keg(EVP_PKEY *pub, EVP_PKEY *priv, int nid, + const unsigned char *ukm, unsigned char *keg_out) +{ + if (nid == NID_id_tc26_gost3411_2012_512) { + if (gost01_VKO_key(pub, priv, ukm, 16, 1, NID_id_tc26_gost3411_2012_512, keg_out) == 0) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + return 0; + } + + return 1; + } else { + unsigned char tmp[32]; + + if (gost01_VKO_key(pub, priv, ukm, 16, 1, NID_id_tc26_gost3411_2012_256, tmp) == 0) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + return 0; + } + + return KDF_TREE(EVP_streebog256(), NULL, + tmp, 32, + "kdf tree", 8, + ukm + 16, 8, + 1, + keg_out, 64); + } +} +#endif diff --git a/src/lib/libcrypto/gost/gost_locl.h b/src/lib/libcrypto/gost/gost_locl.h index f512029e1..13e9fd459 100644 --- a/src/lib/libcrypto/gost/gost_locl.h +++ b/src/lib/libcrypto/gost/gost_locl.h @@ -121,6 +121,8 @@ extern int VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey, GOST_KEY *priv_key, const BIGNUM *ukm); extern BIGNUM *GOST_le2bn(const unsigned char *buf, size_t len, BIGNUM *bn); extern int GOST_bn2le(BIGNUM *bn, unsigned char *buf, int len); +extern int gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm, + unsigned int ukm_len, int ukm_be, int out_nid, unsigned char *key); /* GOST R 34.10 parameters */ extern int GostR3410_get_md_digest(int nid); @@ -128,6 +130,21 @@ extern int GostR3410_get_pk_digest(int nid); extern int GostR3410_256_param_id(const char *value); extern int GostR3410_512_param_id(const char *value); +int gost_keg(EVP_PKEY *pub, EVP_PKEY *priv, int nid, + const unsigned char *ukm, unsigned char *keg_out); +int gost_kexp15(const EVP_CIPHER *cmac_cipher, const EVP_CIPHER *ctr_cipher, + const unsigned char *key, unsigned int key_length, + const unsigned char *key_mac, + const unsigned char *key_enc, + const unsigned char *iv, + unsigned char *out, size_t *out_length); +int gost_kimp15(const EVP_CIPHER *cmac_cipher, const EVP_CIPHER *ctr_cipher, + const unsigned char *sexp, unsigned int sexp_length, + const unsigned char *key_mac, + const unsigned char *key_enc, + const unsigned char *iv, + unsigned char *out, size_t *out_length); + __END_HIDDEN_DECLS #endif diff --git a/src/lib/libcrypto/gost/gostr341001.c b/src/lib/libcrypto/gost/gostr341001.c index ba70d5f1f..5ac36d25f 100644 --- a/src/lib/libcrypto/gost/gostr341001.c +++ b/src/lib/libcrypto/gost/gostr341001.c @@ -329,7 +329,7 @@ int VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey, GOST_KEY *priv_key, const BIGNUM *ukm) { - BIGNUM *p = NULL, *order = NULL; + BIGNUM *p = NULL, *order = NULL, *cofactor = NULL; const BIGNUM *key = GOST_KEY_get0_private_key(priv_key); const EC_GROUP *group = GOST_KEY_get0_group(priv_key); const EC_POINT *pub_key = GOST_KEY_get0_public_key(pkey); @@ -350,7 +350,13 @@ VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey, GOST_KEY *priv_key, goto err; if (EC_GROUP_get_order(group, order, ctx) == 0) goto err; - if (BN_mod_mul(p, key, ukm, order, ctx) == 0) + if ((cofactor = BN_CTX_get(ctx)) == NULL) + goto err; + if (EC_GROUP_get_cofactor(group, cofactor, ctx) == 0) + goto err; + if (BN_mod_mul(p, key, cofactor, order, ctx) == 0) + goto err; + if (!BN_is_zero(ukm) && BN_mod_mul(p, p, ukm, order, ctx) == 0) goto err; if (EC_POINT_mul(group, pnt, NULL, pub_key, p, ctx) == 0) goto err; diff --git a/src/lib/libcrypto/gost/gostr341001_pmeth.c b/src/lib/libcrypto/gost/gostr341001_pmeth.c index 0e0cae99e..455337232 100644 --- a/src/lib/libcrypto/gost/gostr341001_pmeth.c +++ b/src/lib/libcrypto/gost/gostr341001_pmeth.c @@ -130,8 +130,10 @@ struct gost_pmeth_data { int digest_nid; EVP_MD *md; unsigned char *shared_ukm; + unsigned int shared_ukm_len; int peer_key_used; int sig_format; + int enc_format; }; static int @@ -165,8 +167,8 @@ pkey_gost01_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) src_data = EVP_PKEY_CTX_get_data(src); dst_data = EVP_PKEY_CTX_get_data(dst); *dst_data = *src_data; - if (src_data->shared_ukm != NULL) - dst_data->shared_ukm = NULL; + dst_data->shared_ukm = NULL; + dst_data->shared_ukm_len = 0; return 1; } @@ -310,11 +312,12 @@ err: return ok; } -static int +int gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm, - unsigned char *key) + unsigned int ukm_len, int ukm_be, int out_nid, unsigned char *key) { unsigned char hashbuf[128]; + size_t hash_len; int digest_nid; int ret = 0; BN_CTX *ctx = BN_CTX_new(); @@ -331,7 +334,10 @@ gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm, if ((Y = BN_CTX_get(ctx)) == NULL) goto err; - GOST_le2bn(ukm, 8, UKM); + if (ukm_be) + BN_bin2bn (ukm, ukm_len, UKM); + else + GOST_le2bn(ukm, ukm_len, UKM); digest_nid = GOST_KEY_get_digest(priv_key->pkey.gost); if (VKO_compute_key(X, Y, pub_key->pkey.gost, priv_key->pkey.gost, @@ -340,27 +346,37 @@ gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm, switch (digest_nid) { case NID_id_GostR3411_94_CryptoProParamSet: - GOST_bn2le(X, hashbuf, 32); - GOST_bn2le(Y, hashbuf + 32, 32); - GOSTR341194(hashbuf, 64, key, digest_nid); - ret = 1; - break; case NID_id_tc26_gost3411_2012_256: GOST_bn2le(X, hashbuf, 32); GOST_bn2le(Y, hashbuf + 32, 32); - STREEBOG256(hashbuf, 64, key); - ret = 1; + hash_len = 64; break; case NID_id_tc26_gost3411_2012_512: GOST_bn2le(X, hashbuf, 64); GOST_bn2le(Y, hashbuf + 64, 64); - STREEBOG256(hashbuf, 128, key); - ret = 1; + hash_len = 128; break; default: ret = -2; + goto err; + } + + switch (out_nid) { + case NID_id_GostR3411_94_CryptoProParamSet: + GOSTR341194(hashbuf, hash_len, key, out_nid); + break; + case NID_id_tc26_gost3411_2012_256: + STREEBOG256(hashbuf, hash_len, key); + break; + case NID_id_tc26_gost3411_2012_512: + STREEBOG512(hashbuf, hash_len, key); break; + default: + ret = -2; + goto err; } + + ret = 1; err: BN_CTX_end(ctx); BN_CTX_free(ctx); @@ -368,7 +384,7 @@ err: } int -pkey_gost01_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, +pkey_gost01_decrypt_4490(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, const unsigned char *in, size_t in_len) { const unsigned char *p = in; @@ -428,8 +444,15 @@ pkey_gost01_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, goto err; } memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4); - if (gost01_VKO_key(peerkey, priv, wrappedKey, sharedKey) <= 0) + if (gost01_VKO_key(peerkey, priv, wrappedKey, 8, 0, + GOST_KEY_get_digest(priv->pkey.gost) == + NID_id_GostR3411_94_CryptoProParamSet ? + NID_id_GostR3411_94_CryptoProParamSet : + NID_id_tc26_gost3411_2012_256, + sharedKey) <= 0) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); goto err; + } if (gost_key_unwrap_crypto_pro(nid, sharedKey, wrappedKey, key) == 0) { GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); goto err; @@ -464,15 +487,22 @@ pkey_gost01_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen) return 32; } - if (gost01_VKO_key(peer_key, my_key, data->shared_ukm, key) <= 0) + if (gost01_VKO_key(peer_key, my_key, data->shared_ukm, 8, 0, + GOST_KEY_get_digest(my_key->pkey.gost) == + NID_id_GostR3411_94_CryptoProParamSet ? + NID_id_GostR3411_94_CryptoProParamSet : + NID_id_tc26_gost3411_2012_256, + key) <= 0) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); return 0; + } *keylen = 32; return 1; } int -pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, +pkey_gost01_encrypt_4490(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key, size_t key_len) { GOST_KEY_TRANSPORT *gkt = NULL; @@ -484,7 +514,7 @@ pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx); int nid = NID_id_Gost28147_89_CryptoPro_A_ParamSet; - if (data->shared_ukm != NULL) { + if (data->shared_ukm != NULL && data->shared_ukm_len >= 8) { memcpy(ukm, data->shared_ukm, 8); } else /* if (out != NULL) */ { arc4random_buf(ukm, 8); @@ -521,22 +551,29 @@ pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, } if (out != NULL) { - if (gost01_VKO_key(pubk, sec_key, ukm, shared_key) <= 0) + if (gost01_VKO_key(pubk, sec_key, ukm, 8, 0, + GOST_KEY_get_digest(pubk->pkey.gost) == + NID_id_GostR3411_94_CryptoProParamSet ? + NID_id_GostR3411_94_CryptoProParamSet : + NID_id_tc26_gost3411_2012_256, + shared_key) <= 0) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); goto err; + } gost_key_wrap_crypto_pro(nid, shared_key, ukm, key, crypted_key); } gkt = GOST_KEY_TRANSPORT_new(); - if (gkt == NULL) - goto err; - if (ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8) == 0) - goto err; - if (ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, - 4) == 0) + if (gkt == NULL) { + GOSTerror(ERR_R_MALLOC_FAILURE); goto err; - if (ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key, crypted_key + 8, - 32) == 0) + } + if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8) || + !ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4) || + !ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key, crypted_key + 8, 32)) { + GOSTerror(ERR_R_ASN1_LIB); goto err; + } if (key_is_ephemeral) { if (X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key, out != NULL ? sec_key : pubk) == 0) { @@ -568,6 +605,213 @@ err: return -1; } +int +pkey_gost01_decrypt_pskey(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, + const unsigned char *in, size_t in_len, + const EVP_CIPHER *enc_cipher, const EVP_CIPHER *mac_cipher) +{ + const unsigned char *p = in; + EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx); + GOST_KEY_TRANSPORT_PSKEY *gkt = NULL; + struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx); + EVP_PKEY *eph_key = NULL, *peerkey = NULL; + unsigned char keg_out[64]; + const unsigned char *ukm; + + if (key == NULL) { + *key_len = 32; + return 1; + } + + gkt = d2i_GOST_KEY_TRANSPORT_PSKEY(NULL, (const unsigned char **)&p, in_len); + if (gkt == NULL) { + GOSTerror(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO); + return -1; + } + + if (data->shared_ukm_len == 0) { + if (!gkt->ukm || gkt->ukm->length != 32) { + GOSTerror(ERR_R_ASN1_LIB); + goto err; + } + ukm = gkt->ukm->data; + } else if (data->shared_ukm_len != 32) { + GOSTerror(GOST_R_INVALID_IV_LENGTH); + goto err; + } else { + ukm = data->shared_ukm; + } + + eph_key = X509_PUBKEY_get(gkt->ephem_key); + if (eph_key == NULL) { + GOSTerror(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO); + goto err; + } + + if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) { + GOSTerror(GOST_R_INCOMPATIBLE_PEER_KEY); + goto err; + } + + peerkey = EVP_PKEY_CTX_get0_peerkey(pctx); + if (peerkey == NULL) { + GOSTerror(GOST_R_NO_PEER_KEY); + goto err; + } + + /* KEG */ + if (!gost_keg(peerkey, priv, data->digest_nid, ukm, keg_out)) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + goto err; + } + + if (!gost_kimp15(enc_cipher, mac_cipher, + gkt->key_exp->data, gkt->key_exp->length, + keg_out, keg_out + 32, + ukm + 24, + key, key_len)) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + goto err; + } + + GOST_KEY_TRANSPORT_PSKEY_free(gkt); + return 1; + +err: + GOST_KEY_TRANSPORT_PSKEY_free(gkt); + return 0; +} + +int +pkey_gost01_encrypt_pskey(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, + const unsigned char *key, size_t key_len, + const EVP_CIPHER *enc_cipher, const EVP_CIPHER *mac_cipher) +{ + GOST_KEY_TRANSPORT_PSKEY *gkt = NULL; + struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx); + EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx); + EVP_PKEY *sec_key = NULL; + GOST_KEY *tmp_key; + unsigned char ukm[32]; + unsigned char keg_out[64]; + unsigned char kexp_out[64]; + size_t kexp_len = sizeof(kexp_out); + int tmp_len; + + if (data->shared_ukm == NULL) { + arc4random_buf(ukm, sizeof(ukm)); + } else if (data->shared_ukm_len != sizeof(ukm)) { + GOSTerror(GOST_R_INVALID_IV_LENGTH); + return 0; + } else { + memcpy(ukm, data->shared_ukm, sizeof(ukm)); + } + + if (out) { + sec_key = EVP_PKEY_new(); + if (sec_key == NULL) { + GOSTerror(ERR_R_MALLOC_FAILURE); + goto err; + } + + tmp_key = GOST_KEY_new(); + if (tmp_key == NULL) { + GOSTerror(ERR_R_MALLOC_FAILURE); + goto err; + } + + if (EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), tmp_key) == 0) { + GOST_KEY_free(tmp_key); + goto err; + } + if (EVP_PKEY_copy_parameters(sec_key, pubk) == 0) + goto err; + if (gost2001_keygen(sec_key->pkey.gost) == 0) + goto err; + + /* KEG */ + if (!gost_keg(pubk, sec_key, data->digest_nid, ukm, keg_out)) { + GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY); + goto err; + } + + if (!gost_kexp15(enc_cipher, mac_cipher, + key, key_len, + keg_out, keg_out + 32, + ukm + 24, + kexp_out, &kexp_len)) + goto err; + } else { + kexp_len = key_len + EVP_CIPHER_block_size(mac_cipher); + } + + gkt = GOST_KEY_TRANSPORT_PSKEY_new(); + if (gkt == NULL) + goto err; + + if (!ASN1_OCTET_STRING_set(gkt->key_exp, kexp_out, kexp_len) || + !X509_PUBKEY_set(&gkt->ephem_key, out ? sec_key : pubk)) + goto err; + + if (data->shared_ukm == NULL && + (((gkt->ukm = ASN1_OCTET_STRING_new()) == NULL) || + !ASN1_OCTET_STRING_set(gkt->ukm, ukm, sizeof(ukm)))) + goto err; + + tmp_len = i2d_GOST_KEY_TRANSPORT_PSKEY(gkt, NULL); + if (!out) { + *out_len = tmp_len; + } else { + if (*out_len < tmp_len) + goto err; + *out_len = i2d_GOST_KEY_TRANSPORT_PSKEY(gkt, &out); + } + + EVP_PKEY_free(sec_key); + GOST_KEY_TRANSPORT_PSKEY_free(gkt); + + return 1; +err: + EVP_PKEY_free(sec_key); + GOST_KEY_TRANSPORT_PSKEY_free(gkt); + return 0; +} + +int +pkey_gost01_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *out_len, + const unsigned char *key, size_t key_len) +{ + struct gost_pmeth_data *pctx = EVP_PKEY_CTX_get_data(ctx); + + switch (pctx->enc_format) { + case GOST_ENC_FORMAT_4490: + return pkey_gost01_decrypt_4490(ctx, out, out_len, key, key_len); + case GOST_ENC_FORMAT_PSKEY_MAGMA: + return pkey_gost01_decrypt_pskey(ctx, out, out_len, key, key_len, EVP_magma_ctr(), EVP_magma_cbc()); + case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK: + return pkey_gost01_decrypt_pskey(ctx, out, out_len, key, key_len, EVP_kuznyechik_ctr(), EVP_kuznyechik_cbc()); + default: + return -1; + } +} + +int +pkey_gost01_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *out_len, + const unsigned char *key, size_t key_len) +{ + struct gost_pmeth_data *pctx = EVP_PKEY_CTX_get_data(ctx); + + switch (pctx->enc_format) { + case GOST_ENC_FORMAT_4490: + return pkey_gost01_encrypt_4490(ctx, out, out_len, key, key_len); + case GOST_ENC_FORMAT_PSKEY_MAGMA: + return pkey_gost01_encrypt_pskey(ctx, out, out_len, key, key_len, EVP_magma_ctr(), EVP_magma_cbc()); + case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK: + return pkey_gost01_encrypt_pskey(ctx, out, out_len, key, key_len, EVP_kuznyechik_ctr(), EVP_kuznyechik_cbc()); + default: + return -1; + } +} static int pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) @@ -604,6 +848,7 @@ pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) memcpy(ukm, p2, p1); free(pctx->shared_ukm); pctx->shared_ukm = ukm; + pctx->shared_ukm_len = p1; return 1; } @@ -631,6 +876,23 @@ pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) case EVP_PKEY_CTRL_GOST_GET_DIGEST: *(int *)p2 = pctx->digest_nid; return 1; + case EVP_PKEY_CTRL_GOST_ENC_FORMAT: + switch (p1) { + case GOST_ENC_FORMAT_4490: + /* All keys are supported */ + break; + case GOST_ENC_FORMAT_PSKEY_MAGMA: + case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK: + if (pctx->digest_nid != NID_id_tc26_gost3411_2012_256 && + pctx->digest_nid != NID_id_tc26_gost3411_2012_512) + return 0; + break; + default: + return 0; + } + pctx->enc_format = p1; + return 1; + break; default: return -2; } -- 2.17.1