1/* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34/* 35 * CMS encryptedData methods. 36 */ 37 38#include <Security/SecCmsEncryptedData.h> 39 40#include <Security/SecCmsContentInfo.h> 41 42#include "cmslocal.h" 43 44#include "secitem.h" 45#include "secoid.h" 46#include <security_asn1/secasn1.h> 47#include <security_asn1/secerr.h> 48 49/* 50 * SecCmsEncryptedDataCreate - create an empty encryptedData object. 51 * 52 * "algorithm" specifies the bulk encryption algorithm to use. 53 * "keysize" is the key size. 54 * 55 * An error results in a return value of NULL and an error set. 56 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 57 */ 58SecCmsEncryptedDataRef 59SecCmsEncryptedDataCreate(SecCmsMessageRef cmsg, SECOidTag algorithm, int keysize) 60{ 61 void *mark; 62 SecCmsEncryptedDataRef encd; 63 PLArenaPool *poolp; 64 SECAlgorithmID *pbe_algid; 65 OSStatus rv; 66 67 poolp = cmsg->poolp; 68 69 mark = PORT_ArenaMark(poolp); 70 71 encd = (SecCmsEncryptedDataRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsEncryptedData)); 72 if (encd == NULL) 73 goto loser; 74 75 encd->cmsg = cmsg; 76 77 /* version is set in SecCmsEncryptedDataEncodeBeforeStart() */ 78 79 switch (algorithm) { 80 /* XXX hmmm... hardcoded algorithms? */ 81 case SEC_OID_RC2_CBC: 82 case SEC_OID_DES_EDE3_CBC: 83 case SEC_OID_DES_CBC: 84 rv = SecCmsContentInfoSetContentEncAlg((SecArenaPoolRef)poolp, &(encd->contentInfo), algorithm, NULL, keysize); 85 break; 86 default: 87 /* Assume password-based-encryption. At least, try that. */ 88#if 1 89 // @@@ Fix me 90 pbe_algid = NULL; 91#else 92 pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL); 93#endif 94 if (pbe_algid == NULL) { 95 rv = SECFailure; 96 break; 97 } 98 rv = SecCmsContentInfoSetContentEncAlgID((SecArenaPoolRef)poolp, &(encd->contentInfo), pbe_algid, keysize); 99 SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); 100 break; 101 } 102 if (rv != SECSuccess) 103 goto loser; 104 105 PORT_ArenaUnmark(poolp, mark); 106 return encd; 107 108loser: 109 PORT_ArenaRelease(poolp, mark); 110 return NULL; 111} 112 113/* 114 * SecCmsEncryptedDataDestroy - destroy an encryptedData object 115 */ 116void 117SecCmsEncryptedDataDestroy(SecCmsEncryptedDataRef encd) 118{ 119 /* everything's in a pool, so don't worry about the storage */ 120 SecCmsContentInfoDestroy(&(encd->contentInfo)); 121 return; 122} 123 124/* 125 * SecCmsEncryptedDataGetContentInfo - return pointer to encryptedData object's contentInfo 126 */ 127SecCmsContentInfoRef 128SecCmsEncryptedDataGetContentInfo(SecCmsEncryptedDataRef encd) 129{ 130 return &(encd->contentInfo); 131} 132 133/* 134 * SecCmsEncryptedDataEncodeBeforeStart - do all the necessary things to a EncryptedData 135 * before encoding begins. 136 * 137 * In particular: 138 * - set the correct version value. 139 * - get the encryption key 140 */ 141OSStatus 142SecCmsEncryptedDataEncodeBeforeStart(SecCmsEncryptedDataRef encd) 143{ 144 int version; 145 SecSymmetricKeyRef bulkkey = NULL; 146 CSSM_DATA_PTR dummy; 147 SecCmsContentInfoRef cinfo = &(encd->contentInfo); 148 149 if (SecCmsArrayIsEmpty((void **)encd->unprotectedAttr)) 150 version = SEC_CMS_ENCRYPTED_DATA_VERSION; 151 else 152 version = SEC_CMS_ENCRYPTED_DATA_VERSION_UPATTR; 153 154 dummy = SEC_ASN1EncodeInteger (encd->cmsg->poolp, &(encd->version), version); 155 if (dummy == NULL) 156 return SECFailure; 157 158 /* now get content encryption key (bulk key) by using our cmsg callback */ 159 if (encd->cmsg->decrypt_key_cb) 160 bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, 161 SecCmsContentInfoGetContentEncAlg(cinfo)); 162 if (bulkkey == NULL) 163 return SECFailure; 164 165 /* store the bulk key in the contentInfo so that the encoder can find it */ 166 SecCmsContentInfoSetBulkKey(cinfo, bulkkey); 167 CFRelease(bulkkey); /* This assumes the decrypt_key_cb hands us a copy of the key --mb */ 168 169 return SECSuccess; 170} 171 172/* 173 * SecCmsEncryptedDataEncodeBeforeData - set up encryption 174 */ 175OSStatus 176SecCmsEncryptedDataEncodeBeforeData(SecCmsEncryptedDataRef encd) 177{ 178 SecCmsContentInfoRef cinfo; 179 SecSymmetricKeyRef bulkkey; 180 SECAlgorithmID *algid; 181 182 cinfo = &(encd->contentInfo); 183 184 /* find bulkkey and algorithm - must have been set by SecCmsEncryptedDataEncodeBeforeStart */ 185 bulkkey = SecCmsContentInfoGetBulkKey(cinfo); 186 if (bulkkey == NULL) 187 return SECFailure; 188 algid = SecCmsContentInfoGetContentEncAlg(cinfo); 189 if (algid == NULL) 190 return SECFailure; 191 192 /* this may modify algid (with IVs generated in a token). 193 * it is therefore essential that algid is a pointer to the "real" contentEncAlg, 194 * not just to a copy */ 195 cinfo->ciphcx = SecCmsCipherContextStartEncrypt(encd->cmsg->poolp, bulkkey, algid); 196 CFRelease(bulkkey); 197 if (cinfo->ciphcx == NULL) 198 return SECFailure; 199 200 return SECSuccess; 201} 202 203/* 204 * SecCmsEncryptedDataEncodeAfterData - finalize this encryptedData for encoding 205 */ 206OSStatus 207SecCmsEncryptedDataEncodeAfterData(SecCmsEncryptedDataRef encd) 208{ 209 if (encd->contentInfo.ciphcx) { 210 SecCmsCipherContextDestroy(encd->contentInfo.ciphcx); 211 encd->contentInfo.ciphcx = NULL; 212 } 213 214 /* nothing to do after data */ 215 return SECSuccess; 216} 217 218 219/* 220 * SecCmsEncryptedDataDecodeBeforeData - find bulk key & set up decryption 221 */ 222OSStatus 223SecCmsEncryptedDataDecodeBeforeData(SecCmsEncryptedDataRef encd) 224{ 225 SecSymmetricKeyRef bulkkey = NULL; 226 SecCmsContentInfoRef cinfo; 227 SECAlgorithmID *bulkalg; 228 OSStatus rv = SECFailure; 229 230 cinfo = &(encd->contentInfo); 231 232 bulkalg = SecCmsContentInfoGetContentEncAlg(cinfo); 233 234 if (encd->cmsg->decrypt_key_cb == NULL) /* no callback? no key../ */ 235 goto loser; 236 237 bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg); 238 if (bulkkey == NULL) 239 /* no success finding a bulk key */ 240 goto loser; 241 242 SecCmsContentInfoSetBulkKey(cinfo, bulkkey); 243 244 cinfo->ciphcx = SecCmsCipherContextStartDecrypt(bulkkey, bulkalg); 245 if (cinfo->ciphcx == NULL) 246 goto loser; /* error has been set by SecCmsCipherContextStartDecrypt */ 247 248#if 1 249 // @@@ Not done yet 250#else 251 /* 252 * HACK ALERT!! 253 * For PKCS5 Encryption Algorithms, the bulkkey is actually a different 254 * structure. Therefore, we need to set the bulkkey to the actual key 255 * prior to freeing it. 256 */ 257 if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) { 258 SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey; 259 bulkkey = keyPwd->key; 260 } 261#endif 262 263 /* we are done with (this) bulkkey now. */ 264 CFRelease(bulkkey); 265 266 rv = SECSuccess; 267 268loser: 269 return rv; 270} 271 272/* 273 * SecCmsEncryptedDataDecodeAfterData - finish decrypting this encryptedData's content 274 */ 275OSStatus 276SecCmsEncryptedDataDecodeAfterData(SecCmsEncryptedDataRef encd) 277{ 278 if (encd->contentInfo.ciphcx) { 279 SecCmsCipherContextDestroy(encd->contentInfo.ciphcx); 280 encd->contentInfo.ciphcx = NULL; 281 } 282 283 return SECSuccess; 284} 285 286/* 287 * SecCmsEncryptedDataDecodeAfterEnd - finish decoding this encryptedData 288 */ 289OSStatus 290SecCmsEncryptedDataDecodeAfterEnd(SecCmsEncryptedDataRef encd) 291{ 292 /* apply final touches */ 293 return SECSuccess; 294} 295