1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/smime/cmsenvdata.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,397 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * CMS envelopedData methods. 1.10 + */ 1.11 + 1.12 +#include "cmslocal.h" 1.13 + 1.14 +#include "cert.h" 1.15 +#include "key.h" 1.16 +#include "secasn1.h" 1.17 +#include "secitem.h" 1.18 +#include "secoid.h" 1.19 +#include "pk11func.h" 1.20 +#include "secerr.h" 1.21 +#include "secpkcs5.h" 1.22 + 1.23 +/* 1.24 + * NSS_CMSEnvelopedData_Create - create an enveloped data message 1.25 + */ 1.26 +NSSCMSEnvelopedData * 1.27 +NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize) 1.28 +{ 1.29 + void *mark; 1.30 + NSSCMSEnvelopedData *envd; 1.31 + PLArenaPool *poolp; 1.32 + SECStatus rv; 1.33 + 1.34 + poolp = cmsg->poolp; 1.35 + 1.36 + mark = PORT_ArenaMark(poolp); 1.37 + 1.38 + envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData)); 1.39 + if (envd == NULL) 1.40 + goto loser; 1.41 + 1.42 + envd->cmsg = cmsg; 1.43 + 1.44 + /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */ 1.45 + 1.46 + rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize); 1.47 + if (rv != SECSuccess) 1.48 + goto loser; 1.49 + 1.50 + PORT_ArenaUnmark(poolp, mark); 1.51 + return envd; 1.52 + 1.53 +loser: 1.54 + PORT_ArenaRelease(poolp, mark); 1.55 + return NULL; 1.56 +} 1.57 + 1.58 +/* 1.59 + * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message 1.60 + */ 1.61 +void 1.62 +NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp) 1.63 +{ 1.64 + NSSCMSRecipientInfo **recipientinfos; 1.65 + NSSCMSRecipientInfo *ri; 1.66 + 1.67 + if (edp == NULL) 1.68 + return; 1.69 + 1.70 + recipientinfos = edp->recipientInfos; 1.71 + if (recipientinfos == NULL) 1.72 + return; 1.73 + 1.74 + while ((ri = *recipientinfos++) != NULL) 1.75 + NSS_CMSRecipientInfo_Destroy(ri); 1.76 + 1.77 + NSS_CMSContentInfo_Destroy(&(edp->contentInfo)); 1.78 + 1.79 +} 1.80 + 1.81 +/* 1.82 + * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo 1.83 + */ 1.84 +NSSCMSContentInfo * 1.85 +NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd) 1.86 +{ 1.87 + return &(envd->contentInfo); 1.88 +} 1.89 + 1.90 +/* 1.91 + * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg 1.92 + * 1.93 + * rip must be created on the same pool as edp - this is not enforced, though. 1.94 + */ 1.95 +SECStatus 1.96 +NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip) 1.97 +{ 1.98 + void *mark; 1.99 + SECStatus rv; 1.100 + 1.101 + /* XXX compare pools, if not same, copy rip into edp's pool */ 1.102 + 1.103 + PR_ASSERT(edp != NULL); 1.104 + PR_ASSERT(rip != NULL); 1.105 + 1.106 + mark = PORT_ArenaMark(edp->cmsg->poolp); 1.107 + 1.108 + rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip); 1.109 + if (rv != SECSuccess) { 1.110 + PORT_ArenaRelease(edp->cmsg->poolp, mark); 1.111 + return SECFailure; 1.112 + } 1.113 + 1.114 + PORT_ArenaUnmark (edp->cmsg->poolp, mark); 1.115 + return SECSuccess; 1.116 +} 1.117 + 1.118 +/* 1.119 + * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding 1.120 + * 1.121 + * at this point, we need 1.122 + * - recipientinfos set up with recipient's certificates 1.123 + * - a content encryption algorithm (if none, 3DES will be used) 1.124 + * 1.125 + * this function will generate a random content encryption key (aka bulk key), 1.126 + * initialize the recipientinfos with certificate identification and wrap the bulk key 1.127 + * using the proper algorithm for every certificiate. 1.128 + * it will finally set the bulk algorithm and key so that the encode step can find it. 1.129 + */ 1.130 +SECStatus 1.131 +NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd) 1.132 +{ 1.133 + int version; 1.134 + NSSCMSRecipientInfo **recipientinfos; 1.135 + NSSCMSContentInfo *cinfo; 1.136 + PK11SymKey *bulkkey = NULL; 1.137 + SECOidTag bulkalgtag; 1.138 + CK_MECHANISM_TYPE type; 1.139 + PK11SlotInfo *slot; 1.140 + SECStatus rv; 1.141 + SECItem *dummy; 1.142 + PLArenaPool *poolp; 1.143 + extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; 1.144 + void *mark = NULL; 1.145 + int i; 1.146 + 1.147 + poolp = envd->cmsg->poolp; 1.148 + cinfo = &(envd->contentInfo); 1.149 + 1.150 + recipientinfos = envd->recipientInfos; 1.151 + if (recipientinfos == NULL) { 1.152 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.153 +#if 0 1.154 + PORT_SetErrorString("Cannot find recipientinfos to encode."); 1.155 +#endif 1.156 + goto loser; 1.157 + } 1.158 + 1.159 + version = NSS_CMS_ENVELOPED_DATA_VERSION_REG; 1.160 + if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) { 1.161 + version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; 1.162 + } else { 1.163 + for (i = 0; recipientinfos[i] != NULL; i++) { 1.164 + if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) { 1.165 + version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; 1.166 + break; 1.167 + } 1.168 + } 1.169 + } 1.170 + dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version); 1.171 + if (dummy == NULL) 1.172 + goto loser; 1.173 + 1.174 + /* now we need to have a proper content encryption algorithm 1.175 + * on the SMIME level, we would figure one out by looking at SMIME capabilities 1.176 + * we cannot do that on our level, so if none is set already, we'll just go 1.177 + * with one of the mandatory algorithms (3DES) */ 1.178 + if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) { 1.179 + rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168); 1.180 + if (rv != SECSuccess) 1.181 + goto loser; 1.182 + bulkalgtag = SEC_OID_DES_EDE3_CBC; 1.183 + } 1.184 + 1.185 + /* generate a random bulk key suitable for content encryption alg */ 1.186 + type = PK11_AlgtagToMechanism(bulkalgtag); 1.187 + slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg); 1.188 + if (slot == NULL) 1.189 + goto loser; /* error has been set by PK11_GetBestSlot */ 1.190 + 1.191 + /* this is expensive... */ 1.192 + bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg); 1.193 + PK11_FreeSlot(slot); 1.194 + if (bulkkey == NULL) 1.195 + goto loser; /* error has been set by PK11_KeyGen */ 1.196 + 1.197 + mark = PORT_ArenaMark(poolp); 1.198 + 1.199 + /* Encrypt the bulk key with the public key of each recipient. */ 1.200 + for (i = 0; recipientinfos[i] != NULL; i++) { 1.201 + rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag); 1.202 + if (rv != SECSuccess) 1.203 + goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */ 1.204 + /* could be: alg not supported etc. */ 1.205 + } 1.206 + 1.207 + /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */ 1.208 + rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL); 1.209 + if (rv != SECSuccess) 1.210 + goto loser; /* error has been set by NSS_CMSArray_SortByDER */ 1.211 + 1.212 + /* store the bulk key in the contentInfo so that the encoder can find it */ 1.213 + NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); 1.214 + 1.215 + PORT_ArenaUnmark(poolp, mark); 1.216 + 1.217 + PK11_FreeSymKey(bulkkey); 1.218 + 1.219 + return SECSuccess; 1.220 + 1.221 +loser: 1.222 + if (mark != NULL) 1.223 + PORT_ArenaRelease (poolp, mark); 1.224 + if (bulkkey) 1.225 + PK11_FreeSymKey(bulkkey); 1.226 + 1.227 + return SECFailure; 1.228 +} 1.229 + 1.230 +/* 1.231 + * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption 1.232 + * 1.233 + * it is essential that this is called before the contentEncAlg is encoded, because 1.234 + * setting up the encryption may generate IVs and thus change it! 1.235 + */ 1.236 +SECStatus 1.237 +NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd) 1.238 +{ 1.239 + NSSCMSContentInfo *cinfo; 1.240 + PK11SymKey *bulkkey; 1.241 + SECAlgorithmID *algid; 1.242 + SECStatus rv; 1.243 + 1.244 + cinfo = &(envd->contentInfo); 1.245 + 1.246 + /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */ 1.247 + bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); 1.248 + if (bulkkey == NULL) 1.249 + return SECFailure; 1.250 + algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); 1.251 + if (algid == NULL) 1.252 + return SECFailure; 1.253 + 1.254 + rv = NSS_CMSContentInfo_Private_Init(cinfo); 1.255 + if (rv != SECSuccess) { 1.256 + return SECFailure; 1.257 + } 1.258 + /* this may modify algid (with IVs generated in a token). 1.259 + * it is essential that algid is a pointer to the contentEncAlg data, not a 1.260 + * pointer to a copy! */ 1.261 + cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid); 1.262 + PK11_FreeSymKey(bulkkey); 1.263 + if (cinfo->privateInfo->ciphcx == NULL) 1.264 + return SECFailure; 1.265 + 1.266 + return SECSuccess; 1.267 +} 1.268 + 1.269 +/* 1.270 + * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding 1.271 + */ 1.272 +SECStatus 1.273 +NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd) 1.274 +{ 1.275 + if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { 1.276 + NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); 1.277 + envd->contentInfo.privateInfo->ciphcx = NULL; 1.278 + } 1.279 + 1.280 + /* nothing else to do after data */ 1.281 + return SECSuccess; 1.282 +} 1.283 + 1.284 +/* 1.285 + * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, 1.286 + * derive bulk key & set up our contentinfo 1.287 + */ 1.288 +SECStatus 1.289 +NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd) 1.290 +{ 1.291 + NSSCMSRecipientInfo *ri; 1.292 + PK11SymKey *bulkkey = NULL; 1.293 + SECOidTag bulkalgtag; 1.294 + SECAlgorithmID *bulkalg; 1.295 + SECStatus rv = SECFailure; 1.296 + NSSCMSContentInfo *cinfo; 1.297 + NSSCMSRecipient **recipient_list = NULL; 1.298 + NSSCMSRecipient *recipient; 1.299 + int rlIndex; 1.300 + 1.301 + if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) { 1.302 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.303 +#if 0 1.304 + PORT_SetErrorString("No recipient data in envelope."); 1.305 +#endif 1.306 + goto loser; 1.307 + } 1.308 + 1.309 + /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */ 1.310 + /* get the cert and private key for it right away */ 1.311 + recipient_list = nss_cms_recipient_list_create(envd->recipientInfos); 1.312 + if (recipient_list == NULL) 1.313 + goto loser; 1.314 + 1.315 + /* what about multiple recipientInfos that match? 1.316 + * especially if, for some reason, we could not produce a bulk key with the first match?! 1.317 + * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList... 1.318 + * maybe later... */ 1.319 + rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg); 1.320 + 1.321 + /* if that fails, then we're not an intended recipient and cannot decrypt */ 1.322 + if (rlIndex < 0) { 1.323 + PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT); 1.324 +#if 0 1.325 + PORT_SetErrorString("Cannot decrypt data because proper key cannot be found."); 1.326 +#endif 1.327 + goto loser; 1.328 + } 1.329 + 1.330 + recipient = recipient_list[rlIndex]; 1.331 + if (!recipient->cert || !recipient->privkey) { 1.332 + /* XXX should set an error code ?!? */ 1.333 + goto loser; 1.334 + } 1.335 + /* get a pointer to "our" recipientinfo */ 1.336 + ri = envd->recipientInfos[recipient->riIndex]; 1.337 + 1.338 + cinfo = &(envd->contentInfo); 1.339 + bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); 1.340 + if (bulkalgtag == SEC_OID_UNKNOWN) { 1.341 + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 1.342 + } else 1.343 + bulkkey = 1.344 + NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex, 1.345 + recipient->cert, 1.346 + recipient->privkey, 1.347 + bulkalgtag); 1.348 + if (bulkkey == NULL) { 1.349 + /* no success finding a bulk key */ 1.350 + goto loser; 1.351 + } 1.352 + 1.353 + NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); 1.354 + 1.355 + bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); 1.356 + 1.357 + rv = NSS_CMSContentInfo_Private_Init(cinfo); 1.358 + if (rv != SECSuccess) { 1.359 + goto loser; 1.360 + } 1.361 + rv = SECFailure; 1.362 + cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); 1.363 + if (cinfo->privateInfo->ciphcx == NULL) 1.364 + goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ 1.365 + 1.366 + 1.367 + rv = SECSuccess; 1.368 + 1.369 +loser: 1.370 + if (bulkkey) 1.371 + PK11_FreeSymKey(bulkkey); 1.372 + if (recipient_list != NULL) 1.373 + nss_cms_recipient_list_destroy(recipient_list); 1.374 + return rv; 1.375 +} 1.376 + 1.377 +/* 1.378 + * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content 1.379 + */ 1.380 +SECStatus 1.381 +NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd) 1.382 +{ 1.383 + if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { 1.384 + NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); 1.385 + envd->contentInfo.privateInfo->ciphcx = NULL; 1.386 + } 1.387 + 1.388 + return SECSuccess; 1.389 +} 1.390 + 1.391 +/* 1.392 + * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData 1.393 + */ 1.394 +SECStatus 1.395 +NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd) 1.396 +{ 1.397 + /* apply final touches */ 1.398 + return SECSuccess; 1.399 +} 1.400 +