diff options
author | Dmitry Belyavskiy <beldmit@gmail.com> | 2020-01-20 18:17:44 +0300 |
---|---|---|
committer | Dmitry Belyavskiy <beldmit@gmail.com> | 2020-03-03 16:34:40 +0300 |
commit | 71434aed0de274abe8f10768c4dd11a5b3b387e4 (patch) | |
tree | 3603b2e31c1b29a09990d028b0d9390b7a5a9fa5 /crypto/cms | |
parent | 88398d2a358fe41e33c61ac02f23ffaeacddcff0 (diff) |
Implementation of Russian GOST CMS
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/10904)
Diffstat (limited to 'crypto/cms')
-rw-r--r-- | crypto/cms/cms_env.c | 137 | ||||
-rw-r--r-- | crypto/cms/cms_kari.c | 93 | ||||
-rw-r--r-- | crypto/cms/cms_lib.c | 4 | ||||
-rw-r--r-- | crypto/cms/cms_local.h | 7 | ||||
-rw-r--r-- | crypto/cms/cms_smime.c | 40 |
5 files changed, 236 insertions, 45 deletions
diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index ac34f3efd6..003a406c68 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -20,6 +20,8 @@ /* CMS EnvelopedData Utilities */ +static void cms_env_set_version(CMS_EnvelopedData *env); + CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms) { if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_enveloped) { @@ -122,6 +124,47 @@ CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher) return NULL; } +int cms_EnvelopedData_final(CMS_ContentInfo *cms, BIO *chain) +{ + CMS_EnvelopedData *env = NULL; + EVP_CIPHER_CTX *ctx = NULL; + BIO *mbio = BIO_find_type(chain, BIO_TYPE_CIPHER); + + env = cms_get0_enveloped(cms); + if (env == NULL) + return 0; + + if (mbio == NULL) { + CMSerr(CMS_F_CMS_ENVELOPEDDATA_FINAL, CMS_R_CONTENT_NOT_FOUND); + return 0; + } + + BIO_get_cipher_ctx(mbio, &ctx); + + /* + * If the selected cipher supports unprotected attributes, + * deal with it using special ctrl function + */ + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_CIPHER_WITH_MAC) { + if (cms->d.envelopedData->unprotectedAttrs == NULL) + cms->d.envelopedData->unprotectedAttrs = sk_X509_ATTRIBUTE_new_null(); + + if (cms->d.envelopedData->unprotectedAttrs == NULL) { + CMSerr(CMS_F_CMS_ENVELOPEDDATA_FINAL, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_PROCESS_UNPROTECTED, + 1, env->unprotectedAttrs) <= 0) { + CMSerr(CMS_F_CMS_ENVELOPEDDATA_FINAL, CMS_R_CTRL_FAILURE); + return 0; + } + } + + cms_env_set_version(cms->d.envelopedData); + return 1; +} + /* Key Transport Recipient Info (KTRI) routines */ /* Initialise a ktri based on passed certificate and key */ @@ -176,8 +219,9 @@ static int cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip, * Add a recipient certificate using appropriate type of RecipientInfo */ -CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, - X509 *recip, unsigned int flags) +CMS_RecipientInfo *CMS_add1_recipient(CMS_ContentInfo *cms, X509 *recip, + EVP_PKEY *originatorPrivKey, + X509 *originator, unsigned int flags) { CMS_RecipientInfo *ri = NULL; CMS_EnvelopedData *env; @@ -193,7 +237,7 @@ CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, pk = X509_get0_pubkey(recip); if (pk == NULL) { - CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, CMS_R_ERROR_GETTING_PUBLIC_KEY); + CMSerr(CMS_F_CMS_ADD1_RECIPIENT, CMS_R_ERROR_GETTING_PUBLIC_KEY); goto err; } @@ -205,12 +249,12 @@ CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, break; case CMS_RECIPINFO_AGREE: - if (!cms_RecipientInfo_kari_init(ri, recip, pk, flags)) + if (!cms_RecipientInfo_kari_init(ri, recip, pk, originator, originatorPrivKey, flags)) goto err; break; default: - CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, + CMSerr(CMS_F_CMS_ADD1_RECIPIENT, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); goto err; @@ -222,13 +266,19 @@ CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, return ri; merr: - CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, ERR_R_MALLOC_FAILURE); + CMSerr(CMS_F_CMS_ADD1_RECIPIENT, ERR_R_MALLOC_FAILURE); err: M_ASN1_free_of(ri, CMS_RecipientInfo); return NULL; } +CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, + X509 *recip, unsigned int flags) +{ + return CMS_add1_recipient(cms, recip, NULL, NULL, flags); +} + int CMS_RecipientInfo_ktri_get0_algs(CMS_RecipientInfo *ri, EVP_PKEY **pk, X509 **recip, X509_ALGOR **palg) @@ -894,7 +944,34 @@ static void cms_env_set_version(CMS_EnvelopedData *env) env->version = 0; } -BIO *cms_EnvelopedData_init_bio(const CMS_ContentInfo *cms) +static BIO *cms_EnvelopedData_Decryption_init_bio(CMS_ContentInfo *cms) +{ + CMS_EncryptedContentInfo *ec = cms->d.envelopedData->encryptedContentInfo; + BIO *contentBio = cms_EncryptedContent_init_bio(ec); + EVP_CIPHER_CTX *ctx = NULL; + + if (contentBio == NULL) + return NULL; + + BIO_get_cipher_ctx(contentBio, &ctx); + if (ctx == NULL) { + BIO_free(contentBio); + return NULL; + } +/* + * If the selected cipher supports unprotected attributes, + * deal with it using special ctrl function + */ + if ((EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_CIPHER_WITH_MAC) + && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_PROCESS_UNPROTECTED, 0, + cms->d.envelopedData->unprotectedAttrs) <= 0) { + BIO_free(contentBio); + return NULL; + } + return contentBio; +} + +static BIO *cms_EnvelopedData_Encryption_init_bio(CMS_ContentInfo *cms) { CMS_EncryptedContentInfo *ec; STACK_OF(CMS_RecipientInfo) *rinfos; @@ -907,22 +984,19 @@ BIO *cms_EnvelopedData_init_bio(const CMS_ContentInfo *cms) ec = cms->d.envelopedData->encryptedContentInfo; ret = cms_EncryptedContent_init_bio(ec); - /* If error or no cipher end of processing */ - - if (!ret || !ec->cipher) + /* If error end of processing */ + if (!ret) return ret; /* Now encrypt content key according to each RecipientInfo type */ - rinfos = cms->d.envelopedData->recipientInfos; for (i = 0; i < sk_CMS_RecipientInfo_num(rinfos); i++) { - ri = sk_CMS_RecipientInfo_value(rinfos, i); - if (CMS_RecipientInfo_encrypt(cms, ri) <= 0) { - CMSerr(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO, - CMS_R_ERROR_SETTING_RECIPIENTINFO); - goto err; - } + ri = sk_CMS_RecipientInfo_value(rinfos, i); + if (CMS_RecipientInfo_encrypt(cms, ri) <= 0) { + CMSerr(0, CMS_R_ERROR_SETTING_RECIPIENTINFO); + goto err; + } } cms_env_set_version(cms->d.envelopedData); @@ -937,7 +1011,17 @@ BIO *cms_EnvelopedData_init_bio(const CMS_ContentInfo *cms) return ret; BIO_free(ret); return NULL; +} + +BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms) +{ + if (cms->d.envelopedData->encryptedContentInfo->cipher != NULL) { + /* If cipher is set it's encryption */ + return cms_EnvelopedData_Encryption_init_bio(cms); + } + /* If cipher is not set it's decryption */ + return cms_EnvelopedData_Decryption_init_bio(cms); } /* @@ -955,3 +1039,22 @@ int cms_pkey_get_ri_type(EVP_PKEY *pk) } return CMS_RECIPINFO_TRANS; } + +int cms_pkey_is_ri_type_supported(EVP_PKEY *pk, int ri_type) +{ + int supportedRiType; + + if (pk->ameth != NULL && pk->ameth->pkey_ctrl != NULL) { + int i, r; + + i = pk->ameth->pkey_ctrl(pk, ASN1_PKEY_CTRL_CMS_IS_RI_TYPE_SUPPORTED, ri_type, &r); + if (i > 0) + return r; + } + + supportedRiType = cms_pkey_get_ri_type(pk); + if (supportedRiType < 0) + return 0; + + return (supportedRiType == ri_type); +} diff --git a/crypto/cms/cms_kari.c b/crypto/cms/cms_kari.c index 6b0a59ebde..3299e9b5f5 100644 --- a/crypto/cms/cms_kari.c +++ b/crypto/cms/cms_kari.c @@ -152,7 +152,7 @@ int CMS_RecipientEncryptedKey_cert_cmp(CMS_RecipientEncryptedKey *rek, return -1; } -int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk) +int CMS_RecipientInfo_kari_set0_pkey_and_peer(CMS_RecipientInfo *ri, EVP_PKEY *pk, X509 *peer) { EVP_PKEY_CTX *pctx; CMS_KeyAgreeRecipientInfo *kari = ri->d.kari; @@ -161,9 +161,18 @@ int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk) kari->pctx = NULL; if (pk == NULL) return 1; + pctx = EVP_PKEY_CTX_new(pk, NULL); if (pctx == NULL || EVP_PKEY_derive_init(pctx) <= 0) goto err; + + if (peer != NULL) { + EVP_PKEY *pub_pkey = X509_get0_pubkey(peer); + + if (EVP_PKEY_derive_set_peer(pctx, pub_pkey) <= 0) + goto err; + } + kari->pctx = pctx; return 1; err: @@ -171,6 +180,11 @@ int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk) return 0; } +int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk) +{ + return CMS_RecipientInfo_kari_set0_pkey_and_peer(ri, pk, NULL); +} + EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri) { if (ri->type == CMS_RECIPINFO_AGREE) @@ -283,10 +297,29 @@ static int cms_kari_create_ephemeral_key(CMS_KeyAgreeRecipientInfo *kari, return rv; } +/* Set originator private key and initialise context based on it */ +static int cms_kari_set_originator_private_key(CMS_KeyAgreeRecipientInfo *kari, EVP_PKEY *originatorPrivKey ) +{ + EVP_PKEY_CTX *pctx = NULL; + int rv = 0; + + pctx = EVP_PKEY_CTX_new(originatorPrivKey, NULL); + if (pctx == NULL) + goto err; + if (EVP_PKEY_derive_init(pctx) <= 0) + goto err; + + kari->pctx = pctx; + rv = 1; + err: + if (rv == 0) + EVP_PKEY_CTX_free(pctx); + return rv; +} + /* Initialise a kari based on passed certificate and key */ -int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, - EVP_PKEY *pk, unsigned int flags) +int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, EVP_PKEY *recipPubKey, X509 * originator, EVP_PKEY *originatorPrivKey, unsigned int flags) { CMS_KeyAgreeRecipientInfo *kari; CMS_RecipientEncryptedKey *rek = NULL; @@ -321,12 +354,36 @@ int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, return 0; } - /* Create ephemeral key */ - if (!cms_kari_create_ephemeral_key(kari, pk)) - return 0; + if (originatorPrivKey == NULL && originator == NULL) { + /* Create ephemeral key */ + if (!cms_kari_create_ephemeral_key(kari, recipPubKey)) + return 0; + } else { + /* Use originator key */ + CMS_OriginatorIdentifierOrKey *oik = ri->d.kari->originator; + + if (originatorPrivKey == NULL && originator == NULL) + return 0; + + if (flags & CMS_USE_ORIGINATOR_KEYID) { + oik->type = CMS_OIK_KEYIDENTIFIER; + oik->d.subjectKeyIdentifier = ASN1_OCTET_STRING_new(); + if (oik->d.subjectKeyIdentifier == NULL) + return 0; + if (!cms_set1_keyid(&oik->d.subjectKeyIdentifier, originator)) + return 0; + } else { + oik->type = CMS_REK_ISSUER_SERIAL; + if (!cms_set1_ias(&oik->d.issuerAndSerialNumber, originator)) + return 0; + } + + if (!cms_kari_set_originator_private_key(kari, originatorPrivKey)) + return 0; + } - EVP_PKEY_up_ref(pk); - rek->pkey = pk; + EVP_PKEY_up_ref(recipPubKey); + rek->pkey = recipPubKey; return 1; } @@ -336,14 +393,30 @@ static int cms_wrap_init(CMS_KeyAgreeRecipientInfo *kari, EVP_CIPHER_CTX *ctx = kari->ctx; const EVP_CIPHER *kekcipher; int keylen = EVP_CIPHER_key_length(cipher); + int ret; + /* If a suitable wrap algorithm is already set nothing to do */ kekcipher = EVP_CIPHER_CTX_cipher(ctx); - - if (kekcipher) { + if (kekcipher != NULL) { if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_WRAP_MODE) return 0; return 1; } + else if (cipher != NULL + && (EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_GET_WRAP_CIPHER)) { + ret = EVP_CIPHER_meth_get_ctrl(cipher)(NULL, EVP_CTRL_GET_WRAP_CIPHER, + 0, &kekcipher); + if (ret <= 0) + return 0; + + if (kekcipher != NULL) { + if (EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE) + return 0; + + return EVP_EncryptInit_ex(ctx, kekcipher, NULL, NULL, NULL); + } + } + /* * Pick a cipher based on content encryption cipher. If it is DES3 use * DES3 wrap otherwise use AES wrap similar to key size. diff --git a/crypto/cms/cms_lib.c b/crypto/cms/cms_lib.c index 245544e3e9..15aba4af52 100644 --- a/crypto/cms/cms_lib.c +++ b/crypto/cms/cms_lib.c @@ -133,12 +133,14 @@ int CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio) switch (OBJ_obj2nid(cms->contentType)) { case NID_pkcs7_data: - case NID_pkcs7_enveloped: case NID_pkcs7_encrypted: case NID_id_smime_ct_compressedData: /* Nothing to do */ return 1; + case NID_pkcs7_enveloped: + return cms_EnvelopedData_final(cms, cmsbio); + case NID_pkcs7_signed: return cms_SignedData_final(cms, cmsbio); diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h index 6cb31955eb..46956a0947 100644 --- a/crypto/cms/cms_local.h +++ b/crypto/cms/cms_local.h @@ -402,13 +402,16 @@ int cms_Receipt_verify(CMS_ContentInfo *cms, CMS_ContentInfo *req_cms); int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src); ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si); -BIO *cms_EnvelopedData_init_bio(const CMS_ContentInfo *cms); +BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms); +int cms_EnvelopedData_final(CMS_ContentInfo *cms, BIO *chain); CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms); int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd); int cms_pkey_get_ri_type(EVP_PKEY *pk); +int cms_pkey_is_ri_type_supported(EVP_PKEY *pk, int ri_type); /* KARI routines */ int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, - EVP_PKEY *pk, unsigned int flags); + EVP_PKEY *recipPubKey, X509 *originator, + EVP_PKEY *originatorPrivKey, unsigned int flags); int cms_RecipientInfo_kari_encrypt(const CMS_ContentInfo *cms, CMS_RecipientInfo *ri); diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 4ae85c0335..d5112a83ea 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -576,19 +576,20 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data, return NULL; } -static int cms_kari_set1_pkey(CMS_ContentInfo *cms, CMS_RecipientInfo *ri, - EVP_PKEY *pk, X509 *cert) +static int cms_kari_set1_pkey_and_peer(CMS_ContentInfo *cms, CMS_RecipientInfo *ri, + EVP_PKEY *pk, X509 *cert, X509 *peer) { int i; STACK_OF(CMS_RecipientEncryptedKey) *reks; CMS_RecipientEncryptedKey *rek; + reks = CMS_RecipientInfo_kari_get0_reks(ri); for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++) { int rv; rek = sk_CMS_RecipientEncryptedKey_value(reks, i); if (cert != NULL && CMS_RecipientEncryptedKey_cert_cmp(rek, cert)) continue; - CMS_RecipientInfo_kari_set0_pkey(ri, pk); + CMS_RecipientInfo_kari_set0_pkey_and_peer(ri, pk, peer); rv = CMS_RecipientInfo_kari_decrypt(cms, ri, rek); CMS_RecipientInfo_kari_set0_pkey(ri, NULL); if (rv > 0) @@ -600,27 +601,36 @@ static int cms_kari_set1_pkey(CMS_ContentInfo *cms, CMS_RecipientInfo *ri, int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert) { + return CMS_decrypt_set1_pkey_and_peer(cms, pk, cert, NULL); +} + +int CMS_decrypt_set1_pkey_and_peer(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert, X509 *peer) +{ STACK_OF(CMS_RecipientInfo) *ris; CMS_RecipientInfo *ri; - int i, r, ri_type; + int i, r, cms_pkey_ri_type; int debug = 0, match_ri = 0; ris = CMS_get0_RecipientInfos(cms); if (ris) debug = cms->d.envelopedData->encryptedContentInfo->debug; - ri_type = cms_pkey_get_ri_type(pk); - if (ri_type == CMS_RECIPINFO_NONE) { - CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, - CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); - return 0; + + cms_pkey_ri_type = cms_pkey_get_ri_type(pk); + if (cms_pkey_ri_type == CMS_RECIPINFO_NONE) { + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY_AND_PEER, + CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); + return 0; } for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) { + int ri_type; + ri = sk_CMS_RecipientInfo_value(ris, i); - if (CMS_RecipientInfo_type(ri) != ri_type) + ri_type = CMS_RecipientInfo_type(ri); + if (!cms_pkey_is_ri_type_supported(pk, ri_type)) continue; match_ri = 1; if (ri_type == CMS_RECIPINFO_AGREE) { - r = cms_kari_set1_pkey(cms, ri, pk, cert); + r = cms_kari_set1_pkey_and_peer(cms, ri, pk, cert, peer); if (r > 0) return 1; if (r < 0) @@ -646,7 +656,7 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert) } if (r > 0) return 1; - CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_DECRYPT_ERROR); + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY_AND_PEER, CMS_R_DECRYPT_ERROR); return 0; } /* @@ -654,17 +664,17 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert) * successful decrypt. Always attempt to decrypt all recipients * to avoid leaking timing of a successful decrypt. */ - else if (r > 0 && debug) + else if (r > 0 && (debug || cms_pkey_ri_type != CMS_RECIPINFO_TRANS)) return 1; } } /* If no cert, key transport and not debugging always return success */ - if (cert == NULL && ri_type == CMS_RECIPINFO_TRANS && match_ri && !debug) { + if (cert == NULL && cms_pkey_ri_type == CMS_RECIPINFO_TRANS && match_ri && !debug) { ERR_clear_error(); return 1; } - CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT); + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY_AND_PEER, CMS_R_NO_MATCHING_RECIPIENT); return 0; } |