Experimental CMS password based recipient Info support.
authorDr. Stephen Henson <steve@openssl.org>
Thu, 26 Nov 2009 18:57:39 +0000 (18:57 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Thu, 26 Nov 2009 18:57:39 +0000 (18:57 +0000)
CHANGES
apps/cms.c
crypto/cms/Makefile
crypto/cms/cms.h
crypto/cms/cms_asn1.c
crypto/cms/cms_env.c
crypto/cms/cms_err.c
crypto/cms/cms_lcl.h
crypto/cms/cms_pwri.c [new file with mode: 0644]
crypto/cms/cms_smime.c

diff --git a/CHANGES b/CHANGES
index 44a9661..50f6cc1 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.0 and 1.1.0  [xx XXX xxxx]
 
+  *) Experiemental password based recipient info support for CMS library:
+     implementing RFC3211.
+     [Steve Henson]
+
   *) Split password based encryption into PBES2 and PBKDF2 functions. This
      neatly separates the code into cipher and PBE sections and is required
      for some algorithms that split PBES2 into separate pieces (such as
index d29a884..e8981c6 100644 (file)
@@ -136,6 +136,7 @@ int MAIN(int argc, char **argv)
        char *engine=NULL;
 #endif
        unsigned char *secret_key = NULL, *secret_keyid = NULL;
+       unsigned char *pwri_pass = NULL, *pwri_tmp = NULL;
        size_t secret_keylen = 0, secret_keyidlen = 0;
 
        ASN1_OBJECT *econtent_type = NULL;
@@ -326,6 +327,13 @@ int MAIN(int argc, char **argv)
                                }
                        secret_keyidlen = (size_t)ltmp;
                        }
