tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }