Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/pkcs7/p7encode.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
 * PKCS7 encoding.
7
 */
8
9
#include "p7local.h"
10
11
#include "cert.h"
12
#include "cryptohi.h"
13
#include "keyhi.h"
14
#include "secasn1.h"
15
#include "secoid.h"
16
#include "secitem.h"
17
#include "pk11func.h"
18
#include "secerr.h"
19
#include "sechash.h" /* for HASH_GetHashObject() */
20
21
struct sec_pkcs7_encoder_output {
22
    SEC_PKCS7EncoderOutputCallback outputfn;
23
    void *outputarg;
24
};
25
26
struct SEC_PKCS7EncoderContextStr {
27
    SEC_ASN1EncoderContext *ecx;
28
    SEC_PKCS7ContentInfo *cinfo;
29
    struct sec_pkcs7_encoder_output output;
30
    sec_PKCS7CipherObject *encryptobj;
31
    const SECHashObject *digestobj;
32
    void *digestcx;
33
};
34
35
/*
36
 * The little output function that the ASN.1 encoder calls to hand
37
 * us bytes which we in turn hand back to our caller (via the callback
38
 * they gave us).
39
 */
40
static void
41
sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len,
42
                      int depth, SEC_ASN1EncodingPart data_kind)
43
0
{
44
0
    struct sec_pkcs7_encoder_output *output;
45
0
46
0
    output = (struct sec_pkcs7_encoder_output *)arg;
47
0
    output->outputfn(output->outputarg, buf, len);
48
0
}
49
50
static sec_PKCS7CipherObject *
51
sec_pkcs7_encoder_start_encrypt(SEC_PKCS7ContentInfo *cinfo,
52
                                PK11SymKey *orig_bulkkey)
53
0
{
54
0
    SECOidTag kind;
55
0
    sec_PKCS7CipherObject *encryptobj;
56
0
    SEC_PKCS7RecipientInfo **recipientinfos, *ri;
57
0
    SEC_PKCS7EncryptedContentInfo *enccinfo;
58
0
    SECKEYPublicKey *publickey = NULL;
59
0
    SECKEYPrivateKey *ourPrivKey = NULL;
60
0
    PK11SymKey *bulkkey;
61
0
    void *mark;
62
0
    int i;
63
0
    PLArenaPool *arena = NULL;
64
0
65
0
    kind = SEC_PKCS7ContentType(cinfo);
66
0
    switch (kind) {
67
0
        default:
68
0
        case SEC_OID_PKCS7_DATA:
69
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
70
0
        case SEC_OID_PKCS7_SIGNED_DATA:
71
0
            recipientinfos = NULL;
72
0
            enccinfo = NULL;
73
0
            break;
74
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA: {
75
0
            SEC_PKCS7EncryptedData *encdp;
76
0
77
0
            /* To do EncryptedData we *must* be given a bulk key. */
78
0
            PORT_Assert(orig_bulkkey != NULL);
79
0
            if (orig_bulkkey == NULL) {
80
0
                /* XXX error? */
81
0
                return NULL;
82
0
            }
83
0
84
0
            encdp = cinfo->content.encryptedData;
85
0
            recipientinfos = NULL;
86
0
            enccinfo = &(encdp->encContentInfo);
87
0
        } break;
88
0
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
89
0
            SEC_PKCS7EnvelopedData *envdp;
90
0
91
0
            envdp = cinfo->content.envelopedData;
92
0
            recipientinfos = envdp->recipientInfos;
93
0
            enccinfo = &(envdp->encContentInfo);
94
0
        } break;
95
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
96
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
97
0
98
0
            saedp = cinfo->content.signedAndEnvelopedData;
99
0
            recipientinfos = saedp->recipientInfos;
100
0
            enccinfo = &(saedp->encContentInfo);
101
0
        } break;
102
0
    }
103
0
104
0
    if (enccinfo == NULL)
105
0
        return NULL;
106
0
107
0
    bulkkey = orig_bulkkey;
108
0
    if (bulkkey == NULL) {
109
0
        CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg);
110
0
        PK11SlotInfo *slot;
111
0
112
0
        slot = PK11_GetBestSlot(type, cinfo->pwfn_arg);
113
0
        if (slot == NULL) {
114
0
            return NULL;
115
0
        }
116
0
        bulkkey = PK11_KeyGen(slot, type, NULL, enccinfo->keysize / 8,
117
0
                              cinfo->pwfn_arg);
118
0
        PK11_FreeSlot(slot);
119
0
        if (bulkkey == NULL) {
120
0
            return NULL;
121
0
        }
122
0
    }
123
0
124
0
    encryptobj = NULL;
125
0
    mark = PORT_ArenaMark(cinfo->poolp);
126
0
127
0
    /*
128
0
     * Encrypt the bulk key with the public key of each recipient.
129
0
     */
