From bb3f59aa72cd62dd7fb3f99a36d42b75110f2a94 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 8 Apr 2020 21:27:46 +0300 Subject: [PATCH 64/87] gost: add support for decoding KeyAgreement CMS files Sponsored by ROSA Linux Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/gost/gost.h | 1 + src/lib/libcrypto/gost/gost_err.c | 1 + src/lib/libcrypto/gost/gostr341001_ameth.c | 285 +++++++++++++++++++-- 3 files changed, 263 insertions(+), 24 deletions(-) diff --git a/src/lib/libcrypto/gost/gost.h b/src/lib/libcrypto/gost/gost.h index 694906b76..25beb4a8a 100644 --- a/src/lib/libcrypto/gost/gost.h +++ b/src/lib/libcrypto/gost/gost.h @@ -301,6 +301,7 @@ void ERR_load_GOST_strings(void); #define GOST_R_SIGNATURE_MISMATCH 121 #define GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q 122 #define GOST_R_UKM_NOT_SET 123 +#define GOST_R_NO_ORIGINATOR_CERTIFICATE 124 #ifdef __cplusplus } diff --git a/src/lib/libcrypto/gost/gost_err.c b/src/lib/libcrypto/gost/gost_err.c index e7111dd34..ea734897c 100644 --- a/src/lib/libcrypto/gost/gost_err.c +++ b/src/lib/libcrypto/gost/gost_err.c @@ -96,6 +96,7 @@ static ERR_STRING_DATA GOST_str_reasons[] = { {ERR_REASON(GOST_R_SIGNATURE_MISMATCH) ,"signature mismatch"}, {ERR_REASON(GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q),"signature parts greater than q"}, {ERR_REASON(GOST_R_UKM_NOT_SET) ,"ukm not set"}, + {ERR_REASON(GOST_R_NO_ORIGINATOR_CERTIFICATE), "originator certificate not provided"}, {0, NULL} }; #endif diff --git a/src/lib/libcrypto/gost/gostr341001_ameth.c b/src/lib/libcrypto/gost/gostr341001_ameth.c index 965b36237..2b59642fe 100644 --- a/src/lib/libcrypto/gost/gostr341001_ameth.c +++ b/src/lib/libcrypto/gost/gostr341001_ameth.c @@ -263,25 +263,24 @@ pkey_bits_gost01(const EVP_PKEY *pk) } static int -pub_decode_gost01(EVP_PKEY *pk, X509_PUBKEY *pub) +pub_decode_gost01_int(EVP_PKEY *pk, X509_ALGOR *palg, const unsigned char *pubkey_buf, int pub_len) { - X509_ALGOR *palg = NULL; - const unsigned char *pubkey_buf = NULL; + const ASN1_OBJECT *poid; const unsigned char *p; - ASN1_OBJECT *palgobj = NULL; - int pub_len; BIGNUM *X, *Y; ASN1_OCTET_STRING *octet = NULL; int len; int ret; int ptype = V_ASN1_UNDEF; ASN1_STRING *pval = NULL; + int nid; - if (X509_PUBKEY_get0_param(&palgobj, &pubkey_buf, &pub_len, &palg, pub) - == 0) + X509_ALGOR_get0(&poid, &ptype, (const void **)&pval, palg); + nid = OBJ_obj2nid(poid); + if (nid != NID_id_GostR3410_2001 && + nid != NID_id_tc26_gost3410_2012_256 && + nid != NID_id_tc26_gost3410_2012_512) return 0; - (void)EVP_PKEY_assign_GOST(pk, NULL); - X509_ALGOR_get0(NULL, &ptype, (const void **)&pval, palg); if (ptype != V_ASN1_SEQUENCE) { GOSTerror(GOST_R_BAD_KEY_PARAMETERS_FORMAT); return 0; @@ -315,26 +314,44 @@ pub_decode_gost01(EVP_PKEY *pk, X509_PUBKEY *pub) } static int -pub_encode_gost01(X509_PUBKEY *pub, const EVP_PKEY *pk) +pub_decode_gost01(EVP_PKEY *pk, X509_PUBKEY *pub) +{ + X509_ALGOR *palg = NULL; + const unsigned char *pubkey_buf = NULL; + int pub_len; + + if (X509_PUBKEY_get0_param(NULL, &pubkey_buf, &pub_len, &palg, pub) == 0) + return 0; + (void)EVP_PKEY_assign_GOST(pk, NULL); + + return pub_decode_gost01_int(pk, palg, pubkey_buf, pub_len); +} + +static int +pub_encode_gost01_int(const EVP_PKEY *pk, ASN1_OBJECT **palgobj, ASN1_STRING **pparams, unsigned char **pbuf, int *plen) { ASN1_OBJECT *algobj = NULL; ASN1_OCTET_STRING *octet = NULL; ASN1_STRING *params = NULL; - void *pval = NULL; unsigned char *buf = NULL, *sptr; int key_size, ret = 0; const EC_POINT *pub_key; BIGNUM *X = NULL, *Y = NULL; const GOST_KEY *ec = pk->pkey.gost; - int ptype = V_ASN1_UNDEF; + + *palgobj = NULL; + *pparams = NULL; + *pbuf = NULL; + *plen = 0; algobj = OBJ_nid2obj(GostR3410_get_pk_digest(GOST_KEY_get_digest(ec))); + if (algobj == NULL) + return 0; + if (pk->save_parameters) { params = encode_gost01_algor_params(pk); if (params == NULL) - return 0; - pval = params; - ptype = V_ASN1_SEQUENCE; + goto err; } key_size = GOST_KEY_get_size(ec); @@ -375,21 +392,44 @@ pub_encode_gost01(X509_PUBKEY *pub, const EVP_PKEY *pk) GOST_bn2le(X, sptr, key_size); GOST_bn2le(Y, sptr + key_size, key_size); - BN_free(Y); - BN_free(X); - ret = i2d_ASN1_OCTET_STRING(octet, &buf); - ASN1_BIT_STRING_free(octet); if (ret < 0) - return 0; + goto err; - return X509_PUBKEY_set0_param(pub, algobj, ptype, pval, buf, ret); + *palgobj = algobj; + *pparams = params; + *pbuf = buf; + *plen = ret; + + return 1; err: BN_free(Y); BN_free(X); ASN1_BIT_STRING_free(octet); ASN1_STRING_free(params); + ASN1_OBJECT_free(algobj); + return 0; +} + +static int +pub_encode_gost01(X509_PUBKEY *pub, const EVP_PKEY *pk) +{ + ASN1_OBJECT *algobj = NULL; + ASN1_STRING *params = NULL; + unsigned char *buf = NULL; + int len; + + if (pub_encode_gost01_int(pk, &algobj, ¶ms, &buf, &len) <= 0) + return 0; + + if (X509_PUBKEY_set0_param(pub, algobj, V_ASN1_SEQUENCE, params, buf, len) == 1) + return 1; + + free(buf); + ASN1_STRING_free(params); + ASN1_OBJECT_free(algobj); + return 0; } @@ -890,6 +930,89 @@ int gost01_smime_encrypt(EVP_PKEY_CTX *ctx, X509_ALGOR *alg, int enc_nid) } #ifndef OPENSSL_NO_CMS +static int +gost01_cms_set_peerkey(EVP_PKEY_CTX *pctx, X509_ALGOR *alg, + ASN1_BIT_STRING *pubkey) +{ + int rv = 0; + EVP_PKEY *pkpeer = NULL; + int ret; + + pkpeer = EVP_PKEY_new(); + if (pkpeer == NULL) + return 0; + (void)EVP_PKEY_assign_GOST(pkpeer, NULL); + + ret = pub_decode_gost01_int(pkpeer, alg, pubkey->data, pubkey->length); + if (ret <= 0) + goto err; + + if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0) + rv = 1; + err: + + EVP_PKEY_free(pkpeer); + return rv; +} + +static int +gost01_cms_decrypt_kari(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) +{ + EVP_CIPHER_CTX *kekctx; + X509_ALGOR *alg; + ASN1_OCTET_STRING *ukm; + const EVP_CIPHER *kekcipher; + + if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm)) + return 0; + + if (ukm == NULL) + return 0; + + if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DERIVE, + EVP_PKEY_CTRL_SET_IV, ukm->length, ukm->data) < 0) + return 0; + + if (!EVP_PKEY_CTX_get0_peerkey(pctx)) { + ASN1_BIT_STRING *pubkey; + X509_ALGOR *pkalg; + + if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &pkalg, &pubkey, + NULL, NULL, NULL)) + return 0; + if (!pkalg || !pubkey) { + GOSTerror(GOST_R_NO_ORIGINATOR_CERTIFICATE); + return 0; + } + if (!gost01_cms_set_peerkey(pctx, pkalg, pubkey)) { + GOSTerror(GOST_R_INCOMPATIBLE_PEER_KEY); + return 0; + } + } + + if (alg->parameter->type != V_ASN1_SEQUENCE) + return 0; + + kekctx = CMS_RecipientInfo_kari_get0_ctx(ri); + if (!kekctx) + return 0; + + kekcipher = EVP_get_cipherbyobj(alg->algorithm); + if (!kekcipher || EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE) + return 0; + if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, ukm->data)) + return 0; + + if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DERIVE, + EVP_PKEY_CTRL_GOST_DERIVE_FORMAT, + GOST_DERIVE_FORMAT_KEG, NULL) <= 0) { + GOSTerror(ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + static int gost01_cms_decrypt(CMS_RecipientInfo *ri) { @@ -899,12 +1022,116 @@ gost01_cms_decrypt(CMS_RecipientInfo *ri) pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri); if (pkctx == NULL) return 0; + switch (CMS_RecipientInfo_type(ri)) { + case CMS_RECIPINFO_TRANS: + if (!CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &cmsalg)) + return 0; + return gost01_smime_decrypt(pkctx, cmsalg); + case CMS_RECIPINFO_AGREE: + return gost01_cms_decrypt_kari(pkctx, ri); + default: + GOSTerror(ERR_R_INTERNAL_ERROR); + return 0; + } +} + +static int +gost01_cms_encrypt_kari(CMS_RecipientInfo *ri) +{ + EVP_PKEY_CTX *pctx; + EVP_PKEY *pkey; + EVP_CIPHER_CTX *ctx; + X509_ALGOR *talg, *wrap_alg = NULL; + ASN1_BIT_STRING *pubkey; + int wrap_nid; + unsigned char iv[32]; + ASN1_STRING *params; + + pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); + if (!pctx) + return 0; - if (!CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &cmsalg)) + /* Get ephemeral key */ + pkey = EVP_PKEY_CTX_get0_pkey(pctx); + if (pkey == NULL) return 0; - return gost01_smime_decrypt(pkctx, cmsalg); + if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DERIVE, + EVP_PKEY_CTRL_GOST_DERIVE_FORMAT, + GOST_DERIVE_FORMAT_KEG, NULL) <= 0) { + GOSTerror(ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey, + NULL, NULL, NULL)) + goto err; + + /* Ephemeral key */ + if (talg) { + const ASN1_OBJECT *aoid = NULL; + + X509_ALGOR_get0(&aoid, NULL, NULL, talg); + + /* Is everything uninitialised? */ + if (aoid == OBJ_nid2obj(NID_undef)) { + ASN1_OBJECT *algobj = NULL; + ASN1_STRING *params = NULL; + unsigned char *buf = NULL; + int len; + + if (pub_encode_gost01_int(pkey, &algobj, ¶ms, &buf, &len) <= 0) + return 0; + + X509_ALGOR_set0(talg, algobj, V_ASN1_SEQUENCE, params); + ASN1_STRING_set0(pubkey, buf, len); + } + } + + /* Get wrap NID */ + ctx = CMS_RecipientInfo_kari_get0_ctx(ri); + wrap_nid = EVP_CIPHER_CTX_type(ctx); + + /* Package wrap algorithm in an AlgorithmIdentifier */ + + if (!CMS_RecipientInfo_kari_get0_alg(ri, &wrap_alg, NULL)) + goto err; + if (wrap_alg == NULL) + goto err; + if ((params = encode_gost01_kexp_params(pkey)) == NULL) + goto err; + X509_ALGOR_set0(wrap_alg, OBJ_nid2obj(wrap_nid), V_ASN1_SEQUENCE, params); + + arc4random_buf(iv, sizeof(iv)); + if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DERIVE, + EVP_PKEY_CTRL_SET_IV, sizeof(iv), iv) < 0) + goto err; + + if (!EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, -1)) + goto err; + + if (!CMS_RecipientInfo_kari_set0_ukm(ri, iv, sizeof(iv))) + goto err; + + return 1; +err: + return 0; +} + +static int +gost01_cms_encrypt(CMS_RecipientInfo *ri) +{ + switch (CMS_RecipientInfo_type(ri)) { + case CMS_RECIPINFO_TRANS: + /* do nothing, handled in pmeth */ + return 1; + case CMS_RECIPINFO_AGREE: + return gost01_cms_encrypt_kari(ri); + default: + return 0; + } } + #endif static int @@ -921,9 +1148,19 @@ pkey_ctrl_gost01(EVP_PKEY *pkey, int op, long arg1, void *arg2) &alg1, &alg2); break; case ASN1_PKEY_CTRL_CMS_ENVELOPE: - if (arg1 == 1) + if (arg1 == 0) + return gost01_cms_encrypt(arg2); + else if (arg1 == 1) return gost01_cms_decrypt(arg2); break; + case ASN1_PKEY_CTRL_CMS_RI_TYPE: + if (arg2 != NULL) + *(int *)arg2 = CMS_RECIPINFO_TRANS; /* default */ + break; + case ASN1_PKEY_CTRL_CMS_IS_RI_TYPE_SUPPORTED: + if (arg2 != NULL) + *(int *)arg2 = (arg1 == CMS_RECIPINFO_TRANS || arg1 == CMS_RECIPINFO_AGREE); + break; #endif case ASN1_PKEY_CTRL_PKCS7_SIGN: if (arg1 == 0) -- 2.17.1