cmsenvdata.c (12334B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * CMS envelopedData methods. 7 */ 8 9 #include "cmslocal.h" 10 11 #include "cert.h" 12 #include "keyhi.h" 13 #include "secasn1.h" 14 #include "secitem.h" 15 #include "secoid.h" 16 #include "pk11func.h" 17 #include "secerr.h" 18 #include "secpkcs5.h" 19 20 /* 21 * NSS_CMSEnvelopedData_Create - create an enveloped data message 22 */ 23 NSSCMSEnvelopedData * 24 NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize) 25 { 26 void *mark; 27 NSSCMSEnvelopedData *envd; 28 PLArenaPool *poolp; 29 SECStatus rv; 30 31 poolp = cmsg->poolp; 32 33 mark = PORT_ArenaMark(poolp); 34 35 envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData)); 36 if (envd == NULL) 37 goto loser; 38 39 envd->cmsg = cmsg; 40 41 /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */ 42 43 rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), 44 algorithm, NULL, keysize); 45 if (rv != SECSuccess) 46 goto loser; 47 48 PORT_ArenaUnmark(poolp, mark); 49 return envd; 50 51 loser: 52 PORT_ArenaRelease(poolp, mark); 53 return NULL; 54 } 55 56 /* 57 * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message 58 */ 59 void 60 NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp) 61 { 62 NSSCMSRecipientInfo **recipientinfos; 63 NSSCMSRecipientInfo *ri; 64 65 if (edp == NULL) 66 return; 67 68 recipientinfos = edp->recipientInfos; 69 while (recipientinfos && (ri = *recipientinfos++) != NULL) 70 NSS_CMSRecipientInfo_Destroy(ri); 71 72 NSS_CMSContentInfo_Destroy(&(edp->contentInfo)); 73 } 74 75 /* 76 * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo 77 */ 78 NSSCMSContentInfo * 79 NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd) 80 { 81 return &(envd->contentInfo); 82 } 83 84 /* 85 * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg 86 * 87 * rip must be created on the same pool as edp - this is not enforced, though. 88 */ 89 SECStatus 90 NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip) 91 { 92 void *mark; 93 SECStatus rv; 94 95 /* XXX compare pools, if not same, copy rip into edp's pool */ 96 97 PR_ASSERT(edp != NULL); 98 PR_ASSERT(rip != NULL); 99 100 mark = PORT_ArenaMark(edp->cmsg->poolp); 101 102 rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip); 103 if (rv != SECSuccess) { 104 PORT_ArenaRelease(edp->cmsg->poolp, mark); 105 return SECFailure; 106 } 107 108 PORT_ArenaUnmark(edp->cmsg->poolp, mark); 109 return SECSuccess; 110 } 111 112 /* 113 * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding 114 * 115 * at this point, we need 116 * - recipientinfos set up with recipient's certificates 117 * - a content encryption algorithm (if none, 3DES will be used) 118 * 119 * this function will generate a random content encryption key (aka bulk key), 120 * initialize the recipientinfos with certificate identification and wrap the bulk key 121 * using the proper algorithm for every certificiate. 122 * it will finally set the bulk algorithm and key so that the encode step can find it. 123 */ 124 SECStatus 125 NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd) 126 { 127 int version; 128 NSSCMSRecipientInfo **recipientinfos; 129 NSSCMSContentInfo *cinfo; 130 PK11SymKey *bulkkey = NULL; 131 SECOidTag bulkalgtag; 132 CK_MECHANISM_TYPE type; 133 PK11SlotInfo *slot; 134 SECStatus rv; 135 SECItem *dummy; 136 PLArenaPool *poolp; 137 extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; 138 void *mark = NULL; 139 int i; 140 141 poolp = envd->cmsg->poolp; 142 cinfo = &(envd->contentInfo); 143 144 if (cinfo == NULL) { 145 PORT_SetError(SEC_ERROR_BAD_DATA); 146 goto loser; 147 } 148 149 recipientinfos = envd->recipientInfos; 150 if (recipientinfos == NULL) { 151 PORT_SetError(SEC_ERROR_BAD_DATA); 152 #if 0 153 PORT_SetErrorString("Cannot find recipientinfos to encode."); 154 #endif 155 goto loser; 156 } 157 158 version = NSS_CMS_ENVELOPED_DATA_VERSION_REG; 159 if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) { 160 version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; 161 } else { 162 for (i = 0; recipientinfos[i] != NULL; i++) { 163 if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) { 164 version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; 165 break; 166 } 167 } 168 } 169 dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version); 170 if (dummy == NULL) 171 goto loser; 172 173 /* now we need to have a proper content encryption algorithm 174 * on the SMIME level, we would figure one out by looking at SMIME capabilities 175 * we cannot do that on our level, so if none is set already, we'll just go 176 * with one of the mandatory algorithms (3DES) */ 177 if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) { 178 rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168); 179 if (rv != SECSuccess) 180 goto loser; 181 bulkalgtag = SEC_OID_DES_EDE3_CBC; 182 } 183 184 /* generate a random bulk key suitable for content encryption alg */ 185 type = PK11_AlgtagToMechanism(bulkalgtag); 186 slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg); 187 if (slot == NULL) 188 goto loser; /* error has been set by PK11_GetBestSlot */ 189 190 /* this is expensive... */ 191 bulkkey = PK11_KeyGen(slot, type, NULL, 192 NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, 193 envd->cmsg->pwfn_arg); 194 PK11_FreeSlot(slot); 195 if (bulkkey == NULL) 196 goto loser; /* error has been set by PK11_KeyGen */ 197 198 mark = PORT_ArenaMark(poolp); 199 200 /* Encrypt the bulk key with the public key of each recipient. */ 201 for (i = 0; recipientinfos[i] != NULL; i++) { 202 rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag); 203 if (rv != SECSuccess) 204 goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */ 205 /* could be: alg not supported etc. */ 206 } 207 208 /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */ 209 rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, 210 NSSCMSRecipientInfoTemplate, NULL); 211 if (rv != SECSuccess) 212 goto loser; /* error has been set by NSS_CMSArray_SortByDER */ 213 214 /* store the bulk key in the contentInfo so that the encoder can find it */ 215 NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); 216 217 PORT_ArenaUnmark(poolp, mark); 218 219 PK11_FreeSymKey(bulkkey); 220 221 return SECSuccess; 222 223 loser: 224 if (mark != NULL) 225 PORT_ArenaRelease(poolp, mark); 226 if (bulkkey) 227 PK11_FreeSymKey(bulkkey); 228 229 return SECFailure; 230 } 231 232 /* 233 * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption 234 * 235 * it is essential that this is called before the contentEncAlg is encoded, because 236 * setting up the encryption may generate IVs and thus change it! 237 */ 238 SECStatus 239 NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd) 240 { 241 NSSCMSContentInfo *cinfo; 242 PK11SymKey *bulkkey; 243 SECAlgorithmID *algid; 244 SECStatus rv; 245 246 cinfo = &(envd->contentInfo); 247 248 /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */ 249 bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); 250 if (bulkkey == NULL) 251 return SECFailure; 252 algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); 253 if (algid == NULL) 254 return SECFailure; 255 256 rv = NSS_CMSContentInfo_Private_Init(cinfo); 257 if (rv != SECSuccess) { 258 return SECFailure; 259 } 260 /* this may modify algid (with IVs generated in a token). 261 * it is essential that algid is a pointer to the contentEncAlg data, not a 262 * pointer to a copy! */ 263 cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid); 264 PK11_FreeSymKey(bulkkey); 265 if (cinfo->privateInfo->ciphcx == NULL) 266 return SECFailure; 267 268 return SECSuccess; 269 } 270 271 /* 272 * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding 273 */ 274 SECStatus 275 NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd) 276 { 277 if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { 278 NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); 279 envd->contentInfo.privateInfo->ciphcx = NULL; 280 } 281 282 /* nothing else to do after data */ 283 return SECSuccess; 284 } 285 286 /* 287 * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, 288 * derive bulk key & set up our contentinfo 289 */ 290 SECStatus 291 NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd) 292 { 293 NSSCMSRecipientInfo *ri; 294 PK11SymKey *bulkkey = NULL; 295 SECOidTag bulkalgtag; 296 SECAlgorithmID *bulkalg; 297 SECStatus rv = SECFailure; 298 NSSCMSContentInfo *cinfo; 299 NSSCMSRecipient **recipient_list = NULL; 300 NSSCMSRecipient *recipient; 301 int rlIndex; 302 303 if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) { 304 PORT_SetError(SEC_ERROR_BAD_DATA); 305 #if 0 306 PORT_SetErrorString("No recipient data in envelope."); 307 #endif 308 goto loser; 309 } 310 311 /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */ 312 /* get the cert and private key for it right away */ 313 recipient_list = nss_cms_recipient_list_create(envd->recipientInfos); 314 if (recipient_list == NULL) 315 goto loser; 316 317 /* what about multiple recipientInfos that match? 318 * especially if, for some reason, we could not produce a bulk key with the first match?! 319 * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList... 320 * maybe later... */ 321 rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg); 322 323 /* if that fails, then we're not an intended recipient and cannot decrypt */ 324 if (rlIndex < 0) { 325 PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT); 326 #if 0 327 PORT_SetErrorString("Cannot decrypt data because proper key cannot be found."); 328 #endif 329 goto loser; 330 } 331 332 recipient = recipient_list[rlIndex]; 333 if (!recipient->cert || !recipient->privkey) { 334 /* XXX should set an error code ?!? */ 335 goto loser; 336 } 337 /* get a pointer to "our" recipientinfo */ 338 ri = envd->recipientInfos[recipient->riIndex]; 339 340 cinfo = &(envd->contentInfo); 341 bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); 342 if (bulkalgtag == SEC_OID_UNKNOWN) { 343 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 344 } else 345 bulkkey = 346 NSS_CMSRecipientInfo_UnwrapBulkKey(ri, recipient->subIndex, 347 recipient->cert, 348 recipient->privkey, 349 bulkalgtag); 350 if (bulkkey == NULL) { 351 /* no success finding a bulk key */ 352 goto loser; 353 } 354 355 NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); 356 357 bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); 358 359 rv = NSS_CMSContentInfo_Private_Init(cinfo); 360 if (rv != SECSuccess) { 361 goto loser; 362 } 363 rv = SECFailure; 364 cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); 365 if (cinfo->privateInfo->ciphcx == NULL) 366 goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ 367 368 rv = SECSuccess; 369 370 loser: 371 if (bulkkey) 372 PK11_FreeSymKey(bulkkey); 373 if (recipient_list != NULL) 374 nss_cms_recipient_list_destroy(recipient_list); 375 return rv; 376 } 377 378 /* 379 * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content 380 */ 381 SECStatus 382 NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd) 383 { 384 if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { 385 NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); 386 envd->contentInfo.privateInfo->ciphcx = NULL; 387 } 388 389 return SECSuccess; 390 } 391 392 /* 393 * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData 394 */ 395 SECStatus 396 NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd) 397 { 398 /* apply final touches */ 399 return SECSuccess; 400 }