130
0
    for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) {
131
0
        CERTCertificate *cert;
132
0
        SECOidTag certalgtag, encalgtag;
133
0
        SECStatus rv;
134
0
        int data_len;
135
0
        SECItem *params = NULL;
136
0
137
0
        cert = ri->cert;
138
0
        PORT_Assert(cert != NULL);
139
0
        if (cert == NULL)
140
0
            continue;
141
0
142
0
        /*
143
0
         * XXX Want an interface that takes a cert and some data and
144
0
         * fills in an algorithmID and encrypts the data with the public
145
0
         * key from the cert.  Or, give me two interfaces -- one which
146
0
         * gets the algorithm tag from a cert (I should not have to go
147
0
         * down into the subjectPublicKeyInfo myself) and another which
148
0
         * takes a public key and algorithm tag and data and encrypts
149
0
         * the data.  Or something like that.  The point is that all
150
0
         * of the following hardwired RSA stuff should be done elsewhere.
151
0
         */
152
0
153
0
        certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
154
0
155
0
        switch (certalgtag) {
156
0
            case SEC_OID_PKCS1_RSA_ENCRYPTION:
157
0
                encalgtag = certalgtag;
158
0
                publickey = CERT_ExtractPublicKey(cert);
159
0
                if (publickey == NULL)
160
0
                    goto loser;
161
0
162
0
                data_len = SECKEY_PublicKeyStrength(publickey);
163
0
                ri->encKey.data =
164
0
                    (unsigned char *)PORT_ArenaAlloc(cinfo->poolp, data_len);
165
0
                ri->encKey.len = data_len;
166
0
                if (ri->encKey.data == NULL)
167
0
                    goto loser;
168
0
169
0
                rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag), publickey,
170
0
                                        bulkkey, &ri->encKey);
171
0
172
0
                SECKEY_DestroyPublicKey(publickey);
173
0
                publickey = NULL;
174
0
                if (rv != SECSuccess)
175
0
                    goto loser;
176
0
                params = NULL; /* paranoia */
177
0
                break;
178
0
            default:
179
0
                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
180
0
                goto loser;
181
0
        }
182
0
183
0
        rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag,
184
0
                                   params);
185
0
        if (rv != SECSuccess)
186
0
            goto loser;
187
0
        if (arena)
188
0
            PORT_FreeArena(arena, PR_FALSE);
189
0
        arena = NULL;
190
0
    }
191
0
192
0
    encryptobj = sec_PKCS7CreateEncryptObject(cinfo->poolp, bulkkey,
193
0
                                              enccinfo->encalg,
194
0
                                              &(enccinfo->contentEncAlg));
195
0
    if (encryptobj != NULL) {
196
0
        PORT_ArenaUnmark(cinfo->poolp, mark);
197
0
        mark = NULL; /* good one; do not want to release */
198
0
    }
199
0
/* fallthru */
200
0
201
0
loser:
202
0
    if (arena) {
203
0
        PORT_FreeArena(arena, PR_FALSE);
204
0
    }
205
0
    if (publickey) {
206
0
        SECKEY_DestroyPublicKey(publickey);
207
0
    }
208
0
    if (ourPrivKey) {
209
0
        SECKEY_DestroyPrivateKey(ourPrivKey);
210
0
    }
211
0
    if (mark != NULL) {
212
0
        PORT_ArenaRelease(cinfo->poolp, mark);
213
0
    }
214
0
    if (orig_bulkkey == NULL) {
215
0
        if (bulkkey)
216
0
            PK11_FreeSymKey(bulkkey);
217
0
    }
218
0
219
0
    return encryptobj;
220
0
}
221
222
static void
223
sec_pkcs7_encoder_notify(void *arg, PRBool before, void *dest, int depth)
224
0
{
225
0
    SEC_PKCS7EncoderContext *p7ecx;
226
0
    SEC_PKCS7ContentInfo *cinfo;
227
0
    SECOidTag kind;
228
0
    PRBool before_content;
229
0
230
0
    /*
231
0
     * We want to notice just before the content field.  After fields are
232
0
     * not interesting to us.
233
0
     */
234
0
    if (!before)
235
0
        return;
236
0
237
0
    p7ecx = (SEC_PKCS7EncoderContext *)arg;
238
0
    cinfo = p7ecx->cinfo;
239
0
240
0
    before_content = PR_FALSE;
241
0
242
0
    /*
243
0
     * Watch for the content field, at which point we want to instruct
244
0
     * the ASN.1 encoder to start taking bytes from the buffer.
245
0
     *
246
0
     * XXX The following assumes the inner content type is data;
247
0
     * if/when we want to handle fully nested types, this will have
248
0
     * to recurse until reaching the innermost data content.
249
0
     */
250
0
    kind = SEC_PKCS7ContentType(cinfo);
251
0
    switch (kind) {
252
0
        default:
253
0
        case SEC_OID_PKCS7_DATA:
254
0
            if (dest == &(cinfo->content.data))
255
0
                before_content = PR_TRUE;
256
0
            break;
257
0
258
0
        case SEC_OID_PKCS7_DIGESTED_DATA: {
259
0
            SEC_PKCS7DigestedData *digd;
260
0
261
0
            digd = cinfo->content.digestedData;
262
0
            if (digd == NULL)
263
0
                break;
264
0
265
0
            if (dest == &(digd->contentInfo.content))
266
0
                before_content = PR_TRUE;
267
0
        } break;
268
0
269
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA: {
270
0
            SEC_PKCS7EncryptedData *encd;
271
0
272
0
            encd = cinfo->content.encryptedData;
273
0
            if (encd == NULL)
274
0
                break;
275
0
276
0
            if (dest == &(encd->encContentInfo.encContent))
277
0
                before_content = PR_TRUE;
278
0
        } break;
279
0
280
0
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
281
0
            SEC_PKCS7EnvelopedData *envd;
282
0
283
0
            envd = cinfo->content.envelopedData;
284
0
            if (envd == NULL)
285
0
                break;
286
0
287
0
            if (dest == &(envd->encContentInfo.encContent))
288
0
                before_content = PR_TRUE;
289
0
        } break;
290
0
291
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
292
0
            SEC_PKCS7SignedData *sigd;
293
0
294
0
            sigd = cinfo->content.signedData;
295
0
            if (sigd == NULL)
296
0
                break;
297
0
298
0
            if (dest == &(sigd->contentInfo.content))
299
0
                before_content = PR_TRUE;
300
0
        } break;
301
0
302
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
303
0
            SEC_PKCS7SignedAndEnvelopedData *saed;
304
0
305
0
            saed = cinfo->content.signedAndEnvelopedData;
306
0
            if (saed == NULL)
307
0
                break;
308
0
309
0
            if (dest == &(saed->encContentInfo.encContent))
310
0
                before_content = PR_TRUE;
311
0
        } break;
312
0
    }
313
0
314
0
    if (before_content) {
315
0
        /*
316
0
         * This will cause the next SEC_ASN1EncoderUpdate to take the
317
0
         * contents bytes from the passed-in buffer.
318
0
         */
319
0
        SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
320
0
        /*
321
0
         * And that is all we needed this notify function for.
322
0
         */
323
0
        SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);
324
0
    }
325
0
}
326
327
static SEC_PKCS7EncoderContext *
328
sec_pkcs7_encoder_start_contexts(SEC_PKCS7ContentInfo *cinfo,
329
                                 PK11SymKey *bulkkey)
330
0
{
331
0
    SEC_PKCS7EncoderContext *p7ecx;
332
0
    SECOidTag kind;
333
0
    PRBool encrypt;
334
0
    SECItem **digests;
335
0
    SECAlgorithmID *digestalg, **digestalgs;
336
0
337
0
    p7ecx =
338
0
        (SEC_PKCS7EncoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7EncoderContext));
339
0
    if (p7ecx == NULL)
340
0
        return NULL;
341
0
342
0
    digests = NULL;
343
0
    digestalg = NULL;
344
0
    digestalgs = NULL;
345
0
    encrypt = PR_FALSE;
346
0
347
0
    kind = SEC_PKCS7ContentType(cinfo);
348
0
    switch (kind) {
349
0
        default:
350
0
        case SEC_OID_PKCS7_DATA:
351
0
            break;
352
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
353
0
            digestalg = &(cinfo->content.digestedData->digestAlg);
354
0
            break;
355
0
        case SEC_OID_PKCS7_SIGNED_DATA:
356
0
            digests = cinfo->content.signedData->digests;
357
0
            digestalgs = cinfo->content.signedData->digestAlgorithms;
358
0
            break;
359
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
360
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
361
0
            encrypt = PR_TRUE;
362
0
            break;
363
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
364
0
            digests = cinfo->content.signedAndEnvelopedData->digests;
365
0
            digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms;
366
0
            encrypt = PR_TRUE;
367
0
            break;
368
0
    }
369
0
370
0
    if (encrypt) {
371
0
        p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt(cinfo, bulkkey);
372
0
        if (p7ecx->encryptobj == NULL) {
373
0
            PORT_Free(p7ecx);
374
0
            return NULL;
375
0
        }
376
0
    }
377
0
378
0
    if (digestalgs != NULL) {
379
0
        if (digests != NULL) {
380
0
            /* digests already created (probably for detached data) */
381
0
            digestalg = NULL;
382
0
        } else {
383
0
            /*
384
0
             * XXX Some day we should handle multiple digests; for now,
385
0
             * assume only one will be done.
386
0
             */
387
0
            PORT_Assert(digestalgs[0] != NULL && digestalgs[1] == NULL);
388
0
            digestalg = digestalgs[0];
389
0
        }
390
0
    }
391
0
392
0
    if (digestalg != NULL) {
393
0
        SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm));
394
0
395
0
        p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag);
396
0
        if (p7ecx->digestobj != NULL) {
397
0
            p7ecx->digestcx = (*p7ecx->digestobj->create)();
398
0
            if (p7ecx->digestcx == NULL)
399
0
                p7ecx->digestobj = NULL;
400
0
            else
401
0
                (*p7ecx->digestobj->begin)(p7ecx->digestcx);
402
0
        }
403
0
        if (p7ecx->digestobj == NULL) {
404
0
            if (p7ecx->encryptobj != NULL)
405
0
                sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
406
0
            PORT_Free(p7ecx);
407
0
            return NULL;
408
0
        }
409
0
    }
410
0
411
0
    p7ecx->cinfo = cinfo;
412
0
    return p7ecx;
413
0
}
414
415
SEC_PKCS7EncoderContext *
416
SEC_PKCS7EncoderStart(SEC_PKCS7ContentInfo *cinfo,
417
                      SEC_PKCS7EncoderOutputCallback outputfn,
418
                      void *outputarg,
419
                      PK11SymKey *bulkkey)
420
0
{
421
0
    SEC_PKCS7EncoderContext *p7ecx;
422
0
    SECStatus rv;
423
0
424
0
    p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
425
0
    if (p7ecx == NULL)
426
0
        return NULL;
427
0
428
0
    p7ecx->output.outputfn = outputfn;
429
0
    p7ecx->output.outputarg = outputarg;
430
0
431
0
    /*
432
0
     * Initialize the BER encoder.
433
0
     */
434
0
    p7ecx->ecx = SEC_ASN1EncoderStart(cinfo, sec_PKCS7ContentInfoTemplate,
435
0
                                      sec_pkcs7_encoder_out, &(p7ecx->output));
436
0
    if (p7ecx->ecx == NULL) {
437
0
        PORT_Free(p7ecx);
438
0
        return NULL;
439
0
    }
440
0
441
0
    /*
442
0
     * Indicate that we are streaming.  We will be streaming until we
443
0
     * get past the contents bytes.
444
0
     */
445
0
    SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
446
0
447
0
    /*
448
0
     * The notify function will watch for the contents field.
449
0
     */
450
0
    SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx);
451
0
452
0
    /*
453
0
     * This will encode everything up to the content bytes.  (The notify
454
0
     * function will then cause the encoding to stop there.)  Then our
455
0
     * caller can start passing contents bytes to our Update, which we
456
0
     * will pass along.
457
0
     */
458
0
    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
459
0
    if (rv != SECSuccess) {
460
0
        PORT_Free(p7ecx);
461
0
        return NULL;
462
0
    }
463
0
464
0
    return p7ecx;
465
0
}
466
467
/*
468
 * XXX If/when we support nested contents, this needs to be revised.
469
 */
470
static SECStatus
471
sec_pkcs7_encoder_work_data(SEC_PKCS7EncoderContext *p7ecx, SECItem *dest,
472
                            const unsigned char *data, unsigned long len,
473
                            PRBool final)
474
0
{
475
0
    unsigned char *buf = NULL;
476
0
    SECStatus rv;
477
0
478
0
    rv = SECSuccess; /* may as well be optimistic */
479
0
480
0
    /*
481
0
     * We should really have data to process, or we should be trying
482
0
     * to finish/flush the last block.  (This is an overly paranoid
483
0
     * check since all callers are in this file and simple inspection
484
0
     * proves they do it right.  But it could find a bug in future
485
0
     * modifications/development, that is why it is here.)
486
0
     */
487
0
    PORT_Assert((data != NULL && len) || final);
488
0
489
0
    /*
490
0
     * Update the running digest.
491
0
     * XXX This needs modification if/when we handle multiple digests.
492
0
     */
493
0
    if (len && p7ecx->digestobj != NULL) {
494
0
        (*p7ecx->digestobj->update)(p7ecx->digestcx, data, len);
495
0
    }
496
0
497
0
    /*
498
0
     * Encrypt this chunk.
499
0
     */
500
0
    if (p7ecx->encryptobj != NULL) {
501
0
        /* XXX the following lengths should all be longs? */
502
0
        unsigned int inlen;  /* length of data being encrypted */
503
0
        unsigned int outlen; /* length of encrypted data */
504
0
        unsigned int buflen; /* length available for encrypted data */
505
0
506
0
        inlen = len;
507
0
        buflen = sec_PKCS7EncryptLength(p7ecx->encryptobj, inlen, final);
508
0
        if (buflen == 0) {
509
0
            /*
510
0
             * No output is expected, but the input data may be buffered
511
0
             * so we still have to call Encrypt.
512
0
             */
513
0
            rv = sec_PKCS7Encrypt(p7ecx->encryptobj, NULL, &outlen, 0,
514
0
                                  data, inlen, final);
515
0
            if (final) {
516
0
                len = 0;
517
0
                goto done;
518
0
            }
519
0
            return rv;
520
0
        }
521
0
522
0
        if (dest != NULL)
523
0
            buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen);
524
0
        else
525
0
            buf = (unsigned char *)PORT_Alloc(buflen);
526
0
527
0
        if (buf == NULL) {
528
0
            rv = SECFailure;
529
0
        } else {
530
0
            rv = sec_PKCS7Encrypt(p7ecx->encryptobj, buf, &outlen, buflen,
531
0
                                  data, inlen, final);
532
0
            data = buf;
533
0
            len = outlen;
534
0
        }
535
0
        if (rv != SECSuccess) {
536
0
            if (final)
537
0
                goto done;
538
0
            return rv;
539
0
        }
540
0
    }
541
0
542
0
    if (p7ecx->ecx != NULL) {
543
0
        /*
544
0
         * Encode the contents bytes.
545
0
         */
546
0
        if (len) {
547
0
            rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
548
0
        }
549
0
    }
550
0
551
0
done:
552
0
    if (p7ecx->encryptobj != NULL) {
553
0
        if (final)
554
0
            sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
555
0
        if (dest != NULL) {
556
0
            dest->data = buf;
557
0
            dest->len = len;
558
0
        } else if (buf != NULL) {
559
0
            PORT_Free(buf);
560
0
        }
561
0
    }
562
0
563
0
    if (final && p7ecx->digestobj != NULL) {
564
0
        SECItem *digest, **digests, ***digestsp;
565
0
        unsigned char *digdata;
566
0
        SECOidTag kind;
567
0
568
0
        kind = SEC_PKCS7ContentType(p7ecx->cinfo);
569
0
        switch (kind) {
570
0
            default:
571
0
                PORT_Assert(0);
572
0
                return SECFailure;
573
0
            case SEC_OID_PKCS7_DIGESTED_DATA:
574
0
                digest = &(p7ecx->cinfo->content.digestedData->digest);
575
0
                digestsp = NULL;
576
0
                break;
577
0
            case SEC_OID_PKCS7_SIGNED_DATA:
578
0
                digest = NULL;
579
0
                digestsp = &(p7ecx->cinfo->content.signedData->digests);
580
0
                break;
581
0
            case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
582
0
                digest = NULL;
583
0
                digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests);
584
0
                break;
585
0
        }
586
0
587
0
        digdata = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
588
0
                                                   p7ecx->digestobj->length);
589
0
        if (digdata == NULL)
590
0
            return SECFailure;
591
0
592
0
        if (digestsp != NULL) {
593
0
            PORT_Assert(digest == NULL);
594
0
595
0
            digest = (SECItem *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
596
0
                                                sizeof(SECItem));
597
0
            digests = (SECItem **)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
598
0
                                                  2 * sizeof(SECItem *));
599
0
            if (digests == NULL || digest == NULL)
600
0
                return SECFailure;
601
0
602
0
            digests[0] = digest;
603
0
            digests[1] = NULL;
604
0
605
0
            *digestsp = digests;
606
0
        }
607
0
608
0
        PORT_Assert(digest != NULL);
609
0
610
0
        digest->data = digdata;
611
0
        digest->len = p7ecx->digestobj->length;
612
0
613
0
        (*p7ecx->digestobj->end)(p7ecx->digestcx, digest->data,
614
0
                                 &(digest->len), digest->len);
615
0
        (*p7ecx->digestobj->destroy)(p7ecx->digestcx, PR_TRUE);
616
0
    }
617
0
618
0
    return rv;
619
0
}
620
621
SECStatus
622
SEC_PKCS7EncoderUpdate(SEC_PKCS7EncoderContext *p7ecx,
623
                       const char *data, unsigned long len)
624
0
{
625
0
    /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
626
0
    return sec_pkcs7_encoder_work_data(p7ecx, NULL,
627
0
                                       (const unsigned char *)data, len,
628
0
                                       PR_FALSE);
629
0
}
630
631
static SECStatus
632
sec_pkcs7_encoder_sig_and_certs(SEC_PKCS7ContentInfo *cinfo,
633
                                SECKEYGetPasswordKey pwfn, void *pwfnarg)
634
0
{
635
0
    SECOidTag kind;
636
0
    CERTCertificate **certs;
637
0
    CERTCertificateList **certlists;
638
0
    SECAlgorithmID **digestalgs;
639
0
    SECItem **digests;
640
0
    SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
641
0
    SECItem **rawcerts, ***rawcertsp;
642
0
    PLArenaPool *poolp;
643
0
    int certcount;
644
0
    int ci, cli, rci, si;
645
0
646
0
    kind = SEC_PKCS7ContentType(cinfo);
647
0
    switch (kind) {
648
0
        default:
649
0
        case SEC_OID_PKCS7_DATA:
650
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
651
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
652
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
653
0
            certs = NULL;
654
0
            certlists = NULL;
655
0
            digestalgs = NULL;
656
0
            digests = NULL;
657
0
            signerinfos = NULL;
658
0
            rawcertsp = NULL;
659
0
            break;
660
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
661
0
            SEC_PKCS7SignedData *sdp;
662
0
663
0
            sdp = cinfo->content.signedData;
664
0
            certs = sdp->certs;
665
0
            certlists = sdp->certLists;
666
0
            digestalgs = sdp->digestAlgorithms;
667
0
            digests = sdp->digests;
668
0
            signerinfos = sdp->signerInfos;
669
0
            rawcertsp = &(sdp->rawCerts);
670
0
        } break;
671
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
672
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
673
0
674
0
            saedp = cinfo->content.signedAndEnvelopedData;
675
0
            certs = saedp->certs;
676
0
            certlists = saedp->certLists;
677
0
            digestalgs = saedp->digestAlgorithms;
678
0
            digests = saedp->digests;
679
0
            signerinfos = saedp->signerInfos;
680
0
            rawcertsp = &(saedp->rawCerts);
681
0
        } break;
682
0
    }
683
0
684
0
    if (certs == NULL && certlists == NULL && signerinfos == NULL)
685
0
        return SECSuccess; /* nothing for us to do! */
686
0
687
0
    poolp = cinfo->poolp;
688
0
    certcount = 0;
689
0
690
0
    if (signerinfos != NULL) {
691
0
        SECOidTag digestalgtag;
692
0
        int di;
693
0
        SECStatus rv;
694
0
        CERTCertificate *cert;
695
0
        SECKEYPrivateKey *privkey;
696
0
        SECItem signature;
697
0
        SECOidTag signalgtag;
698
0
699
0
        PORT_Assert(digestalgs != NULL && digests != NULL);
700
0
701
0
        /*
702
0
         * If one fails, we bail right then.  If we want to continue and
703
0
         * try to do subsequent signatures, this loop, and the departures
704
0
         * from it, will need to be reworked.
705
0
         */
706
0
        for (si = 0; signerinfos[si] != NULL; si++) {
707
0
708
0
            signerinfo = signerinfos[si];
709
0
710
0
            /* find right digest */
711
0
            digestalgtag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
712
0
            for (di = 0; digestalgs[di] != NULL; di++) {
713
0
                /* XXX Should I be comparing more than the tag? */
714
0
                if (digestalgtag == SECOID_GetAlgorithmTag(digestalgs[di]))
715
0
                    break;
716
0
            }
717
0
            if (digestalgs[di] == NULL) {
718
0
                /* XXX oops; do what? set an error? */
719
0
                return SECFailure;
720
0
            }
721
0
            PORT_Assert(digests[di] != NULL);
722
0
723
0
            cert = signerinfo->cert;
724
0
            privkey = PK11_FindKeyByAnyCert(cert, pwfnarg);
725
0
            if (privkey == NULL)
726
0
                return SECFailure;
727
0
728
0
            /*
729
0
             * XXX I think there should be a cert-level interface for this,
730
0
             * so that I do not have to know about subjectPublicKeyInfo...
731
0
             */
732
0
            signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
733
0
734
0
            if (signerinfo->authAttr != NULL) {
735
0
                SEC_PKCS7Attribute *attr;
736
0
                SECItem encoded_attrs;
737
0
                SECItem *dummy;
738
0
                SECOidTag algid;
739
0
740
0
                /*
741
0
                 * First, find and fill in the message digest attribute.
742
0
                 */
743
0
                attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
744
0
                                              SEC_OID_PKCS9_MESSAGE_DIGEST,
745
0
                                              PR_TRUE);
746
0
                PORT_Assert(attr != NULL);
747
0
                if (attr == NULL) {
748
0
                    SECKEY_DestroyPrivateKey(privkey);
749
0
                    return SECFailure;
750
0
                }
751
0
752
0
                /*
753
0
                 * XXX The second half of the following assertion prevents
754
0
                 * the encoder from being called twice on the same content.
755
0
                 * Either just remove the second half the assertion, or
756
0
                 * change the code to check if the value already there is
757
0
                 * the same as digests[di], whichever seems more right.
758
0
                 */
759
0
                PORT_Assert(attr->values != NULL && attr->values[0] == NULL);
760
0
                attr->values[0] = digests[di];
761
0
762
0
                /*
763
0
                 * Before encoding, reorder the attributes so that when they
764
0
                 * are encoded, they will be conforming DER, which is required
765
0
                 * to have a specific order and that is what must be used for
766
0
                 * the hash/signature.  We do this here, rather than building
767
0
                 * it into EncodeAttributes, because we do not want to do
768
0
                 * such reordering on incoming messages (which also uses
769
0
                 * EncodeAttributes) or our old signatures (and other "broken"
770
0
                 * implementations) will not verify.  So, we want to guarantee
771
0
                 * that we send out good DER encodings of attributes, but not
772
0
                 * to expect to receive them.
773
0
                 */
774
0
                rv = sec_PKCS7ReorderAttributes(signerinfo->authAttr);
775
0
                if (rv != SECSuccess) {
776
0
                    SECKEY_DestroyPrivateKey(privkey);
777
0
                    return SECFailure;
778
0
                }
779
0
780
0
                encoded_attrs.data = NULL;
781
0
                encoded_attrs.len = 0;
782
0
                dummy = sec_PKCS7EncodeAttributes(NULL, &encoded_attrs,
783
0
                                                  &(signerinfo->authAttr));
784
0
                if (dummy == NULL) {
785
0
                    SECKEY_DestroyPrivateKey(privkey);
786
0
                    return SECFailure;
787
0
                }
788
0
789
0
                algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType,
790
0
                                                        digestalgtag);
791
0
                if (algid == SEC_OID_UNKNOWN) {
792
0
                    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
793
0
                    SECKEY_DestroyPrivateKey(privkey);
794
0
                    return SECFailure;
795
0
                }
796
0
                rv = SEC_SignData(&signature,
797
0
                                  encoded_attrs.data, encoded_attrs.len,
798
0
                                  privkey,
799
0
                                  algid);
800
0
                SECITEM_FreeItem(&encoded_attrs, PR_FALSE);
801
0
            } else {
802
0
                rv = SGN_Digest(privkey, digestalgtag, &signature,
803
0
                                digests[di]);
804
0
            }
805
0
806
0
            SECKEY_DestroyPrivateKey(privkey);
807
0
808
0
            if (rv != SECSuccess)
809
0
                return rv;
810
0
811
0
            rv = SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature);
812
0
            if (rv != SECSuccess)
813
0
                return rv;
814
0
815
0
            SECITEM_FreeItem(&signature, PR_FALSE);
816
0
817
0
            rv = SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
818
0
                                       signalgtag, NULL);
819
0
            if (rv != SECSuccess)
820
0
                return SECFailure;
821
0
822
0
            /*
823
0
             * Count the cert chain for this signer.
824
0
             */
825
0
            if (signerinfo->certList != NULL)
826
0
                certcount += signerinfo->certList->len;
827
0
        }
828
0
    }
829
0
830
0
    if (certs != NULL) {
831
0
        for (ci = 0; certs[ci] != NULL; ci++)
832
0
            certcount++;
833
0
    }
834
0
835
0
    if (certlists != NULL) {
836
0
        for (cli = 0; certlists[cli] != NULL; cli++)
837
0
            certcount += certlists[cli]->len;
838
0
    }
839
0
840
0
    if (certcount == 0)
841
0
        return SECSuccess; /* signing done; no certs */
842
0
843
0
    /*
844
0
     * Combine all of the certs and cert chains into rawcerts.
845
0
     * Note: certcount is an upper bound; we may not need that many slots
846
0
     * but we will allocate anyway to avoid having to do another pass.
847
0
     * (The temporary space saving is not worth it.)
848
0
     */
849
0
    rawcerts = (SECItem **)PORT_ArenaAlloc(poolp,
850
0
                                           (certcount + 1) * sizeof(SECItem *));
851
0
    if (rawcerts == NULL)
852
0
        return SECFailure;
853
0
854
0
    /*
855
0
     * XXX Want to check for duplicates and not add *any* cert that is
856
0
     * already in the set.  This will be more important when we start
857
0
     * dealing with larger sets of certs, dual-key certs (signing and
858
0
     * encryption), etc.  For the time being we can slide by...
859
0
     */
860
0
    rci = 0;
861
0
    if (signerinfos != NULL) {
862
0
        for (si = 0; signerinfos[si] != NULL; si++) {
863
0
            signerinfo = signerinfos[si];
864
0
            for (ci = 0; ci < signerinfo->certList->len; ci++)
865
0
                rawcerts[rci++] = &(signerinfo->certList->certs[ci]);
866
0
        }
867
0
    }
868
0
869
0
    if (certs != NULL) {
870
0
        for (ci = 0; certs[ci] != NULL; ci++)
871
0
            rawcerts[rci++] = &(certs[ci]->derCert);
872
0
    }
873
0
874
0
    if (certlists != NULL) {
875
0
        for (cli = 0; certlists[cli] != NULL; cli++) {
876
0
            for (ci = 0; ci < certlists[cli]->len; ci++)
877
0
                rawcerts[rci++] = &(certlists[cli]->certs[ci]);
878
0
        }
879
0
    }
880
0
881
0
    rawcerts[rci] = NULL;
882
0
    *rawcertsp = rawcerts;
883
0
884
0
    return SECSuccess;
885
0
}
886
887
SECStatus
888
SEC_PKCS7EncoderFinish(SEC_PKCS7EncoderContext *p7ecx,
889
                       SECKEYGetPasswordKey pwfn, void *pwfnarg)
890
0
{
891
0
    SECStatus rv;
892
0
893
0
    /*
894
0
     * Flush out any remaining data.
895
0
     */
896
0
    rv = sec_pkcs7_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE);
897
0
898
0
    /*
899
0
     * Turn off streaming stuff.
900
0
     */
901
0
    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
902
0
    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
903
0
904
0
    if (rv != SECSuccess)
905
0
        goto loser;
906
0
907
0
    rv = sec_pkcs7_encoder_sig_and_certs(p7ecx->cinfo, pwfn, pwfnarg);
908
0
    if (rv != SECSuccess)
909
0
        goto loser;
910
0
911
0
    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
912
0
913
0
loser:
914
0
    SEC_ASN1EncoderFinish(p7ecx->ecx);
915
0
    PORT_Free(p7ecx);
916
0
    return rv;
917
0
}
918
919
/*
920
 * Abort the ASN.1 stream. Used by pkcs 12
921
 */
922
void
923
SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error)
924
0
{
925
0
    PORT_Assert(p7ecx);
926
0
    SEC_ASN1EncoderAbort(p7ecx->ecx, error);
927
0
}
928
929
/*
930
 * After this routine is called, the entire PKCS7 contentInfo is ready
931
 * to be encoded.  This is used internally, but can also be called from
932
 * elsewhere for those who want to be able to just have pointers to
933
 * the ASN1 template for pkcs7 contentInfo built into their own encodings.
934
 */
935
SECStatus
936
SEC_PKCS7PrepareForEncode(SEC_PKCS7ContentInfo *cinfo,
937
                          PK11SymKey *bulkkey,
938
                          SECKEYGetPasswordKey pwfn,
939
                          void *pwfnarg)
940
0
{
941
0
    SEC_PKCS7EncoderContext *p7ecx;
942
0
    SECItem *content, *enc_content;
943
0
    SECStatus rv;
944
0
945
0
    p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
946
0
    if (p7ecx == NULL)
947
0
        return SECFailure;
948
0
949
0
    content = SEC_PKCS7GetContent(cinfo);
950
0
951
0
    if (p7ecx->encryptobj != NULL) {
952
0
        SECOidTag kind;
953
0
        SEC_PKCS7EncryptedContentInfo *enccinfo;
954
0
955
0
        kind = SEC_PKCS7ContentType(p7ecx->cinfo);
956
0
        switch (kind) {
957
0
            default:
958
0
                PORT_Assert(0);
959
0
                rv = SECFailure;
960
0
                goto loser;
961
0
            case SEC_OID_PKCS7_ENCRYPTED_DATA:
962
0
                enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo);
963
0
                break;
964
0
            case SEC_OID_PKCS7_ENVELOPED_DATA:
965
0
                enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo);
966
0
                break;
967
0
            case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
968
0
                enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo);
969
0
                break;
970
0
        }
971
0
        enc_content = &(enccinfo->encContent);
972
0
    } else {
973
0
        enc_content = NULL;
974
0
    }
975
0
976
0
    if (content != NULL && content->data != NULL && content->len) {
977
0
        rv = sec_pkcs7_encoder_work_data(p7ecx, enc_content,
978
0
                                         content->data, content->len, PR_TRUE);
979
0
        if (rv != SECSuccess)
980
0
            goto loser;
981
0
    }
982
0
983
0
    rv = sec_pkcs7_encoder_sig_and_certs(cinfo, pwfn, pwfnarg);
984
0
985
0
loser:
986
0
    PORT_Free(p7ecx);
987
0
    return rv;
988
0
}
989
990
/*
991
 * Encode a PKCS7 object, in one shot.  All necessary components
992
 * of the object must already be specified.  Either the data has
993
 * already been included (via SetContent), or the data is detached,
994
 * or there is no data at all (certs-only).
995
 *
996
 * "cinfo" specifies the object to be encoded.
997
 *
998
 * "outputfn" is where the encoded bytes will be passed.
999
 *
1000
 * "outputarg" is an opaque argument to the above callback.
1001
 *
1002
 * "bulkkey" specifies the bulk encryption key to use.   This argument
1003
 * can be NULL if no encryption is being done, or if the bulk key should
1004
 * be generated internally (usually the case for EnvelopedData but never
1005
 * for EncryptedData, which *must* provide a bulk encryption key).
1006
 *
1007
 * "pwfn" is a callback for getting the password which protects the
1008
 * private key of the signer.  This argument can be NULL if it is known
1009
 * that no signing is going to be done.
1010
 *
1011
 * "pwfnarg" is an opaque argument to the above callback.
1012
 */
1013
SECStatus
1014
SEC_PKCS7Encode(SEC_PKCS7ContentInfo *cinfo,
1015
                SEC_PKCS7EncoderOutputCallback outputfn,
1016
                void *outputarg,
1017
                PK11SymKey *bulkkey,
1018
                SECKEYGetPasswordKey pwfn,
1019
                void *pwfnarg)
1020
0
{
1021
0
    SECStatus rv;
1022
0
1023
0
    rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
1024
0
    if (rv == SECSuccess) {
1025
0
        struct sec_pkcs7_encoder_output outputcx;
1026
0
1027
0
        outputcx.outputfn = outputfn;
1028
0
        outputcx.outputarg = outputarg;
1029
0
1030
0
        rv = SEC_ASN1Encode(cinfo, sec_PKCS7ContentInfoTemplate,
1031
0
                            sec_pkcs7_encoder_out, &outputcx);
1032
0
    }
1033
0
1034
0
    return rv;
1035
0
}
1036
1037
/*
1038
 * Encode a PKCS7 object, in one shot.  All necessary components
1039
 * of the object must already be specified.  Either the data has
1040
 * already been included (via SetContent), or the data is detached,
1041
 * or there is no data at all (certs-only).  The output, rather than
1042
 * being passed to an output function as is done above, is all put
1043
 * into a SECItem.
1044
 *
1045
 * "pool" specifies a pool from which to allocate the result.
1046
 * It can be NULL, in which case memory is allocated generically.
1047
 *
1048
 * "dest" specifies a SECItem in which to put the result data.
1049
 * It can be NULL, in which case the entire item is allocated, too.
1050
 *
1051
 * "cinfo" specifies the object to be encoded.
1052
 *
1053
 * "bulkkey" specifies the bulk encryption key to use.   This argument
1054
 * can be NULL if no encryption is being done, or if the bulk key should
1055
 * be generated internally (usually the case for EnvelopedData but never
1056
 * for EncryptedData, which *must* provide a bulk encryption key).
1057
 *
1058
 * "pwfn" is a callback for getting the password which protects the
1059
 * private key of the signer.  This argument can be NULL if it is known
1060
 * that no signing is going to be done.
1061
 *
1062
 * "pwfnarg" is an opaque argument to the above callback.
1063
 */
1064
SECItem *
1065
SEC_PKCS7EncodeItem(PLArenaPool *pool,
1066
                    SECItem *dest,
1067
                    SEC_PKCS7ContentInfo *cinfo,
1068
                    PK11SymKey *bulkkey,
1069
                    SECKEYGetPasswordKey pwfn,
1070
                    void *pwfnarg)
1071
0
{
1072
0
    SECStatus rv;
1073
0
1074
0
    rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
1075
0
    if (rv != SECSuccess)
1076
0
        return NULL;
1077
0
1078
0
    return SEC_ASN1EncodeItem(pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);
1079
0
}