+               else if (!strcmp(*args,"-pwri_password"))
+                       {
+                       if (!args[1])
+                               goto argerr;
+                       args++;
+                       pwri_pass = (unsigned char *)*args;
+                       }
                else if (!strcmp(*args,"-econtent_type"))
                        {
                        if (!args[1])
@@ -559,7 +567,7 @@ int MAIN(int argc, char **argv)
 
        else if (operation == SMIME_DECRYPT)
                {
-               if (!recipfile && !keyfile && !secret_key)
+               if (!recipfile && !keyfile && !secret_key && !pwri_pass)
                        {
                        BIO_printf(bio_err, "No recipient certificate or key specified\n");
                        badarg = 1;
@@ -567,7 +575,7 @@ int MAIN(int argc, char **argv)
                }
        else if (operation == SMIME_ENCRYPT)
                {
-               if (!*args && !secret_key)
+               if (!*args && !secret_key && !pwri_pass)
                        {
                        BIO_printf(bio_err, "No recipient(s) certificate(s) specified\n");
                        badarg = 1;
@@ -917,6 +925,17 @@ int MAIN(int argc, char **argv)
                        secret_key = NULL;
                        secret_keyid = NULL;
                        }
+               if (pwri_pass)
+                       {
+                       pwri_tmp = (unsigned char *)BUF_strdup((char *)pwri_pass);
+                       if (!pwri_tmp)
+                               goto end;
+                       if (!CMS_add0_recipient_password(cms,
+                                               -1, NID_undef, NID_undef,
+                                                pwri_tmp, -1, NULL))
+                               goto end;
+                       pwri_tmp = NULL;
+                       }
                if (!(flags & CMS_STREAM))
                        {
                        if (!CMS_final(cms, in, NULL, flags))
@@ -1043,6 +1062,16 @@ int MAIN(int argc, char **argv)
                                }
                        }
 
+               if (pwri_pass)
+                       {
+                       if (!CMS_decrypt_set1_password(cms, pwri_pass, -1))
+                               {
+                               BIO_puts(bio_err,
+                                       "Error decrypting CMS using password\n");
+                               goto end;
+                               }
+                       }
+
                if (!CMS_decrypt(cms, NULL, NULL, indata, out, flags))
                        {
                        BIO_printf(bio_err, "Error decrypting CMS structure\n");
@@ -1167,6 +1196,8 @@ end:
                OPENSSL_free(secret_key);
        if (secret_keyid)
                OPENSSL_free(secret_keyid);
+       if (pwri_tmp)
+               OPENSSL_free(pwri_tmp);
        if (econtent_type)
                ASN1_OBJECT_free(econtent_type);
        if (rr)
index 5837049..03a17cf 100644 (file)
@@ -18,9 +18,11 @@ APPS=
 
 LIB=$(TOP)/libcrypto.a
 LIBSRC= cms_lib.c cms_asn1.c cms_att.c cms_io.c cms_smime.c cms_err.c \
-       cms_sd.c cms_dd.c cms_cd.c cms_env.c cms_enc.c cms_ess.c
+       cms_sd.c cms_dd.c cms_cd.c cms_env.c cms_enc.c cms_ess.c \
+       cms_pwri.c
 LIBOBJ= cms_lib.o cms_asn1.o cms_att.o cms_io.o cms_smime.o cms_err.o \
-       cms_sd.o cms_dd.o cms_cd.o cms_env.o cms_enc.o cms_ess.o
+       cms_sd.o cms_dd.o cms_cd.o cms_env.o cms_enc.o cms_ess.o \
+       cms_pwri.o
 
 SRC= $(LIBSRC)
 
index 09c45d0..8d23021 100644 (file)
@@ -184,6 +184,8 @@ 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);
+int CMS_decrypt_set1_password(CMS_ContentInfo *cms, 
+                               unsigned char *pass, ssize_t passlen);
 
 STACK_OF(CMS_RecipientInfo) *CMS_get0_RecipientInfos(CMS_ContentInfo *cms);
 int CMS_RecipientInfo_type(CMS_RecipientInfo *ri);
@@ -219,6 +221,14 @@ int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri,
 int CMS_RecipientInfo_kekri_id_cmp(CMS_RecipientInfo *ri, 
                                        const unsigned char *id, size_t idlen);
 
+int CMS_RecipientInfo_set0_password(CMS_RecipientInfo *ri, 
+                                       unsigned char *pass, ssize_t passlen);
+
+CMS_RecipientInfo *CMS_add0_recipient_password(CMS_ContentInfo *cms,
+                                       int iter, int wrap_nid, int pbe_nid,
+                                       unsigned char *pass, ssize_t passlen,
+                                       const EVP_CIPHER *kekciph);
+
 int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri);
        
 int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out,
@@ -330,6 +340,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_F_CHECK_CONTENT                             99
 #define CMS_F_CMS_ADD0_CERT                             164
 #define CMS_F_CMS_ADD0_RECIPIENT_KEY                    100
+#define CMS_F_CMS_ADD0_RECIPIENT_PASSWORD               165
 #define CMS_F_CMS_ADD1_RECEIPTREQUEST                   158
 #define CMS_F_CMS_ADD1_RECIPIENT_CERT                   101
 #define CMS_F_CMS_ADD1_SIGNER                           102
@@ -344,6 +355,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_F_CMS_DATAINIT                              111
 #define CMS_F_CMS_DECRYPT                               112
 #define CMS_F_CMS_DECRYPT_SET1_KEY                      113
+#define CMS_F_CMS_DECRYPT_SET1_PASSWORD                         166
 #define CMS_F_CMS_DECRYPT_SET1_PKEY                     114
 #define CMS_F_CMS_DIGESTALGORITHM_FIND_CTX              115
 #define CMS_F_CMS_DIGESTALGORITHM_INIT_BIO              116
@@ -378,7 +390,9 @@ void ERR_load_CMS_strings(void);
 #define CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT            141
 #define CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_ALGS          142
 #define CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_SIGNER_ID     143
+#define CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT              167
 #define CMS_F_CMS_RECIPIENTINFO_SET0_KEY                144
+#define CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD           168
 #define CMS_F_CMS_RECIPIENTINFO_SET0_PKEY               145
 #define CMS_F_CMS_SET1_SIGNERIDENTIFIER                         146
 #define CMS_F_CMS_SET_DETACHED                          147
@@ -419,6 +433,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_R_ERROR_SETTING_KEY                                 115
 #define CMS_R_ERROR_SETTING_RECIPIENTINFO               116
 #define CMS_R_INVALID_ENCRYPTED_KEY_LENGTH              117
+#define CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER          176
 #define CMS_R_INVALID_KEY_LENGTH                        118
 #define CMS_R_MD_BIO_INIT_ERROR                                 119
 #define CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH      120
@@ -431,6 +446,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_R_NOT_ENCRYPTED_DATA                        122
 #define CMS_R_NOT_KEK                                   123
 #define CMS_R_NOT_KEY_TRANSPORT                                 124
+#define CMS_R_NOT_PWRI                                  177
 #define CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE           125
 #define CMS_R_NO_CIPHER                                         126
 #define CMS_R_NO_CONTENT                                127
@@ -443,6 +459,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_R_NO_MATCHING_RECIPIENT                     132
 #define CMS_R_NO_MATCHING_SIGNATURE                     166
 #define CMS_R_NO_MSGSIGDIGEST                           167
+#define CMS_R_NO_PASSWORD                               178
 #define CMS_R_NO_PRIVATE_KEY                            133
 #define CMS_R_NO_PUBLIC_KEY                             134
 #define CMS_R_NO_RECEIPT_REQUEST                        168
@@ -466,10 +483,12 @@ void ERR_load_CMS_strings(void);
 #define CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM                 151
 #define CMS_R_UNSUPPORTED_CONTENT_TYPE                  152
 #define CMS_R_UNSUPPORTED_KEK_ALGORITHM                         153
+#define CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM      179
 #define CMS_R_UNSUPPORTED_RECIPIENT_TYPE                154
 #define CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE             155
 #define CMS_R_UNSUPPORTED_TYPE                          156
 #define CMS_R_UNWRAP_ERROR                              157
+#define CMS_R_UNWRAP_FAILURE                            180
 #define CMS_R_VERIFICATION_FAILURE                      158
 #define CMS_R_WRAP_ERROR                                159
 
index 7f7132c..835cae4 100644 (file)
@@ -237,6 +237,15 @@ static int cms_ri_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
                                OPENSSL_free(kekri->key);
                                }
                        }
