security/nss/lib/smime/cmsenvdata.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial