Coverage Report

Created: 2018-09-25 14:53

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