+               else if (ri->type == CMS_RECIPINFO_PASS)
+                       {
+                       CMS_PasswordRecipientInfo *pwri = ri->d.pwri;
+                       if (pwri->pass)
+                               {
+                               OPENSSL_cleanse(pwri->pass, pwri->passlen);
+                               OPENSSL_free(pwri->pass);
+                               }
+                       }
                }
        return 1;
        }
index b3237d4..87d67d3 100644 (file)
 /* CMS EnvelopedData Utilities */
 
 DECLARE_ASN1_ITEM(CMS_EnvelopedData)
-DECLARE_ASN1_ITEM(CMS_RecipientInfo)
 DECLARE_ASN1_ITEM(CMS_KeyTransRecipientInfo)
 DECLARE_ASN1_ITEM(CMS_KEKRecipientInfo)
 DECLARE_ASN1_ITEM(CMS_OtherKeyAttribute)
 
 DECLARE_STACK_OF(CMS_RecipientInfo)
 
-static CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms)
+CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms)
        {
        if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_enveloped)
                {
@@ -786,6 +785,9 @@ int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
                case CMS_RECIPINFO_KEK:
                return cms_RecipientInfo_kekri_decrypt(cms, ri);
 
+               case CMS_RECIPINFO_PASS:
+               return cms_RecipientInfo_pwri_crypt(cms, ri, 0);
+
                default:
                CMSerr(CMS_F_CMS_RECIPIENTINFO_DECRYPT,
                        CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE);
@@ -829,6 +831,10 @@ BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
                        r = cms_RecipientInfo_kekri_encrypt(cms, ri);
                        break;
 
+                       case CMS_RECIPINFO_PASS:
+                       r = cms_RecipientInfo_pwri_crypt(cms, ri, 1);
+                       break;
+
                        default:
                        CMSerr(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO,
                                CMS_R_UNSUPPORTED_RECIPIENT_TYPE);
index ff7b030..8330ead 100644 (file)
@@ -1,6 +1,6 @@
 /* crypto/cms/cms_err.c */
 /* ====================================================================
- * Copyright (c) 1999-2007 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2009 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -73,6 +73,7 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CHECK_CONTENT),        "CHECK_CONTENT"},
 {ERR_FUNC(CMS_F_CMS_ADD0_CERT),        "CMS_add0_cert"},
 {ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_KEY),       "CMS_add0_recipient_key"},
+{ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD),  "CMS_add0_recipient_password"},
 {ERR_FUNC(CMS_F_CMS_ADD1_RECEIPTREQUEST),      "CMS_add1_ReceiptRequest"},
 {ERR_FUNC(CMS_F_CMS_ADD1_RECIPIENT_CERT),      "CMS_add1_recipient_cert"},
 {ERR_FUNC(CMS_F_CMS_ADD1_SIGNER),      "CMS_add1_signer"},
@@ -87,6 +88,7 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CMS_DATAINIT), "CMS_dataInit"},
 {ERR_FUNC(CMS_F_CMS_DECRYPT),  "CMS_decrypt"},
 {ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_KEY), "CMS_decrypt_set1_key"},
+{ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_PASSWORD),    "CMS_decrypt_set1_password"},
 {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"},
@@ -105,7 +107,7 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CMS_GET0_CERTIFICATE_CHOICES), "CMS_GET0_CERTIFICATE_CHOICES"},
 {ERR_FUNC(CMS_F_CMS_GET0_CONTENT),     "CMS_get0_content"},
 {ERR_FUNC(CMS_F_CMS_GET0_ECONTENT_TYPE),       "CMS_GET0_ECONTENT_TYPE"},
-{ERR_FUNC(CMS_F_CMS_GET0_ENVELOPED),   "CMS_GET0_ENVELOPED"},
+{ERR_FUNC(CMS_F_CMS_GET0_ENVELOPED),   "cms_get0_enveloped"},
 {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_MSGSIGDIGEST_ADD1),        "cms_msgSigDigest_add1"},
@@ -121,7 +123,9 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {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_PWRI_CRYPT), "cms_RecipientInfo_pwri_crypt"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_KEY),   "CMS_RecipientInfo_set0_key"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD),      "CMS_RecipientInfo_set0_password"},
 {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"},
@@ -165,6 +169,7 @@ static ERR_STRING_DATA CMS_str_reasons[]=
 {ERR_REASON(CMS_R_ERROR_SETTING_KEY)     ,"error setting key"},
 {ERR_REASON(CMS_R_ERROR_SETTING_RECIPIENTINFO),"error setting recipientinfo"},
 {ERR_REASON(CMS_R_INVALID_ENCRYPTED_KEY_LENGTH),"invalid encrypted key length"},
+{ERR_REASON(CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER),"invalid key encryption parameter"},
 {ERR_REASON(CMS_R_INVALID_KEY_LENGTH)    ,"invalid key length"},
 {ERR_REASON(CMS_R_MD_BIO_INIT_ERROR)     ,"md bio init error"},
 {ERR_REASON(CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH),"messagedigest attribute wrong length"},
@@ -177,6 +182,7 @@ static ERR_STRING_DATA CMS_str_reasons[]=
 {ERR_REASON(CMS_R_NOT_ENCRYPTED_DATA)    ,"not encrypted data"},
 {ERR_REASON(CMS_R_NOT_KEK)               ,"not kek"},
 {ERR_REASON(CMS_R_NOT_KEY_TRANSPORT)     ,"not key transport"},
+{ERR_REASON(CMS_R_NOT_PWRI)              ,"not pwri"},
 {ERR_REASON(CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE),"not supported for this key type"},
 {ERR_REASON(CMS_R_NO_CIPHER)             ,"no cipher"},
 {ERR_REASON(CMS_R_NO_CONTENT)            ,"no content"},
@@ -189,6 +195,7 @@ static ERR_STRING_DATA CMS_str_reasons[]=
 {ERR_REASON(CMS_R_NO_MATCHING_RECIPIENT) ,"no matching recipient"},
 {ERR_REASON(CMS_R_NO_MATCHING_SIGNATURE) ,"no matching signature"},
 {ERR_REASON(CMS_R_NO_MSGSIGDIGEST)       ,"no msgsigdigest"},
+{ERR_REASON(CMS_R_NO_PASSWORD)           ,"no password"},
 {ERR_REASON(CMS_R_NO_PRIVATE_KEY)        ,"no private key"},
 {ERR_REASON(CMS_R_NO_PUBLIC_KEY)         ,"no public key"},
 {ERR_REASON(CMS_R_NO_RECEIPT_REQUEST)    ,"no receipt request"},
@@ -212,10 +219,12 @@ static ERR_STRING_DATA CMS_str_reasons[]=
 {ERR_REASON(CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM),"unsupported compression algorithm"},
 {ERR_REASON(CMS_R_UNSUPPORTED_CONTENT_TYPE),"unsupported content type"},
 {ERR_REASON(CMS_R_UNSUPPORTED_KEK_ALGORITHM),"unsupported kek algorithm"},
+{ERR_REASON(CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM),"unsupported key encryption algorithm"},
 {ERR_REASON(CMS_R_UNSUPPORTED_RECIPIENT_TYPE),"unsupported recipient type"},
 {ERR_REASON(CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE),"unsupported recpientinfo type"},
 {ERR_REASON(CMS_R_UNSUPPORTED_TYPE)      ,"unsupported type"},
 {ERR_REASON(CMS_R_UNWRAP_ERROR)          ,"unwrap error"},
+{ERR_REASON(CMS_R_UNWRAP_FAILURE)        ,"unwrap failure"},
 {ERR_REASON(CMS_R_VERIFICATION_FAILURE)  ,"verification failure"},
 {ERR_REASON(CMS_R_WRAP_ERROR)            ,"wrap error"},
 {0,NULL}
index c8ecfa7..5aea7f8 100644 (file)
@@ -273,6 +273,9 @@ struct CMS_PasswordRecipientInfo_st
        X509_ALGOR *keyDerivationAlgorithm;
        X509_ALGOR *keyEncryptionAlgorithm;
        ASN1_OCTET_STRING *encryptedKey;
+       /* Extra info: password to use */
+       unsigned char *pass;
+       size_t passlen;
        };
 
 struct CMS_OtherRecipientInfo_st
