tor-browser

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

cmsencode.c (28357B)


      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 encoding.
      7 */
      8 
      9 #include "cmslocal.h"
     10 
     11 #include "cert.h"
     12 #include "keyhi.h"
     13 #include "secasn1.h"
     14 #include "secoid.h"
     15 #include "secitem.h"
     16 #include "pk11func.h"
     17 #include "secerr.h"
     18 
     19 struct nss_cms_encoder_output {
     20    NSSCMSContentCallback outputfn;
     21    void *outputarg;
     22    PLArenaPool *destpoolp;
     23    SECItem *dest;
     24 };
     25 
     26 struct NSSCMSEncoderContextStr {
     27    SEC_ASN1EncoderContext *ecx;          /* ASN.1 encoder context */
     28    PRBool ecxupdated;                    /* true if data was handed in */
     29    NSSCMSMessage *cmsg;                  /* pointer to the root message */
     30    SECOidTag type;                       /* type tag of the current content */
     31    NSSCMSContent content;                /* pointer to current content */
     32    struct nss_cms_encoder_output output; /* output function */
     33    int error;                            /* error code */
     34    NSSCMSEncoderContext *childp7ecx;     /* link to child encoder context */
     35 };
     36 
     37 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
     38 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
     39 static void nss_cms_encoder_update(void *p7ecx,
     40                                   const char *data, unsigned long len);
     41 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
     42                                           const unsigned char *data, unsigned long len,
     43                                           PRBool final, PRBool innermost);
     44 
     45 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
     46 
     47 /*
     48 * The little output function that the ASN.1 encoder calls to hand
     49 * us bytes which we in turn hand back to our caller (via the callback
     50 * they gave us).
     51 */
     52 static void
     53 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
     54                    int depth, SEC_ASN1EncodingPart data_kind)
     55 {
     56    struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
     57    unsigned char *dest;
     58    unsigned long offset;
     59 
     60 #ifdef CMSDEBUG
     61    int i;
     62    const char *data_name = "unknown";
     63 
     64    switch (data_kind) {
     65        case SEC_ASN1_Identifier:
     66            data_name = "identifier";
     67            break;
     68        case SEC_ASN1_Length:
     69            data_name = "length";
     70            break;
     71        case SEC_ASN1_Contents:
     72            data_name = "contents";
     73            break;
     74        case SEC_ASN1_EndOfContents:
     75            data_name = "end-of-contents";
     76            break;
     77    }
     78    fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len);
     79    for (i = 0; i < len; i++) {
     80        fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
     81    }
     82    if ((i % 16) != 0)
     83        fprintf(stderr, "\n");
     84 #endif
     85 
     86    if (output->outputfn != NULL)
     87        /* call output callback with DER data */
     88        output->outputfn(output->outputarg, buf, len);
     89 
     90    if (output->dest != NULL) {
     91        /* store DER data in SECItem */
     92        offset = output->dest->len;
     93        if (offset == 0) {
     94            dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
     95        } else {
     96            dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
     97                                                   output->dest->data,
     98                                                   output->dest->len,
     99                                                   output->dest->len + len);
    100        }
    101        if (dest == NULL)
    102            /* oops */
    103            return;
    104 
    105        output->dest->data = dest;
    106        output->dest->len += len;
    107 
    108        /* copy it in */
    109        if (len) {
    110            PORT_Memcpy(output->dest->data + offset, buf, len);
    111        }
    112    }
    113 }
    114 
    115 /*
    116 * nss_cms_encoder_notify - ASN.1 encoder callback
    117 *
    118 * this function is called by the ASN.1 encoder before and after the encoding of
    119 * every object. here, it is used to keep track of data structures, set up
    120 * encryption and/or digesting and possibly set up child encoders.
    121 */
    122 static void
    123 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
    124 {
    125    NSSCMSEncoderContext *p7ecx;
    126    NSSCMSContentInfo *rootcinfo, *cinfo;
    127    PRBool after = !before;
    128    SECOidTag childtype;
    129    SECItem *item;
    130 
    131    p7ecx = (NSSCMSEncoderContext *)arg;
    132    PORT_Assert(p7ecx != NULL);
    133 
    134    rootcinfo = &(p7ecx->cmsg->contentInfo);
    135 
    136 #ifdef CMSDEBUG
    137    fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after",
    138            dest, depth);
    139 #endif
    140 
    141    /*
    142     * Watch for the content field, at which point we want to instruct
    143     * the ASN.1 encoder to start taking bytes from the buffer.
    144     */
    145    if (NSS_CMSType_IsData(p7ecx->type)) {
    146        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    147        if (before && dest == &(cinfo->rawContent)) {
    148            /* just set up encoder to grab from user - no encryption or digesting */
    149            if ((item = cinfo->content.data) != NULL)
    150                (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data,
    151                                                item->len, PR_TRUE, PR_TRUE);
    152            else
    153                SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
    154            SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
    155        }
    156    } else if (NSS_CMSType_IsWrapper(p7ecx->type)) {
    157        /* when we know what the content is, we encode happily until we reach the inner content */
    158        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    159        childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
    160 
    161        if (after && dest == &(cinfo->contentType)) {
    162            /* we're right before encoding the data (if we have some or not) */
    163            /* (for encrypted data, we're right before the contentEncAlg which may change */
    164            /*  in nss_cms_before_data because of IV calculation when setting up encryption) */
    165            if (nss_cms_before_data(p7ecx) != SECSuccess)
    166                p7ecx->error = PORT_GetError();
    167        }
    168        if (before && dest == &(cinfo->rawContent)) {
    169            if (p7ecx->childp7ecx == NULL) {
    170                if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) {
    171                    /* we are the innermost non-data and we have data - feed it in */
    172                    (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data,
    173                                                    item->len, PR_TRUE, PR_TRUE);
    174                } else {
    175                    /* else we'll have to get data from user */
    176                    SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
    177                }
    178            } else {
    179                /* if we have a nested encoder, wait for its data */
    180                SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
    181            }
    182        }
    183        if (after && dest == &(cinfo->rawContent)) {
    184            if (nss_cms_after_data(p7ecx) != SECSuccess)
    185                p7ecx->error = PORT_GetError();
    186            SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
    187        }
    188    } else {
    189        /* we're still in the root message */
    190        if (after && dest == &(rootcinfo->contentType)) {
    191            /* got the content type OID now - so find out the type tag */
    192            p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
    193            /* set up a pointer to our current content */
    194            p7ecx->content = rootcinfo->content;
    195        }
    196    }
    197 }
    198 
    199 /*
    200 * nss_cms_before_data - setup the current encoder to receive data
    201 */
    202 static SECStatus
    203 nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
    204 {
    205    SECStatus rv;
    206    SECOidTag childtype;
    207    NSSCMSContentInfo *cinfo;
    208    NSSCMSEncoderContext *childp7ecx;
    209    const SEC_ASN1Template *template;
    210 
    211    /* call _Encode_BeforeData handlers */
    212    switch (p7ecx->type) {
    213        case SEC_OID_PKCS7_SIGNED_DATA:
    214            /* we're encoding a signedData, so set up the digests */
    215            rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
    216            break;
    217        case SEC_OID_PKCS7_DIGESTED_DATA:
    218            /* we're encoding a digestedData, so set up the digest */
    219            rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
    220            break;
    221        case SEC_OID_PKCS7_ENVELOPED_DATA:
    222            rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
    223            break;
    224        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    225            rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
    226            break;
    227        default:
    228            if (NSS_CMSType_IsWrapper(p7ecx->type)) {
    229                rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type,
    230                                                                 p7ecx->content.genericData);
    231            } else {
    232                rv = SECFailure;
    233            }
    234    }
    235    if (rv != SECSuccess)
    236        return SECFailure;
    237 
    238    /* ok, now we have a pointer to cinfo */
    239    /* find out what kind of data is encapsulated */
    240 
    241    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    242    childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
    243 
    244    if (NSS_CMSType_IsWrapper(childtype)) {
    245        /* in these cases, we need to set up a child encoder! */
    246        /* create new encoder context */
    247        childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
    248        if (childp7ecx == NULL)
    249            return SECFailure;
    250 
    251        /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
    252         * (which will encrypt and/or digest it)
    253         * this needs to route back into our update function
    254         * which finds the lowest encoding context & encrypts and computes digests */
    255        childp7ecx->type = childtype;
    256        childp7ecx->content = cinfo->content;
    257        /* use the non-recursive update function here, of course */
    258        childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
    259        childp7ecx->output.outputarg = p7ecx;
    260        childp7ecx->output.destpoolp = NULL;
    261        childp7ecx->output.dest = NULL;
    262        childp7ecx->cmsg = p7ecx->cmsg;
    263        childp7ecx->ecxupdated = PR_FALSE;
    264        childp7ecx->childp7ecx = NULL;
    265 
    266        template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
    267        if (template == NULL)
    268            goto loser; /* cannot happen */
    269 
    270        /* now initialize the data for encoding the first third */
    271        switch (childp7ecx->type) {
    272            case SEC_OID_PKCS7_SIGNED_DATA:
    273                rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
    274                break;
    275            case SEC_OID_PKCS7_ENVELOPED_DATA:
    276                rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
    277                break;
    278            case SEC_OID_PKCS7_DIGESTED_DATA:
    279                rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
    280                break;
    281            case SEC_OID_PKCS7_ENCRYPTED_DATA:
    282                rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
    283                break;
    284            default:
    285                rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type,
    286                                                                  cinfo->content.genericData);
    287                break;
    288        }
    289        if (rv != SECSuccess)
    290            goto loser;
    291 
    292        /*
    293         * Initialize the BER encoder.
    294         */
    295        childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
    296                                               nss_cms_encoder_out, &(childp7ecx->output));
    297        if (childp7ecx->ecx == NULL)
    298            goto loser;
    299 
    300        /*
    301         * Indicate that we are streaming.  We will be streaming until we
    302         * get past the contents bytes.
    303         */
    304        if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
    305            SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
    306 
    307        /*
    308         * The notify function will watch for the contents field.
    309         */
    310        p7ecx->childp7ecx = childp7ecx;
    311        SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify,
    312                                     childp7ecx);
    313 
    314        /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
    315        /* encoding process - we'll do that from the update function instead */
    316        /* otherwise we'd be encoding data from a call of the notify function of the */
    317        /* parent encoder (which would not work) */
    318 
    319    } else if (NSS_CMSType_IsData(childtype)) {
    320        p7ecx->childp7ecx = NULL;
    321    } else {
    322        /* we do not know this type */
    323        p7ecx->error = SEC_ERROR_BAD_DER;
    324    }
    325 
    326    return SECSuccess;
    327 
    328 loser:
    329    if (childp7ecx) {
    330        if (childp7ecx->ecx)
    331            SEC_ASN1EncoderFinish(childp7ecx->ecx);
    332        PORT_Free(childp7ecx);
    333        p7ecx->childp7ecx = NULL;
    334    }
    335    return SECFailure;
    336 }
    337 
    338 static SECStatus
    339 nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
    340 {
    341    SECStatus rv = SECFailure;
    342 
    343    switch (p7ecx->type) {
    344        case SEC_OID_PKCS7_SIGNED_DATA:
    345            /* this will finish the digests and sign */
    346            rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
    347            break;
    348        case SEC_OID_PKCS7_ENVELOPED_DATA:
    349            rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
    350            break;
    351        case SEC_OID_PKCS7_DIGESTED_DATA:
    352            rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
    353            break;
    354        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    355            rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
    356            break;
    357        default:
    358            if (NSS_CMSType_IsWrapper(p7ecx->type)) {
    359                rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type,
    360                                                                p7ecx->content.genericData);
    361            } else {
    362                rv = SECFailure;
    363            }
    364            break;
    365    }
    366    return rv;
    367 }
    368 
    369 /*
    370 * nss_cms_encoder_work_data - process incoming data
    371 *
    372 * (from the user or the next encoding layer)
    373 * Here, we need to digest and/or encrypt, then pass it on
    374 */
    375 static SECStatus
    376 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
    377                          const unsigned char *data, unsigned long len,
    378                          PRBool final, PRBool innermost)
    379 {
    380    unsigned char *buf = NULL;
    381    SECStatus rv;
    382    NSSCMSContentInfo *cinfo;
    383 
    384    rv = SECSuccess; /* may as well be optimistic */
    385 
    386    /*
    387     * We should really have data to process, or we should be trying
    388     * to finish/flush the last block.  (This is an overly paranoid
    389     * check since all callers are in this file and simple inspection
    390     * proves they do it right.  But it could find a bug in future
    391     * modifications/development, that is why it is here.)
    392     */
    393    PORT_Assert((data != NULL && len) || final);
    394 
    395    /* we got data (either from the caller, or from a lower level encoder) */
    396    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    397    if (!cinfo) {
    398        /* The original programmer didn't expect this to happen */
    399        p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
    400        return SECFailure;
    401    }
    402 
    403    /* Update the running digest. */
    404    if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL)
    405        NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
    406 
    407    /* Encrypt this chunk. */
    408    if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
    409        unsigned int inlen;  /* length of data being encrypted */
    410        unsigned int outlen; /* length of encrypted data */
    411        unsigned int buflen; /* length available for encrypted data */
    412 
    413        inlen = len;
    414        buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx,
    415                                                    inlen, final);
    416        if (buflen == 0) {
    417            /*
    418             * No output is expected, but the input data may be buffered
    419             * so we still have to call Encrypt.
    420             */
    421            rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0,
    422                                              data, inlen, final);
    423            if (final) {
    424                len = 0;
    425                goto done;
    426            }
    427            return rv;
    428        }
    429 
    430        if (dest != NULL)
    431            buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
    432        else
    433            buf = (unsigned char *)PORT_Alloc(buflen);
    434 
    435        if (buf == NULL) {
    436            rv = SECFailure;
    437        } else {
    438            rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf,
    439                                              &outlen, buflen,
    440                                              data, inlen, final);
    441            data = buf;
    442            len = outlen;
    443        }
    444        if (rv != SECSuccess)
    445            /* encryption or malloc failed? */
    446            return rv;
    447    }
    448 
    449    /*
    450     * at this point (data,len) has everything we'd like to give to the CURRENT encoder
    451     * (which will encode it, then hand it back to the user or the parent encoder)
    452     * We don't encode the data if we're innermost and we're told not to include the data
    453     */
    454    if (p7ecx->ecx != NULL && len &&
    455        (!innermost || cinfo->rawContent != cinfo->content.pointer))
    456        rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
    457 
    458 done:
    459 
    460    if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
    461        if (dest != NULL) {
    462            dest->data = buf;
    463            dest->len = len;
    464        } else if (buf != NULL) {
    465            PORT_Free(buf);
    466        }
    467    }
    468    return rv;
    469 }
    470 
    471 /*
    472 * nss_cms_encoder_update - deliver encoded data to the next higher level
    473 *
    474 * no recursion here because we REALLY want to end up at the next higher encoder!
    475 */
    476 static void
    477 nss_cms_encoder_update(void *p7ecx, const char *data, unsigned long len)
    478 {
    479    /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
    480    (void)nss_cms_encoder_work_data((NSSCMSEncoderContext *)p7ecx, NULL, (const unsigned char *)data,
    481                                    len, PR_FALSE, PR_FALSE);
    482 }
    483 
    484 /*
    485 * NSS_CMSEncoder_Start - set up encoding of a CMS message
    486 *
    487 * "cmsg" - message to encode
    488 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
    489 *                           will not be called if NULL.
    490 * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
    491 * "destpoolp" - pool to allocate DER-encoded output in
    492 * "pwfn", pwfn_arg" - callback function for getting token password
    493 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
    494 * "detached_digestalgs", "detached_digests" - digests from detached content
    495 */
    496 NSSCMSEncoderContext *
    497 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
    498                     NSSCMSContentCallback outputfn, void *outputarg,
    499                     SECItem *dest, PLArenaPool *destpoolp,
    500                     PK11PasswordFunc pwfn, void *pwfn_arg,
    501                     NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
    502                     SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
    503 {
    504    NSSCMSEncoderContext *p7ecx;
    505    SECStatus rv;
    506    NSSCMSContentInfo *cinfo;
    507    SECOidTag tag;
    508 
    509    NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
    510                                     detached_digestalgs, detached_digests);
    511 
    512    p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
    513    if (p7ecx == NULL) {
    514        PORT_SetError(SEC_ERROR_NO_MEMORY);
    515        return NULL;
    516    }
    517 
    518    p7ecx->cmsg = cmsg;
    519    p7ecx->output.outputfn = outputfn;
    520    p7ecx->output.outputarg = outputarg;
    521    p7ecx->output.dest = dest;
    522    p7ecx->output.destpoolp = destpoolp;
    523    p7ecx->type = SEC_OID_UNKNOWN;
    524 
    525    cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
    526 
    527    tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
    528    switch (tag) {
    529        case SEC_OID_PKCS7_SIGNED_DATA:
    530            rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
    531            break;
    532        case SEC_OID_PKCS7_ENVELOPED_DATA:
    533            rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
    534            break;
    535        case SEC_OID_PKCS7_DIGESTED_DATA:
    536            rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
    537            break;
    538        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    539            rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
    540            break;
    541        default:
    542            if (NSS_CMSType_IsWrapper(tag)) {
    543                rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag,
    544                                                                  p7ecx->content.genericData);
    545            } else {
    546                rv = SECFailure;
    547            }
    548            break;
    549    }
    550    if (rv != SECSuccess) {
    551        PORT_Free(p7ecx);
    552        return NULL;
    553    }
    554 
    555    /* Initialize the BER encoder.
    556     * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
    557    p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
    558                                      nss_cms_encoder_out, &(p7ecx->output));
    559    if (p7ecx->ecx == NULL) {
    560        PORT_Free(p7ecx);
    561        return NULL;
    562    }
    563    p7ecx->ecxupdated = PR_FALSE;
    564 
    565    /*
    566     * Indicate that we are streaming.  We will be streaming until we
    567     * get past the contents bytes.
    568     */
    569    if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
    570        SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
    571 
    572    /*
    573     * The notify function will watch for the contents field.
    574     */
    575    SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
    576 
    577    /* this will kick off the encoding process & encode everything up to the content bytes,
    578     * at which point the notify function sets streaming mode (and possibly creates
    579     * a child encoder). */
    580    p7ecx->ecxupdated = PR_TRUE;
    581    if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
    582        PORT_Free(p7ecx);
    583        return NULL;
    584    }
    585 
    586    return p7ecx;
    587 }
    588 
    589 /*
    590 * NSS_CMSEncoder_Update - take content data delivery from the user
    591 *
    592 * "p7ecx" - encoder context
    593 * "data" - content data
    594 * "len" - length of content data
    595 *
    596 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
    597 * then hand the data to the work_data fn
    598 */
    599 SECStatus
    600 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
    601 {
    602    SECStatus rv;
    603    NSSCMSContentInfo *cinfo;
    604    SECOidTag childtype;
    605 
    606    if (p7ecx->error)
    607        return SECFailure;
    608 
    609    /* hand data to the innermost decoder */
    610    if (p7ecx->childp7ecx) {
    611        /* tell the child to start encoding, up to its first data byte, if it
    612         * hasn't started yet */
    613        if (!p7ecx->childp7ecx->ecxupdated) {
    614            p7ecx->childp7ecx->ecxupdated = PR_TRUE;
    615            if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess)
    616                return SECFailure;
    617        }
    618        /* recursion here */
    619        rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
    620    } else {
    621        /* we are at innermost decoder */
    622        /* find out about our inner content type - must be data */
    623        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    624        if (!cinfo) {
    625            /* The original programmer didn't expect this to happen */
    626            p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
    627            return SECFailure;
    628        }
    629 
    630        childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
    631        if (!NSS_CMSType_IsData(childtype))
    632            return SECFailure;
    633        /* and we must not have preset data */
    634        if (cinfo->content.data != NULL)
    635            return SECFailure;
    636 
    637        /*  hand it the data so it can encode it (let DER trickle up the chain) */
    638        rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data,
    639                                       len, PR_FALSE, PR_TRUE);
    640    }
    641    return rv;
    642 }
    643 
    644 /*
    645 * NSS_CMSEncoder_Cancel - stop all encoding
    646 *
    647 * we need to walk down the chain of encoders and the finish them from the innermost out
    648 */
    649 SECStatus
    650 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
    651 {
    652    SECStatus rv = SECFailure;
    653 
    654    /* XXX do this right! */
    655 
    656    /*
    657     * Finish any inner decoders before us so that all the encoded data is flushed
    658     * This basically finishes all the decoders from the innermost to the outermost.
    659     * Finishing an inner decoder may result in data being updated to the outer decoder
    660     * while we are already in NSS_CMSEncoder_Finish, but that's allright.
    661     */
    662    if (p7ecx->childp7ecx) {
    663        rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
    664                                                       /* remember rv for now */
    665 #ifdef CMSDEBUG
    666        if (rv != SECSuccess) {
    667            fprintf(stderr, "Fail to cancel inner encoder\n");
    668        }
    669 #endif
    670    }
    671 
    672    /*
    673     * On the way back up, there will be no more data (if we had an
    674     * inner encoder, it is done now!)
    675     * Flush out any remaining data and/or finish digests.
    676     */
    677    rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
    678    if (rv != SECSuccess)
    679        goto loser;
    680 
    681    p7ecx->childp7ecx = NULL;
    682 
    683    /* kick the encoder back into working mode again.
    684     * We turn off streaming stuff (which will cause the encoder to continue
    685     * encoding happily, now that we have all the data (like digests) ready for it).
    686     */
    687    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
    688    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
    689 
    690    /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
    691    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
    692 
    693 loser:
    694    SEC_ASN1EncoderFinish(p7ecx->ecx);
    695    PORT_Free(p7ecx);
    696    return rv;
    697 }
    698 
    699 /*
    700 * NSS_CMSEncoder_Finish - signal the end of data
    701 *
    702 * we need to walk down the chain of encoders and the finish them from the innermost out
    703 */
    704 SECStatus
    705 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
    706 {
    707    SECStatus rv = SECFailure;
    708    NSSCMSContentInfo *cinfo;
    709 
    710    /*
    711     * Finish any inner decoders before us so that all the encoded data is flushed
    712     * This basically finishes all the decoders from the innermost to the outermost.
    713     * Finishing an inner decoder may result in data being updated to the outer decoder
    714     * while we are already in NSS_CMSEncoder_Finish, but that's allright.
    715     */
    716    if (p7ecx->childp7ecx) {
    717        /* tell the child to start encoding, up to its first data byte, if it
    718         * hasn't yet */
    719        if (!p7ecx->childp7ecx->ecxupdated) {
    720            p7ecx->childp7ecx->ecxupdated = PR_TRUE;
    721            rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0);
    722            if (rv != SECSuccess) {
    723                NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
    724                goto loser;
    725            }
    726        }
    727        rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
    728        if (rv != SECSuccess)
    729            goto loser;
    730    }
    731 
    732    /*
    733     * On the way back up, there will be no more data (if we had an
    734     * inner encoder, it is done now!)
    735     * Flush out any remaining data and/or finish digests.
    736     */
    737    rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
    738    if (rv != SECSuccess)
    739        goto loser;
    740 
    741    p7ecx->childp7ecx = NULL;
    742 
    743    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
    744    if (!cinfo) {
    745        /* The original programmer didn't expect this to happen */
    746        p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
    747        rv = SECFailure;
    748        goto loser;
    749    }
    750    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
    751    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
    752    /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
    753    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
    754 
    755    if (p7ecx->error)
    756        rv = SECFailure;
    757 
    758 loser:
    759    SEC_ASN1EncoderFinish(p7ecx->ecx);
    760    PORT_Free(p7ecx);
    761    return rv;
    762 }