~ubuntu-branches/ubuntu/quantal/nss/quantal-updates

« back to all changes in this revision

Viewing changes to nss/lib/smime/cmsencode.c

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2013-11-14 14:58:07 UTC
  • mfrom: (1.1.19)
  • Revision ID: package-import@ubuntu.com-20131114145807-vj6v4erz8xj6kwz3
Tags: 3.15.3-0ubuntu0.12.10.1
* SECURITY UPDATE: New upstream release to fix multiple security issues
  and add TLSv1.2 support.
  - CVE-2013-1739
  - CVE-2013-1741
  - CVE-2013-5605
  - CVE-2013-5606
* Adjusted packaging for 3.15.3:
  - debian/patches/*: refreshed.
  - debian/patches/lower-dhe-priority.patch: removed, no longer needed,
    was a workaround for an old version of firefox.
  - debian/libnss3.symbols: added new symbols.
  - debian/rules: updated for new source layout.

Show diffs side-by-side

added added

removed removed

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