@@ -411,6 +414,8 @@ DECLARE_ASN1_ITEM(CMS_SignerInfo)
 DECLARE_ASN1_ITEM(CMS_IssuerAndSerialNumber)
 DECLARE_ASN1_ITEM(CMS_Attributes_Sign)
 DECLARE_ASN1_ITEM(CMS_Attributes_Verify)
+DECLARE_ASN1_ITEM(CMS_RecipientInfo)
+DECLARE_ASN1_ITEM(CMS_PasswordRecipientInfo)
 DECLARE_ASN1_ALLOC_FUNCTIONS(CMS_IssuerAndSerialNumber)
 
 #define CMS_SIGNERINFO_ISSUER_SERIAL   0
@@ -454,6 +459,11 @@ int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src);
 ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si);
 
 BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms);
+CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms);
+
+/* PWRI routines */
+int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                                       int en_de);
        
 #ifdef  __cplusplus
 }
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
new file mode 100644 (file)
index 0000000..5fe7f49
--- /dev/null
@@ -0,0 +1,453 @@
+/* crypto/cms/cms_pwri.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 2009 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "cryptlib.h"
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/cms.h>
+#include <openssl/rand.h>
+#include <openssl/aes.h>
+#include "cms_lcl.h"
+#include "asn1_locl.h"
+
+int CMS_RecipientInfo_set0_password(CMS_RecipientInfo *ri, 
+                               unsigned char *pass, ssize_t passlen)
+       {
+       CMS_PasswordRecipientInfo *pwri;
+       if (ri->type != CMS_RECIPINFO_PASS)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD, CMS_R_NOT_PWRI);
+               return 0;
+               }
+
+       pwri = ri->d.pwri;
+       pwri->pass = pass;
+       if (pass && passlen < 0)
+               passlen = strlen((char *)pass);
+       pwri->passlen = passlen;
+       return 1;
+       }
+
+CMS_RecipientInfo *CMS_add0_recipient_password(CMS_ContentInfo *cms,
+                                       int iter, int wrap_nid, int pbe_nid,
+                                       unsigned char *pass, ssize_t passlen,
+                                       const EVP_CIPHER *kekciph)
+       {
+       CMS_RecipientInfo *ri = NULL;
+       CMS_EnvelopedData *env;
+       CMS_PasswordRecipientInfo *pwri;
+       EVP_CIPHER_CTX ctx;
+       X509_ALGOR *encalg = NULL;
+       unsigned char iv[EVP_MAX_IV_LENGTH];
+       int ivlen;
+       env = cms_get0_enveloped(cms);
+       if (!env)
+               goto err;
+
+       if (wrap_nid <= 0)
+               wrap_nid = NID_id_alg_PWRI_KEK;
+
+       if (pbe_nid <= 0)
+               pbe_nid = NID_id_pbkdf2;
+
+       /* Get from enveloped data */
+       if (kekciph == NULL)
+               kekciph = env->encryptedContentInfo->cipher;
+
+       if (kekciph == NULL)
+               {
+               CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, CMS_R_NO_CIPHER);
+               return NULL;
+               }
+       if (wrap_nid != NID_id_alg_PWRI_KEK)
+               {
+               CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+                               CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+               return NULL;
+               }
+
+       /* Setup algorithm identifier for cipher */
+       encalg = X509_ALGOR_new();
+       EVP_CIPHER_CTX_init(&ctx);
+
+       if (EVP_EncryptInit_ex(&ctx, kekciph, NULL, NULL, NULL) <= 0)
+               {
+               CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_EVP_LIB);
+               goto err;
+               }
+
+       ivlen = EVP_CIPHER_CTX_iv_length(&ctx);
+
+       if (ivlen > 0)
+               {
+               if (RAND_pseudo_bytes(iv, ivlen) <= 0)
+                       goto err;
+               if (EVP_EncryptInit_ex(&ctx, NULL, NULL, NULL, iv) <= 0)
+                       {
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+                                                       ERR_R_EVP_LIB);
+                       goto err;
+                       }
+               encalg->parameter = ASN1_TYPE_new();
+               if (!encalg->parameter)
+                       {
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+                                                       ERR_R_MALLOC_FAILURE);
+                       goto err;
+                       }
+               if (EVP_CIPHER_param_to_asn1(&ctx, encalg->parameter) <= 0)
+                       {
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+                               CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+                       goto err;
+                       }
+               }
+
+
+       encalg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(&ctx));
+
+       EVP_CIPHER_CTX_cleanup(&ctx);
+
+       /* Initialize recipient info */
+       ri = M_ASN1_new_of(CMS_RecipientInfo);
+       if (!ri)
+               goto merr;
+
+       ri->d.pwri = M_ASN1_new_of(CMS_PasswordRecipientInfo);
+       if (!ri->d.pwri)
+               goto merr;
+       ri->type = CMS_RECIPINFO_PASS;
+
+       pwri = ri->d.pwri;
+       /* Since this is overwritten, free up empty structure already there */
+       X509_ALGOR_free(pwri->keyEncryptionAlgorithm);
+       pwri->keyEncryptionAlgorithm = X509_ALGOR_new();
+       if (!pwri->keyEncryptionAlgorithm)
+               goto merr;
+       pwri->keyEncryptionAlgorithm->algorithm = OBJ_nid2obj(wrap_nid);
+       pwri->keyEncryptionAlgorithm->parameter = ASN1_TYPE_new();
+       if (!pwri->keyEncryptionAlgorithm->parameter)
+               goto merr;
+
+        if(!ASN1_item_pack(encalg, ASN1_ITEM_rptr(X509_ALGOR),
+           &pwri->keyEncryptionAlgorithm->parameter->value.sequence))
+               goto merr;
+        pwri->keyEncryptionAlgorithm->parameter->type = V_ASN1_SEQUENCE;
+
+       X509_ALGOR_free(encalg);
+       encalg = NULL;
+
+       /* Setup PBE algorithm */
+
+       pwri->keyDerivationAlgorithm = PKCS5_pbkdf2_set(iter, NULL, 0, -1, -1);
+
+       if (!pwri->keyDerivationAlgorithm)
+               goto err;
+
+       CMS_RecipientInfo_set0_password(ri, pass, passlen);
+       pwri->version = 0;
+
+       if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
+               goto merr;
+
+       return ri;
+
+       merr:
+       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_MALLOC_FAILURE);
+       err:
+       EVP_CIPHER_CTX_cleanup(&ctx);
+       if (ri)
+               M_ASN1_free_of(ri, CMS_RecipientInfo);
+       if (encalg)
+               X509_ALGOR_free(encalg);
+       return NULL;
+
+       }
+
+/* This is an implementation of the key wrapping mechanism in RFC3211,
+ * at some point this should go into EVP.
+ */
+
+static int kek_unwrap_key(unsigned char *out, size_t *outlen,
+               const unsigned char *in, size_t inlen, EVP_CIPHER_CTX *ctx)
+       {
+       size_t blocklen = EVP_CIPHER_CTX_block_size(ctx);
+       unsigned char *tmp;
+       int outl, rv = 0;
+       if (inlen < 2 * blocklen)
+               {
+               /* too small */
+               return 0;
+               }
+       if (inlen % blocklen)
+               {
+               /* Invalid size */
+               return 0;
+               }
+       tmp = OPENSSL_malloc(inlen);
+       /* setup IV by decrypting last two blocks */
+       EVP_DecryptUpdate(ctx, tmp + inlen - 2 * blocklen, &outl,
+                               in  + inlen - 2 * blocklen, blocklen * 2);
+       /* Do a decrypt of last decrypted block to set IV to correct value
+        * output it to start of buffer so we don't corrupt decrypted block
+        * this works because buffer is at least two block lengths long.
+        */
+       EVP_DecryptUpdate(ctx, tmp, &outl,
+                               tmp  + inlen - blocklen, blocklen);
+       /* Can now decrypt first n - 1 blocks */
+       EVP_DecryptUpdate(ctx, tmp, &outl, in, inlen - blocklen);
+
+       /* Reset IV to original value */
+       EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, NULL);
+       /* Decrypt again */
+       EVP_DecryptUpdate(ctx, tmp, &outl, tmp, inlen);
+       /* Check check bytes */
+       if (((tmp[1] ^ tmp[4]) & (tmp[2] ^ tmp[5]) & (tmp[3] ^ tmp[6])) != 0xff)
+               {
+               /* Check byte failure */
+               goto err;
+               }
+       if (inlen < (size_t)(tmp[0] - 4 ))
+               {
+               /* Invalid length value */
+               goto err;
+               }
+       *outlen = (size_t)tmp[0];
+       memcpy(out, tmp + 4, *outlen);
+       rv = 1;
+       err:
+       OPENSSL_cleanse(tmp, inlen);
+       OPENSSL_free(tmp);
+       return rv;
+
+       }
+
+static int kek_wrap_key(unsigned char *out, size_t *outlen,
+               const unsigned char *in, size_t inlen, EVP_CIPHER_CTX *ctx)
+       {
+       size_t blocklen = EVP_CIPHER_CTX_block_size(ctx);
+       size_t olen;
+       int dummy;
+       /* First decide length of output buffer: need header and round up to
+        * multiple of block length.
+        */
+       olen = (inlen + 4 + blocklen - 1)/blocklen;
+       olen *= blocklen;
+       if (olen < 2 * blocklen)
+               {
+               /* Key too small */
+               return 0;
+               }
+       if (inlen > 0xFF)
+               {
+               /* Key too large */
+               return 0;
+               }
+       if (out)
+               {
+               /* Set header */
+               out[0] = (unsigned char)inlen;
+               out[1] = in[0] ^ 0xFF;
+               out[2] = in[1] ^ 0xFF;
+               out[3] = in[2] ^ 0xFF;
+               memcpy(out + 4, in, inlen);
+               /* Add random padding to end */
+               if (olen > inlen + 4)
+                       RAND_pseudo_bytes(out + 4 + inlen, olen - 4 - inlen);
+               /* Encrypt twice */
+               EVP_EncryptUpdate(ctx, out, &dummy, out, olen);
+               EVP_EncryptUpdate(ctx, out, &dummy, out, olen);
+               }
+
+       *outlen = olen;
+
+       return 1;
+       }
+
+/* Encrypt/Decrypt content key in PWRI recipient info */
+
+int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                                       int en_de)
+       {
+       CMS_EncryptedContentInfo *ec;
+       CMS_PasswordRecipientInfo *pwri;
+       const unsigned char *p = NULL;
+       int plen;
+       int r = 0;
+       X509_ALGOR *algtmp, *kekalg = NULL;
+       EVP_CIPHER_CTX kekctx;
+       const EVP_CIPHER *kekcipher;
+       unsigned char *key = NULL;
+       size_t keylen;
+
+       ec = cms->d.envelopedData->encryptedContentInfo;
+
+       pwri = ri->d.pwri;
+       EVP_CIPHER_CTX_init(&kekctx);
+
+       if (!pwri->pass)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, CMS_R_NO_PASSWORD);
+               return 0;
+               }
+       algtmp = pwri->keyEncryptionAlgorithm;
+
+       if (!algtmp || OBJ_obj2nid(algtmp->algorithm) != NID_id_alg_PWRI_KEK)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                               CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+               return 0;
+               }
+
+       if (algtmp->parameter->type == V_ASN1_SEQUENCE)
+               {
+               p = algtmp->parameter->value.sequence->data;
+               plen = algtmp->parameter->value.sequence->length;
+               kekalg = d2i_X509_ALGOR(NULL, &p, plen);
+               }
+       if (kekalg == NULL)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                               CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER);
+               return 0;
+               }
+
+       kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
+               
+       if(!kekcipher)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                               CMS_R_UNKNOWN_CIPHER);
+               goto err;
+               }
+
+       /* Fixup cipher based on AlgorithmIdentifier to set IV etc */
+       if (!EVP_CipherInit_ex(&kekctx, kekcipher, NULL, NULL, NULL, en_de))
+               goto err;
+       EVP_CIPHER_CTX_set_padding(&kekctx, 0);
+       if(EVP_CIPHER_asn1_to_param(&kekctx, kekalg->parameter) < 0)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                               CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+               goto err;
+               }
+
+       algtmp = pwri->keyDerivationAlgorithm;
+
+       /* Finish password based key derivation to setup key in "ctx" */
+
+       if (EVP_PBE_CipherInit(algtmp->algorithm,
+                               (char *)pwri->pass, pwri->passlen,
+                               algtmp->parameter, &kekctx, en_de) < 0)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, ERR_R_EVP_LIB);
+               goto err;
+               }
+
+       /* Finally wrap/unwrap the key */
+
+       if (en_de)
+               {
+
+               if (!kek_wrap_key(NULL, &keylen, ec->key, ec->keylen, &kekctx))
+                       goto err;
+
+               key = OPENSSL_malloc(keylen);
+
+               if (!key)
+                       goto err;
+
+               if (!kek_wrap_key(key, &keylen, ec->key, ec->keylen, &kekctx))
+                       goto err;
+               pwri->encryptedKey->data = key;
+               pwri->encryptedKey->length = keylen;
+               }
+       else
+               {
+               key = OPENSSL_malloc(pwri->encryptedKey->length);
+
+               if (!key)
+                       {
+                       CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                                                       ERR_R_MALLOC_FAILURE);
+                       goto err;
+                       }
+               if (!kek_unwrap_key(key, &keylen,
+                                       pwri->encryptedKey->data,
+                                       pwri->encryptedKey->length, &kekctx))
+                       {
+                       CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+                                                       CMS_R_UNWRAP_FAILURE);
+                       goto err;
+                       }
+
+               ec->key = key;
+               ec->keylen = keylen;
+
+               }
+
+       r = 1;
+
+       err:
+
+       EVP_CIPHER_CTX_cleanup(&kekctx);
+
+       if (!r && key)
+               OPENSSL_free(key);
+       X509_ALGOR_free(kekalg);
+
+       return r;
+
+       }
index 4a799eb..ab38a25 100644 (file)
@@ -680,6 +680,30 @@ int CMS_decrypt_set1_key(CMS_ContentInfo *cms,
        return 0;
 
        }
+
+int CMS_decrypt_set1_password(CMS_ContentInfo *cms, 
+                               unsigned char *pass, ssize_t passlen)
+       {
+       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_PASS)
+                               continue;
+               CMS_RecipientInfo_set0_password(ri, pass, passlen);
+               r = CMS_RecipientInfo_decrypt(cms, ri);
+               CMS_RecipientInfo_set0_password(ri, NULL, 0);
+               if (r > 0)
+                       return 1;
+               }
+
+       CMSerr(CMS_F_CMS_DECRYPT_SET1_PASSWORD, CMS_R_NO_MATCHING_RECIPIENT);
+       return 0;
+
+       }
        
 int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
                                BIO *dcont, BIO *out,