From eeb9cdfc945783753c1de319a76608422df878aa Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 19 Mar 2008 18:39:51 +0000 Subject: Add support for KEK decrypt in cms utility. --- apps/cms.c | 27 +++++++++++- crypto/cms/cms.h | 19 ++++++++- crypto/cms/cms_env.c | 37 +++++++++++++---- crypto/cms/cms_err.c | 10 ++++- crypto/cms/cms_smime.c | 111 ++++++++++++++++++++++++++++++++++--------------- test/cms-test.pl | 21 ++++++++++ 6 files changed, 178 insertions(+), 47 deletions(-) diff --git a/apps/cms.c b/apps/cms.c index 6c5c4eb819..2cbd43b2a6 100644 --- a/apps/cms.c +++ b/apps/cms.c @@ -465,7 +465,7 @@ int MAIN(int argc, char **argv) } else if (operation == SMIME_DECRYPT) { - if (!recipfile && !keyfile) + if (!recipfile && !keyfile && !secret_key) { BIO_printf(bio_err, "No recipient certificate or key specified\n"); badarg = 1; @@ -838,7 +838,30 @@ int MAIN(int argc, char **argv) ret = 4; if (operation == SMIME_DECRYPT) { - if (!CMS_decrypt(cms, key, recip, indata, out, flags)) + + if (secret_key) + { + if (!CMS_decrypt_set1_key(cms, + secret_key, secret_keylen, + secret_keyid, secret_keyidlen)) + { + BIO_puts(bio_err, + "Error decrypting CMS using secret key\n"); + goto end; + } + } + + if (key) + { + if (!CMS_decrypt_set1_pkey(cms, key, recip)) + { + BIO_puts(bio_err, + "Error decrypting CMS using private key\n"); + goto end; + } + } + + if (!CMS_decrypt(cms, NULL, NULL, indata, out, flags)) { BIO_printf(bio_err, "Error decrypting CMS structure\n"); goto end; diff --git a/crypto/cms/cms.h b/crypto/cms/cms.h index 5a74c4bb21..49fdcf539c 100644 --- a/crypto/cms/cms.h +++ b/crypto/cms/cms.h @@ -166,6 +166,11 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *in, int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pkey, X509 *cert, BIO *data, BIO *dcont, unsigned int flags); + +int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert); +int CMS_decrypt_set1_key(CMS_ContentInfo *cms, + unsigned char *key, size_t keylen, + unsigned char *id, size_t idlen); STACK_OF(CMS_RecipientInfo) *CMS_get0_RecipientInfos(CMS_ContentInfo *cms); int CMS_RecipientInfo_type(CMS_RecipientInfo *ri); @@ -187,7 +192,13 @@ CMS_RecipientInfo *CMS_add0_recipient_key(CMS_ContentInfo *cms, int nid, ASN1_GENERALIZEDTIME *date, ASN1_OBJECT *otherTypeId, ASN1_TYPE *otherType); - + +int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri, + unsigned char *key, size_t keylen); + +int CMS_RecipientInfo_kekri_id_cmp(CMS_RecipientInfo *ri, + const unsigned char *id, size_t idlen); + int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri); int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out, @@ -297,6 +308,8 @@ void ERR_load_CMS_strings(void); #define CMS_F_CMS_DECRYPT 152 #define CMS_F_CMS_DECRYPTEDCONTENT_DECRYPT_BIO 145 #define CMS_F_CMS_DECRYPTEDCONTENT_ENCRYPT_BIO 143 +#define CMS_F_CMS_DECRYPT_SET1_KEY 167 +#define CMS_F_CMS_DECRYPT_SET1_PKEY 168 #define CMS_F_CMS_DIGESTALGORITHM_FIND_CTX 110 #define CMS_F_CMS_DIGESTALGORITHM_INIT_BIO 111 #define CMS_F_CMS_DIGESTEDDATA_DO_FINAL 112 @@ -322,9 +335,12 @@ void ERR_load_CMS_strings(void); #define CMS_F_CMS_GET0_REVOCATION_CHOICES 120 #define CMS_F_CMS_GET0_SIGNED 121 #define CMS_F_CMS_RECIPIENTINFO_DECRYPT 150 +#define CMS_F_CMS_RECIPIENTINFO_KEKI_KEY_CMP 164 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT 161 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT 162 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID 158 +#define CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP 166 +#define CMS_F_CMS_RECIPIENTINFO_KEKRI_KEY_CMP 165 #define CMS_F_CMS_RECIPIENTINFO_KTRI_CERT_CMP 122 #define CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT 160 #define CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT 155 @@ -359,6 +375,7 @@ void ERR_load_CMS_strings(void); #define CMS_R_CONTENT_VERIFY_ERROR 106 #define CMS_R_CTRL_ERROR 107 #define CMS_R_CTRL_FAILURE 108 +#define CMS_R_DECRYPT_ERROR 159 #define CMS_R_ERROR_GETTING_PUBLIC_KEY 109 #define CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE 110 #define CMS_R_ERROR_SETTING_KEY 155 diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index 1bea558216..d9487541c1 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -431,6 +431,24 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms, /* Key Encrypted Key (KEK) RecipientInfo routines */ +int CMS_RecipientInfo_kekri_id_cmp(CMS_RecipientInfo *ri, + const unsigned char *id, size_t idlen) + { + ASN1_OCTET_STRING tmp_os; + CMS_KEKRecipientInfo *kekri; + if (ri->type != CMS_RECIPINFO_KEK) + { + CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP, CMS_R_NOT_KEK); + return -2; + } + kekri = ri->d.kekri; + tmp_os.type = V_ASN1_OCTET_STRING; + tmp_os.flags = 0; + tmp_os.data = (unsigned char *)id; + tmp_os.length = (int)idlen; + return ASN1_OCTET_STRING_cmp(&tmp_os, kekri->kekid->keyIdentifier); + } + /* For now hard code AES key wrap info */ static size_t aes_wrap_keylen(int nid) @@ -605,20 +623,13 @@ int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri, unsigned char *key, size_t keylen) { CMS_KEKRecipientInfo *kekri; - int wrap_nid; if (ri->type != CMS_RECIPINFO_KEK) { CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY, CMS_R_NOT_KEK); return 0; } + kekri = ri->d.kekri; - wrap_nid = OBJ_obj2nid(kekri->keyEncryptionAlgorithm->algorithm); - if (aes_wrap_keylen(wrap_nid) != keylen) - { - CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY, - CMS_R_INVALID_KEY_LENGTH); - return 0; - } kekri->key = key; kekri->keylen = keylen; return 1; @@ -695,7 +706,7 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms, AES_KEY actx; unsigned char *ukey = NULL; int ukeylen; - int r = 0; + int r = 0, wrap_nid; ec = cms->d.envelopedData->encryptedContentInfo; @@ -707,6 +718,14 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms, return 0; } + wrap_nid = OBJ_obj2nid(kekri->keyEncryptionAlgorithm->algorithm); + if (aes_wrap_keylen(wrap_nid) != kekri->keylen) + { + CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, + CMS_R_INVALID_KEY_LENGTH); + return 0; + } + /* If encrypted key length is invalid don't bother */ if (kekri->encryptedKey->length < 16) diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c index f7df77ba90..6fbab4da12 100644 --- a/crypto/cms/cms_err.c +++ b/crypto/cms/cms_err.c @@ -71,7 +71,7 @@ static ERR_STRING_DATA CMS_str_functs[]= { {ERR_FUNC(CMS_F_CHECK_CONTENT), "CHECK_CONTENT"}, -{ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_KEY), "CMS_ADD0_RECIPIENT_KEY"}, +{ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_KEY), "CMS_add0_recipient_key"}, {ERR_FUNC(CMS_F_CMS_ADD1_RECIPIENT_CERT), "CMS_add1_recipient_cert"}, {ERR_FUNC(CMS_F_CMS_ADD1_SIGNER), "CMS_add1_signer"}, {ERR_FUNC(CMS_F_CMS_ADD1_SIGNINGTIME), "CMS_ADD1_SIGNINGTIME"}, @@ -88,6 +88,8 @@ static ERR_STRING_DATA CMS_str_functs[]= {ERR_FUNC(CMS_F_CMS_DECRYPT), "CMS_decrypt"}, {ERR_FUNC(CMS_F_CMS_DECRYPTEDCONTENT_DECRYPT_BIO), "CMS_DECRYPTEDCONTENT_DECRYPT_BIO"}, {ERR_FUNC(CMS_F_CMS_DECRYPTEDCONTENT_ENCRYPT_BIO), "CMS_DECRYPTEDCONTENT_ENCRYPT_BIO"}, +{ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_KEY), "CMS_DECRYPT_SET1_KEY"}, +{ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_PKEY), "CMS_DECRYPT_SET1_PKEY"}, {ERR_FUNC(CMS_F_CMS_DIGESTALGORITHM_FIND_CTX), "CMS_DIGESTALGORITHM_FIND_CTX"}, {ERR_FUNC(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO), "CMS_DIGESTALGORITHM_INIT_BIO"}, {ERR_FUNC(CMS_F_CMS_DIGESTEDDATA_DO_FINAL), "CMS_DIGESTEDDATA_DO_FINAL"}, @@ -113,15 +115,18 @@ static ERR_STRING_DATA CMS_str_functs[]= {ERR_FUNC(CMS_F_CMS_GET0_REVOCATION_CHOICES), "CMS_GET0_REVOCATION_CHOICES"}, {ERR_FUNC(CMS_F_CMS_GET0_SIGNED), "CMS_GET0_SIGNED"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_DECRYPT), "CMS_RecipientInfo_decrypt"}, +{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKI_KEY_CMP), "CMS_RECIPIENTINFO_KEKI_KEY_CMP"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT), "CMS_RECIPIENTINFO_KEKRI_DECRYPT"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT), "CMS_RECIPIENTINFO_KEKRI_ENCRYPT"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID), "CMS_RECIPIENTINFO_KEKRI_GET0_ID"}, +{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP), "CMS_RecipientInfo_kekri_id_cmp"}, +{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_KEY_CMP), "CMS_RECIPIENTINFO_KEKRI_KEY_CMP"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_CERT_CMP), "CMS_RecipientInfo_ktri_cert_cmp"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT), "CMS_RECIPIENTINFO_KTRI_DECRYPT"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT), "CMS_RECIPIENTINFO_KTRI_ENCRYPT"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_ALGS), "CMS_RecipientInfo_ktri_get0_algs"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_SIGNER_ID), "CMS_RecipientInfo_ktri_get0_signer_id"}, -{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_KEY), "CMS_RECIPIENTINFO_SET0_KEY"}, +{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_KEY), "CMS_RecipientInfo_set0_key"}, {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PKEY), "CMS_RecipientInfo_set0_pkey"}, {ERR_FUNC(CMS_F_CMS_SET1_SIGNERIDENTIFIER), "CMS_SET1_SIGNERIDENTIFIER"}, {ERR_FUNC(CMS_F_CMS_SET_DETACHED), "CMS_set_detached"}, @@ -153,6 +158,7 @@ static ERR_STRING_DATA CMS_str_reasons[]= {ERR_REASON(CMS_R_CONTENT_VERIFY_ERROR) ,"content verify error"}, {ERR_REASON(CMS_R_CTRL_ERROR) ,"ctrl error"}, {ERR_REASON(CMS_R_CTRL_FAILURE) ,"ctrl failure"}, +{ERR_REASON(CMS_R_DECRYPT_ERROR) ,"decrypt error"}, {ERR_REASON(CMS_R_ERROR_GETTING_PUBLIC_KEY),"error getting public key"}, {ERR_REASON(CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE),"error reading messagedigest attribute"}, {ERR_REASON(CMS_R_ERROR_SETTING_KEY) ,"error setting key"}, diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index c9be5a03e5..6388df89d1 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -493,12 +493,87 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data, CMS_ContentInfo_free(cms); return NULL; } + +int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert) + { + STACK_OF(CMS_RecipientInfo) *ris; + CMS_RecipientInfo *ri; + int i, r; + ris = CMS_get0_RecipientInfos(cms); + for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) + { + ri = sk_CMS_RecipientInfo_value(ris, i); + if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_TRANS) + continue; + /* If we have a cert try matching RecipientInfo + * otherwise try them all. + */ + if (!cert || (CMS_RecipientInfo_ktri_cert_cmp(ri, cert) == 0)) + { + CMS_RecipientInfo_set0_pkey(ri, pk); + r = CMS_RecipientInfo_decrypt(cms, ri); + CMS_RecipientInfo_set0_pkey(ri, NULL); + if (r > 0) + return 1; + if (cert) + { + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, + CMS_R_DECRYPT_ERROR); + return 0; + } + ERR_clear_error(); + } + } + + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT); + return 0; + + } + +int CMS_decrypt_set1_key(CMS_ContentInfo *cms, + unsigned char *key, size_t keylen, + unsigned char *id, size_t idlen) + { + STACK_OF(CMS_RecipientInfo) *ris; + CMS_RecipientInfo *ri; + int i, r; + ris = CMS_get0_RecipientInfos(cms); + for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) + { + ri = sk_CMS_RecipientInfo_value(ris, i); + if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_KEK) + continue; + + /* If we have an id try matching RecipientInfo + * otherwise try them all. + */ + if (!id || (CMS_RecipientInfo_kekri_id_cmp(ri, id, idlen) == 0)) + { + CMS_RecipientInfo_set0_key(ri, key, keylen); + r = CMS_RecipientInfo_decrypt(cms, ri); + CMS_RecipientInfo_set0_key(ri, NULL, 0); + if (r > 0) + return 1; + if (id) + { + CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, + CMS_R_DECRYPT_ERROR); + return 0; + } + ERR_clear_error(); + } + } + + CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, CMS_R_NO_MATCHING_RECIPIENT); + return 0; + + } int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert, BIO *dcont, BIO *out, unsigned int flags) { - int i, r; + int r; BIO *cont; if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) { @@ -507,39 +582,9 @@ int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert, } if (!dcont && !check_content(cms)) return 0; - if (pk) - { - STACK_OF(CMS_RecipientInfo) *ris; - CMS_RecipientInfo *ri; - ris = CMS_get0_RecipientInfos(cms); - for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) - { - ri = sk_CMS_RecipientInfo_value(ris, i); - if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_TRANS) - continue; - /* If we have a cert try matching RecipientInfo - * otherwise try them all. - */ - if (!cert || - (CMS_RecipientInfo_ktri_cert_cmp(ri, cert) == 0)) - { - CMS_RecipientInfo_set0_pkey(ri, pk); - r = CMS_RecipientInfo_decrypt(cms, ri); - CMS_RecipientInfo_set0_pkey(ri, NULL); - if (r > 0) - break; - if (cert) - return 0; - ERR_clear_error(); - } - } + if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert)) + return 0; - if (i == sk_CMS_RecipientInfo_num(ris)) - { - CMSerr(CMS_F_CMS_DECRYPT, CMS_R_NO_MATCHING_RECIPIENT); - return 0; - } - } cont = CMS_dataInit(cms, dcont); if (!cont) return 0; diff --git a/test/cms-test.pl b/test/cms-test.pl index 3dfbe8d690..db272e4d66 100644 --- a/test/cms-test.pl +++ b/test/cms-test.pl @@ -235,6 +235,27 @@ my @smime_cms_tests = ( "-decrypt -recip $smdir/smrsa1.pem -in test.cms -out smtst.txt" ], + [ + "enveloped content test streaming PEM format, KEK", + "-encrypt -in smcont.txt -outform PEM -aes128" + . " -stream -out test.cms " + . " -secretkey 000102030405060708090A0B0C0D0E0F " + . " -secretkeyid C0FEE0", + "-decrypt -in test.cms -out smtst.txt -inform PEM" + . " -secretkey 000102030405060708090A0B0C0D0E0F " + . " -secretkeyid C0FEE0" + ], + + [ + "enveloped content test streaming PEM format, KEK, key only", + "-encrypt -in smcont.txt -outform PEM -aes128" + . " -stream -out test.cms " + . " -secretkey 000102030405060708090A0B0C0D0E0F " + . " -secretkeyid C0FEE0", + "-decrypt -in test.cms -out smtst.txt -inform PEM" + . " -secretkey 000102030405060708090A0B0C0D0E0F " + ], + [ "data content test streaming PEM format", "-data_create -in smcont.txt -outform PEM -nodetach" -- cgit v1.2.3