~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

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