Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/pkcs7/p7create.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 creation.
7
 */
8
9
#include "p7local.h"
10
11
#include "cert.h"
12
#include "secasn1.h"
13
#include "secitem.h"
14
#include "secoid.h"
15
#include "pk11func.h"
16
#include "prtime.h"
17
#include "secerr.h"
18
#include "secder.h"
19
#include "secpkcs5.h"
20
21
const int NSS_PBE_DEFAULT_ITERATION_COUNT = /* used in p12e.c too */
22
#ifdef DEBUG
23
    10000
24
#else
25
    600000
26
#endif
27
    ;
28
29
static SECStatus
30
sec_pkcs7_init_content_info(SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp,
31
                            SECOidTag kind, PRBool detached)
32
0
{
33
0
    void *thing;
34
0
    int version;
35
0
    SECItem *versionp;
36
0
    SECStatus rv;
37
0
38
0
    PORT_Assert(cinfo != NULL && poolp != NULL);
39
0
    if (cinfo == NULL || poolp == NULL)
40
0
        return SECFailure;
41
0
42
0
    cinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
43
0
    PORT_Assert(cinfo->contentTypeTag && cinfo->contentTypeTag->offset == kind);
44
0
45
0
    rv = SECITEM_CopyItem(poolp, &(cinfo->contentType),
46
0
                          &(cinfo->contentTypeTag->oid));
47
0
    if (rv != SECSuccess)
48
0
        return rv;
49
0
50
0
    if (detached)
51
0
        return SECSuccess;
52
0
53
0
    switch (kind) {
54
0
        default:
55
0
        case SEC_OID_PKCS7_DATA:
56
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SECItem));
57
0
            cinfo->content.data = (SECItem *)thing;
58
0
            versionp = NULL;
59
0
            version = -1;
60
0
            break;
61
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
62
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7DigestedData));
63
0
            cinfo->content.digestedData = (SEC_PKCS7DigestedData *)thing;
64
0
            versionp = &(cinfo->content.digestedData->version);
65
0
            version = SEC_PKCS7_DIGESTED_DATA_VERSION;
66
0
            break;
67
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
68
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EncryptedData));
69
0
            cinfo->content.encryptedData = (SEC_PKCS7EncryptedData *)thing;
70
0
            versionp = &(cinfo->content.encryptedData->version);
71
0
            version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
72
0
            break;
73
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
74
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EnvelopedData));
75
0
            cinfo->content.envelopedData =
76
0
                (SEC_PKCS7EnvelopedData *)thing;
77
0
            versionp = &(cinfo->content.envelopedData->version);
78
0
            version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
79
0
            break;
80
0
        case SEC_OID_PKCS7_SIGNED_DATA:
81
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedData));
82
0
            cinfo->content.signedData =
83
0
                (SEC_PKCS7SignedData *)thing;
84
0
            versionp = &(cinfo->content.signedData->version);
85
0
            version = SEC_PKCS7_SIGNED_DATA_VERSION;
86
0
            break;
87
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
88
0
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedAndEnvelopedData));
89
0
            cinfo->content.signedAndEnvelopedData =
90
0
                (SEC_PKCS7SignedAndEnvelopedData *)thing;
91
0
            versionp = &(cinfo->content.signedAndEnvelopedData->version);
92
0
            version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
93
0
            break;
94
0
    }
95
0
96
0
    if (thing == NULL)
97
0
        return SECFailure;
98
0
99
0
    if (versionp != NULL) {
100
0
        SECItem *dummy;
101
0
102
0
        PORT_Assert(version >= 0);
103
0
        dummy = SEC_ASN1EncodeInteger(poolp, versionp, version);
104
0
        if (dummy == NULL)
105
0
            return SECFailure;
106
0
        PORT_Assert(dummy == versionp);
107
0
    }
108
0
109
0
    return SECSuccess;
110
0
}
111
112
static SEC_PKCS7ContentInfo *
113
sec_pkcs7_create_content_info(SECOidTag kind, PRBool detached,
114
                              SECKEYGetPasswordKey pwfn, void *pwfn_arg)
115
0
{
116
0
    SEC_PKCS7ContentInfo *cinfo;
117
0
    PLArenaPool *poolp;
118
0
    SECStatus rv;
119
0
120
0
    poolp = PORT_NewArena(1024); /* XXX what is right value? */
121
0
    if (poolp == NULL)
122
0
        return NULL;
123
0
124
0
    cinfo = (SEC_PKCS7ContentInfo *)PORT_ArenaZAlloc(poolp, sizeof(*cinfo));
125
0
    if (cinfo == NULL) {
126
0
        PORT_FreeArena(poolp, PR_FALSE);
127
0
        return NULL;
128
0
    }
129
0
130
0
    cinfo->poolp = poolp;
131
0
    cinfo->pwfn = pwfn;
132
0
    cinfo->pwfn_arg = pwfn_arg;
133
0
    cinfo->created = PR_TRUE;
134
0
    cinfo->refCount = 1;
135
0
136
0
    rv = sec_pkcs7_init_content_info(cinfo, poolp, kind, detached);
137
0
    if (rv != SECSuccess) {
138
0
        PORT_FreeArena(poolp, PR_FALSE);
139
0
        return NULL;
140
0
    }
141
0
142
0
    return cinfo;
143
0
}
144
145
/*
146
 * Add a signer to a PKCS7 thing, verifying the signature cert first.
147
 * Any error returns SECFailure.
148
 *
149
 * XXX Right now this only adds the *first* signer.  It fails if you try
150
 * to add a second one -- this needs to be fixed.
151
 */
152
static SECStatus
153
sec_pkcs7_add_signer(SEC_PKCS7ContentInfo *cinfo,
154
                     CERTCertificate *cert,
155
                     SECCertUsage certusage,
156
                     CERTCertDBHandle *certdb,
157
                     SECOidTag digestalgtag,
158
                     SECItem *digestdata)
159
0
{
160
0
    SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
161
0
    SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
162
0
    SECItem *digest, **digests, ***digestsp;
163
0
    SECItem *dummy;
164
0
    void *mark;
165
0
    SECStatus rv;
166
0
    SECOidTag kind;
167
0
168
0
    kind = SEC_PKCS7ContentType(cinfo);
169
0
    switch (kind) {
170
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
171
0
            SEC_PKCS7SignedData *sdp;
172
0
173
0
            sdp = cinfo->content.signedData;
174
0
            digestalgsp = &(sdp->digestAlgorithms);
175
0
            digestsp = &(sdp->digests);
176
0
            signerinfosp = &(sdp->signerInfos);
177
0
        } break;
178
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
179
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
180
0
181
0
            saedp = cinfo->content.signedAndEnvelopedData;
182
0
            digestalgsp = &(saedp->digestAlgorithms);
183
0
            digestsp = &(saedp->digests);
184
0
            signerinfosp = &(saedp->signerInfos);
185
0
        } break;
186
0
        default:
187
0
            return SECFailure; /* XXX set an error? */
188
0
    }
189
0
190
0
    /*
191
0
     * XXX I think that CERT_VerifyCert should do this if *it* is passed
192
0
     * a NULL database.
193
0
     */
194
0
    if (certdb == NULL) {
195
0
        certdb = CERT_GetDefaultCertDB();
196
0
        if (certdb == NULL)
197
0
            return SECFailure; /* XXX set an error? */
198
0
    }
199
0
200
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
201
0
                        cinfo->pwfn_arg, NULL) != SECSuccess) {
202
0
        /* XXX Did CERT_VerifyCert set an error? */
203
0
        return SECFailure;
204
0
    }
205
0
206
0
    /*
207
0
     * XXX This is the check that we do not already have a signer.
208
0
     * This is not what we really want -- we want to allow this
209
0
     * and *add* the new signer.
210
0
     */
211
0
    PORT_Assert(*signerinfosp == NULL && *digestalgsp == NULL && *digestsp == NULL);
212
0
    if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
213
0
        return SECFailure;
214
0
215
0
    mark = PORT_ArenaMark(cinfo->poolp);
216
0
217
0
    signerinfo = (SEC_PKCS7SignerInfo *)PORT_ArenaZAlloc(cinfo->poolp,
218
0
                                                         sizeof(SEC_PKCS7SignerInfo));
219
0
    if (signerinfo == NULL) {
220
0
        PORT_ArenaRelease(cinfo->poolp, mark);
221
0
        return SECFailure;
222
0
    }
223
0
224
0
    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &signerinfo->version,
225
0
                                  SEC_PKCS7_SIGNER_INFO_VERSION);
226
0
    if (dummy == NULL) {
227
0
        PORT_ArenaRelease(cinfo->poolp, mark);
228
0
        return SECFailure;
229
0
    }
230
0
    PORT_Assert(dummy == &signerinfo->version);
231
0
232
0
    signerinfo->cert = CERT_DupCertificate(cert);
233
0
    if (signerinfo->cert == NULL) {
234
0
        PORT_ArenaRelease(cinfo->poolp, mark);
235
0
        return SECFailure;
236
0
    }
237
0
238
0
    signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
239
0
    if (signerinfo->issuerAndSN == NULL) {
240
0
        PORT_ArenaRelease(cinfo->poolp, mark);
241
0
        return SECFailure;
242
0
    }
243
0
244
0
    rv = SECOID_SetAlgorithmID(cinfo->poolp, &signerinfo->digestAlg,
245
0
                               digestalgtag, NULL);
246
0
    if (rv != SECSuccess) {
247
0
        PORT_ArenaRelease(cinfo->poolp, mark);
248
0
        return SECFailure;
249
0
    }
250
0
251
0
    /*
252
0
     * Okay, now signerinfo is all set.  We just need to put it and its
253
0
     * companions (another copy of the digest algorithm, and the digest
254
0
     * itself if given) into the main structure.
255
0
     *
256
0
     * XXX If we are handling more than one signer, the following code
257
0
     * needs to look through the digest algorithms already specified
258
0
     * and see if the same one is there already.  If it is, it does not
259
0
     * need to be added again.  Also, if it is there *and* the digest
260
0
     * is not null, then the digest given should match the digest already
261
0
     * specified -- if not, that is an error.  Finally, the new signerinfo
262
0
     * should be *added* to the set already found.
263
0
     */
264
0
265
0
    signerinfos = (SEC_PKCS7SignerInfo **)PORT_ArenaAlloc(cinfo->poolp,
266
0
                                                          2 * sizeof(SEC_PKCS7SignerInfo *));
267
0
    if (signerinfos == NULL) {
268
0
        PORT_ArenaRelease(cinfo->poolp, mark);
269
0
        return SECFailure;
270
0
    }
271
0
    signerinfos[0] = signerinfo;
272
0
    signerinfos[1] = NULL;
273
0
274
0
    digestalg = PORT_ArenaZAlloc(cinfo->poolp, sizeof(SECAlgorithmID));
275
0
    digestalgs = PORT_ArenaAlloc(cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
276
0
    if (digestalg == NULL || digestalgs == NULL) {
277
0
        PORT_ArenaRelease(cinfo->poolp, mark);
278
0
        return SECFailure;
279
0
    }
280
0
    rv = SECOID_SetAlgorithmID(cinfo->poolp, digestalg, digestalgtag, NULL);
281
0
    if (rv != SECSuccess) {
282
0
        PORT_ArenaRelease(cinfo->poolp, mark);
283
0
        return SECFailure;
284
0
    }
285
0
    digestalgs[0] = digestalg;
286
0
    digestalgs[1] = NULL;
287
0
288
0
    if (digestdata != NULL) {
289
0
        digest = (SECItem *)PORT_ArenaAlloc(cinfo->poolp, sizeof(SECItem));
290
0
        digests = (SECItem **)PORT_ArenaAlloc(cinfo->poolp,
291
0
                                              2 * sizeof(SECItem *));
292
0
        if (digest == NULL || digests == NULL) {
293
0
            PORT_ArenaRelease(cinfo->poolp, mark);
294
0
            return SECFailure;
295
0
        }
296
0
        rv = SECITEM_CopyItem(cinfo->poolp, digest, digestdata);
297
0
        if (rv != SECSuccess) {
298
0
            PORT_ArenaRelease(cinfo->poolp, mark);
299
0
            return SECFailure;
300
0
        }
301
0
        digests[0] = digest;
302
0
        digests[1] = NULL;
303
0
    } else {
304
0
        digests = NULL;
305
0
    }
306
0
307
0
    *signerinfosp = signerinfos;
308
0
    *digestalgsp = digestalgs;
309
0
    *digestsp = digests;
310
0
311
0
    PORT_ArenaUnmark(cinfo->poolp, mark);
312
0
    return SECSuccess;
313
0
}
314
315
/*
316
 * Helper function for creating an empty signedData.
317
 */
318
static SEC_PKCS7ContentInfo *
319
sec_pkcs7_create_signed_data(SECKEYGetPasswordKey pwfn, void *pwfn_arg)
320
0
{
321
0
    SEC_PKCS7ContentInfo *cinfo;
322
0
    SEC_PKCS7SignedData *sigd;
323
0
    SECStatus rv;
324
0
325
0
    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
326
0
                                          pwfn, pwfn_arg);
327
0
    if (cinfo == NULL)
328
0
        return NULL;
329
0
330
0
    sigd = cinfo->content.signedData;
331
0
    PORT_Assert(sigd != NULL);
332
0
333
0
    /*
334
0
     * XXX Might we want to allow content types other than data?
335
0
     * If so, via what interface?
336
0
     */
337
0
    rv = sec_pkcs7_init_content_info(&(sigd->contentInfo), cinfo->poolp,
338
0
                                     SEC_OID_PKCS7_DATA, PR_TRUE);
339
0
    if (rv != SECSuccess) {
340
0
        SEC_PKCS7DestroyContentInfo(cinfo);
341
0
        return NULL;
342
0
    }
343
0
344
0
    return cinfo;
345
0
}
346
347
/*
348
 * Start a PKCS7 signing context.
349
 *
350
 * "cert" is the cert that will be used to sign the data.  It will be
351
 * checked for validity.
352
 *
353
 * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
354
 * XXX Maybe SECCertUsage should be split so that our caller just says
355
 * "email" and *we* add the "signing" part -- otherwise our caller
356
 * could be lying about the usage; we do not want to allow encryption
357
 * certs for signing or vice versa.
358
 *
359
 * "certdb" is the cert database to use for verifying the cert.
360
 * It can be NULL if a default database is available (like in the client).
361
 * 
362
 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
363
 *
364
 * "digest" is the actual digest of the data.  It must be provided in
365
 * the case of detached data or NULL if the content will be included.
366
 *
367
 * The return value can be passed to functions which add things to
368
 * it like attributes, then eventually to SEC_PKCS7Encode() or to
369
 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
370
 * SEC_PKCS7DestroyContentInfo().
371
 *
372
 * An error results in a return value of NULL and an error set.
373
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
374
 */
375
SEC_PKCS7ContentInfo *
376
SEC_PKCS7CreateSignedData(CERTCertificate *cert,
377
                          SECCertUsage certusage,
378
                          CERTCertDBHandle *certdb,
379
                          SECOidTag digestalg,
380
                          SECItem *digest,
381
                          SECKEYGetPasswordKey pwfn, void *pwfn_arg)
382
0
{
383
0
    SEC_PKCS7ContentInfo *cinfo;
384
0
    SECStatus rv;
385
0
386
0
    cinfo = sec_pkcs7_create_signed_data(pwfn, pwfn_arg);
387
0
    if (cinfo == NULL)
388
0
        return NULL;
389
0
390
0
    rv = sec_pkcs7_add_signer(cinfo, cert, certusage, certdb,
391
0
                              digestalg, digest);
392
0
    if (rv != SECSuccess) {
393
0
        SEC_PKCS7DestroyContentInfo(cinfo);
394
0
        return NULL;
395
0
    }
396
0
397
0
    return cinfo;
398
0
}
399
400
static SEC_PKCS7Attribute *
401
sec_pkcs7_create_attribute(PLArenaPool *poolp, SECOidTag oidtag,
402
                           SECItem *value, PRBool encoded)
403
0
{
404
0
    SEC_PKCS7Attribute *attr;
405
0
    SECItem **values;
406
0
    void *mark;
407
0
408
0
    PORT_Assert(poolp != NULL);
409
0
    mark = PORT_ArenaMark(poolp);
410
0
411
0
    attr = (SEC_PKCS7Attribute *)PORT_ArenaAlloc(poolp,
412
0
                                                 sizeof(SEC_PKCS7Attribute));
413
0
    if (attr == NULL)
414
0
        goto loser;
415
0
416
0
    attr->typeTag = SECOID_FindOIDByTag(oidtag);
417
0
    if (attr->typeTag == NULL)
418
0
        goto loser;
419
0
420
0
    if (SECITEM_CopyItem(poolp, &(attr->type),
421
0
                         &(attr->typeTag->oid)) != SECSuccess)
422
0
        goto loser;
423
0
424
0
    values = (SECItem **)PORT_ArenaAlloc(poolp, 2 * sizeof(SECItem *));
425
0
    if (values == NULL)
426
0
        goto loser;
427
0
428
0
    if (value != NULL) {
429
0
        SECItem *copy;
430
0
431
0
        copy = (SECItem *)PORT_ArenaAlloc(poolp, sizeof(SECItem));
432
0
        if (copy == NULL)
433
0
            goto loser;
434
0
435
0
        if (SECITEM_CopyItem(poolp, copy, value) != SECSuccess)
436
0
            goto loser;
437
0
438
0
        value = copy;
439
0
    }
440
0
441
0
    values[0] = value;
442
0
    values[1] = NULL;
443
0
    attr->values = values;
444
0
    attr->encoded = encoded;
445
0
446
0
    PORT_ArenaUnmark(poolp, mark);
447
0
    return attr;
448
0
449
0
loser:
450
0
    PORT_Assert(mark != NULL);
451
0
    PORT_ArenaRelease(poolp, mark);
452
0
    return NULL;
453
0
}
454
455
static SECStatus
456
sec_pkcs7_add_attribute(SEC_PKCS7ContentInfo *cinfo,
457
                        SEC_PKCS7Attribute ***attrsp,
458
                        SEC_PKCS7Attribute *attr)
459
0
{
460
0
    SEC_PKCS7Attribute **attrs;
461
0
    SECItem *ct_value;
462
0
    void *mark;
463
0
464
0
    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
465
0
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
466
0
        return SECFailure;
467
0
468
0
    attrs = *attrsp;
469
0
    if (attrs != NULL) {
470
0
        int count;
471
0
472
0
        /*
473
0
   * We already have some attributes, and just need to add this
474
0
   * new one.
475
0
   */
476
0
477
0
        /*
478
0
   * We should already have the *required* attributes, which were
479
0
   * created/added at the same time the first attribute was added.
480
0
   */
481
0
        PORT_Assert(sec_PKCS7FindAttribute(attrs,
482
0
                                           SEC_OID_PKCS9_CONTENT_TYPE,
483
0
                                           PR_FALSE) != NULL);
484
0
        PORT_Assert(sec_PKCS7FindAttribute(attrs,
485
0
                                           SEC_OID_PKCS9_MESSAGE_DIGEST,
486
0
                                           PR_FALSE) != NULL);
487
0
488
0
        for (count = 0; attrs[count] != NULL; count++)
489
0
            ;
490
0
        attrs = (SEC_PKCS7Attribute **)PORT_ArenaGrow(cinfo->poolp, attrs,
491
0
                                                      (count + 1) * sizeof(SEC_PKCS7Attribute *),
492
0
                                                      (count + 2) * sizeof(SEC_PKCS7Attribute *));
493
0
        if (attrs == NULL)
494
0
            return SECFailure;
495
0
496
0
        attrs[count] = attr;
497
0
        attrs[count + 1] = NULL;
498
0
        *attrsp = attrs;
499
0
500
0
        return SECSuccess;
501
0
    }
502
0
503
0
    /*
504
0
     * This is the first time an attribute is going in.
505
0
     * We need to create and add the required attributes, and then
506
0
     * we will also add in the one our caller gave us.
507
0
     */
508
0
509
0
    /*
510
0
     * There are 2 required attributes, plus the one our caller wants
511
0
     * to add, plus we always end with a NULL one.  Thus, four slots.
512
0
     */
513
0
    attrs = (SEC_PKCS7Attribute **)PORT_ArenaAlloc(cinfo->poolp,
514
0
                                                   4 * sizeof(SEC_PKCS7Attribute *));
515
0
    if (attrs == NULL)
516
0
        return SECFailure;
517
0
518
0
    mark = PORT_ArenaMark(cinfo->poolp);
519
0
520
0
    /*
521
0
     * First required attribute is the content type of the data
522
0
     * being signed.
523
0
     */
524
0
    ct_value = &(cinfo->content.signedData->contentInfo.contentType);
525
0
    attrs[0] = sec_pkcs7_create_attribute(cinfo->poolp,
526
0
                                          SEC_OID_PKCS9_CONTENT_TYPE,
527
0
                                          ct_value, PR_FALSE);
528
0
    /*
529
0
     * Second required attribute is the message digest of the data
530
0
     * being signed; we leave the value NULL for now (just create
531
0
     * the place for it to go), and the encoder will fill it in later.
532
0
     */
533
0
    attrs[1] = sec_pkcs7_create_attribute(cinfo->poolp,
534
0
                                          SEC_OID_PKCS9_MESSAGE_DIGEST,
535
0
                                          NULL, PR_FALSE);
536
0
    if (attrs[0] == NULL || attrs[1] == NULL) {
537
0
        PORT_ArenaRelease(cinfo->poolp, mark);
538
0
        return SECFailure;
539
0
    }
540
0
541
0
    attrs[2] = attr;
542
0
    attrs[3] = NULL;
543
0
    *attrsp = attrs;
544
0
545
0
    PORT_ArenaUnmark(cinfo->poolp, mark);
546
0
    return SECSuccess;
547
0
}
548
549
/*
550
 * Add the signing time to the authenticated (i.e. signed) attributes
551
 * of "cinfo".  This is expected to be included in outgoing signed
552
 * messages for email (S/MIME) but is likely useful in other situations.
553
 *
554
 * This should only be added once; a second call will either do
555
 * nothing or replace an old signing time with a newer one.
556
 *
557
 * XXX This will probably just shove the current time into "cinfo"
558
 * but it will not actually get signed until the entire item is
559
 * processed for encoding.  Is this (expected to be small) delay okay?
560
 *
561
 * "cinfo" should be of type signedData (the only kind of pkcs7 data
562
 * that is allowed authenticated attributes); SECFailure will be returned
563
 * if it is not.
564
 */
565
SECStatus
566
SEC_PKCS7AddSigningTime(SEC_PKCS7ContentInfo *cinfo)
567
0
{
568
0
    SEC_PKCS7SignerInfo **signerinfos;
569
0
    SEC_PKCS7Attribute *attr;
570
0
    SECItem stime;
571
0
    SECStatus rv;
572
0
    int si;
573
0
574
0
    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
575
0
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
576
0
        return SECFailure;
577
0
578
0
    signerinfos = cinfo->content.signedData->signerInfos;
579
0
580
0
    /* There has to be a signer, or it makes no sense. */
581
0
    if (signerinfos == NULL || signerinfos[0] == NULL)
582
0
        return SECFailure;
583
0
584
0
    rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
585
0
    if (rv != SECSuccess)
586
0
        return rv;
587
0
588
0
    attr = sec_pkcs7_create_attribute(cinfo->poolp,
589
0
                                      SEC_OID_PKCS9_SIGNING_TIME,
590
0
                                      &stime, PR_FALSE);
591
0
    SECITEM_FreeItem(&stime, PR_FALSE);
592
0
593
0
    if (attr == NULL)
594
0
        return SECFailure;
595
0
596
0
    rv = SECSuccess;
597
0
    for (si = 0; signerinfos[si] != NULL; si++) {
598
0
        SEC_PKCS7Attribute *oattr;
599
0
600
0
        oattr = sec_PKCS7FindAttribute(signerinfos[si]->authAttr,
601
0
                                       SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
602
0
        PORT_Assert(oattr == NULL);
603
0
        if (oattr != NULL)
604
0
            continue; /* XXX or would it be better to replace it? */
605
0
606
0
        rv = sec_pkcs7_add_attribute(cinfo, &(signerinfos[si]->authAttr),
607
0
                                     attr);
608
0
        if (rv != SECSuccess)
609
0
            break; /* could try to continue, but may as well give up now */
610
0
    }
611
0
612
0
    return rv;
613
0
}
614
615
/*
616
 * Add the specified attribute to the authenticated (i.e. signed) attributes
617
 * of "cinfo" -- "oidtag" describes the attribute and "value" is the
618
 * value to be associated with it.  NOTE! "value" must already be encoded;
619
 * no interpretation of "oidtag" is done.  Also, it is assumed that this
620
 * signedData has only one signer -- if we ever need to add attributes
621
 * when there is more than one signature, we need a way to specify *which*
622
 * signature should get the attribute.
623
 *
624
 * XXX Technically, a signed attribute can have multiple values; if/when
625
 * we ever need to support an attribute which takes multiple values, we
626
 * either need to change this interface or create an AddSignedAttributeValue
627
 * which can be called subsequently, and would then append a value.
628
 *
629
 * "cinfo" should be of type signedData (the only kind of pkcs7 data
630
 * that is allowed authenticated attributes); SECFailure will be returned
631
 * if it is not.
632
 */
633
SECStatus
634
SEC_PKCS7AddSignedAttribute(SEC_PKCS7ContentInfo *cinfo,
635
                            SECOidTag oidtag,
636
                            SECItem *value)
637
0
{
638
0
    SEC_PKCS7SignerInfo **signerinfos;
639
0
    SEC_PKCS7Attribute *attr;
640
0
641
0
    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
642
0
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
643
0
        return SECFailure;
644
0
645
0
    signerinfos = cinfo->content.signedData->signerInfos;
646
0
647
0
    /*
648
0
     * No signature or more than one means no deal.
649
0
     */
650
0
    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
651
0
        return SECFailure;
652
0
653
0
    attr = sec_pkcs7_create_attribute(cinfo->poolp, oidtag, value, PR_TRUE);
654
0
    if (attr == NULL)
655
0
        return SECFailure;
656
0
657
0
    return sec_pkcs7_add_attribute(cinfo, &(signerinfos[0]->authAttr), attr);
658
0
}
659
660
/*
661
 * Mark that the signer certificates and their issuing chain should
662
 * be included in the encoded data.  This is expected to be used
663
 * in outgoing signed messages for email (S/MIME).
664
 *
665
 * "certdb" is the cert database to use for finding the chain.
666
 * It can be NULL, meaning use the default database.
667
 *
668
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
669
 * SECFailure will be returned if it is not.
670
 */
671
SECStatus
672
SEC_PKCS7IncludeCertChain(SEC_PKCS7ContentInfo *cinfo,
673
                          CERTCertDBHandle *certdb)
674
0
{
675
0
    SECOidTag kind;
676
0
    SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
677
0
678
0
    kind = SEC_PKCS7ContentType(cinfo);
679
0
    switch (kind) {
680
0
        case SEC_OID_PKCS7_SIGNED_DATA:
681
0
            signerinfos = cinfo->content.signedData->signerInfos;
682
0
            break;
683
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
684
0
            signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
685
0
            break;
686
0
        default:
687
0
            return SECFailure; /* XXX set an error? */
688
0
    }
689
0
690
0
    if (signerinfos == NULL) /* no signer, no certs? */
691
0
        return SECFailure;   /* XXX set an error? */
692
0
693
0
    if (certdb == NULL) {
694
0
        certdb = CERT_GetDefaultCertDB();
695
0
        if (certdb == NULL) {
696
0
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
697
0
            return SECFailure;
698
0
        }
699
0
    }
700
0
701
0
    /* XXX Should it be an error if we find no signerinfo or no certs? */
702
0
    while ((signerinfo = *signerinfos++) != NULL) {
703
0
        if (signerinfo->cert != NULL)
704
0
            /* get the cert chain.  don't send the root to avoid contamination
705
0
       * of old clients with a new root that they don't trust
706
0
       */
707
0
            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
708
0
                                                          certUsageEmailSigner,
709
0
                                                          PR_FALSE);
710
0
    }
711
0
712
0
    return SECSuccess;
713
0
}
714
715
/*
716
 * Helper function to add a certificate chain for inclusion in the
717
 * bag of certificates in a signedData.
718
 */
719
static SECStatus
720
sec_pkcs7_add_cert_chain(SEC_PKCS7ContentInfo *cinfo,
721
                         CERTCertificate *cert,
722
                         CERTCertDBHandle *certdb)
723
0
{
724
0
    SECOidTag kind;
725
0
    CERTCertificateList *certlist, **certlists, ***certlistsp;
726
0
    int count;
727
0
728
0
    kind = SEC_PKCS7ContentType(cinfo);
729
0
    switch (kind) {
730
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
731
0
            SEC_PKCS7SignedData *sdp;
732
0
733
0
            sdp = cinfo->content.signedData;
734
0
            certlistsp = &(sdp->certLists);
735
0
        } break;
736
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
737
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
738
0
739
0
            saedp = cinfo->content.signedAndEnvelopedData;
740
0
            certlistsp = &(saedp->certLists);
741
0
        } break;
742
0
        default:
743
0
            return SECFailure; /* XXX set an error? */
744
0
    }
745
0
746
0
    if (certdb == NULL) {
747
0
        certdb = CERT_GetDefaultCertDB();
748
0
        if (certdb == NULL) {
749
0
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
750
0
            return SECFailure;
751
0
        }
752
0
    }
753
0
754
0
    certlist = CERT_CertChainFromCert(cert, certUsageEmailSigner, PR_FALSE);
755
0
    if (certlist == NULL)
756
0
        return SECFailure;
757
0
758
0
    certlists = *certlistsp;
759
0
    if (certlists == NULL) {
760
0
        count = 0;
761
0
        certlists = (CERTCertificateList **)PORT_ArenaAlloc(cinfo->poolp,
762
0
                                                            2 * sizeof(CERTCertificateList *));
763
0
    } else {
764
0
        for (count = 0; certlists[count] != NULL; count++)
765
0
            ;
766
0
        PORT_Assert(count); /* should be at least one already */
767
0
        certlists = (CERTCertificateList **)PORT_ArenaGrow(cinfo->poolp,
768
0
                                                           certlists,
769
0
                                                           (count + 1) * sizeof(CERTCertificateList *),
770
0
                                                           (count + 2) * sizeof(CERTCertificateList *));
771
0
    }
772
0
773
0
    if (certlists == NULL) {
774
0
        CERT_DestroyCertificateList(certlist);
775
0
        return SECFailure;
776
0
    }
777
0
778
0
    certlists[count] = certlist;
779
0
    certlists[count + 1] = NULL;
780
0
781
0
    *certlistsp = certlists;
782
0
783
0
    return SECSuccess;
784
0
}
785
786
/*
787
 * Helper function to add a certificate for inclusion in the bag of
788
 * certificates in a signedData.
789
 */
790
static SECStatus
791
sec_pkcs7_add_certificate(SEC_PKCS7ContentInfo *cinfo,
792
                          CERTCertificate *cert)
793
0
{
794
0
    SECOidTag kind;
795
0
    CERTCertificate **certs, ***certsp;
796
0
    int count;
797
0
798
0
    kind = SEC_PKCS7ContentType(cinfo);
799
0
    switch (kind) {
800
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
801
0
            SEC_PKCS7SignedData *sdp;
802
0
803
0
            sdp = cinfo->content.signedData;
804
0
            certsp = &(sdp->certs);
805
0
        } break;
806
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
807
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
808
0
809
0
            saedp = cinfo->content.signedAndEnvelopedData;
810
0
            certsp = &(saedp->certs);
811
0
        } break;
812
0
        default:
813
0
            return SECFailure; /* XXX set an error? */
814
0
    }
815
0
816
0
    cert = CERT_DupCertificate(cert);
817
0
    if (cert == NULL)
818
0
        return SECFailure;
819
0
820
0
    certs = *certsp;
821
0
    if (certs == NULL) {
822
0
        count = 0;
823
0
        certs = (CERTCertificate **)PORT_ArenaAlloc(cinfo->poolp,
824
0
                                                    2 * sizeof(CERTCertificate *));
825
0
    } else {
826
0
        for (count = 0; certs[count] != NULL; count++)
827
0
            ;
828
0
        PORT_Assert(count); /* should be at least one already */
829
0
        certs = (CERTCertificate **)PORT_ArenaGrow(cinfo->poolp, certs,
830
0
                                                   (count + 1) * sizeof(CERTCertificate *),
831
0
                                                   (count + 2) * sizeof(CERTCertificate *));
832
0
    }
833
0
834
0
    if (certs == NULL) {
835
0
        CERT_DestroyCertificate(cert);
836
0
        return SECFailure;
837
0
    }
838
0
839
0
    certs[count] = cert;
840
0
    certs[count + 1] = NULL;
841
0
842
0
    *certsp = certs;
843
0
844
0
    return SECSuccess;
845
0
}
846
847
/*
848
 * Create a PKCS7 certs-only container.
849
 *
850
 * "cert" is the (first) cert that will be included.
851
 *
852
 * "include_chain" specifies whether the entire chain for "cert" should
853
 * be included.
854
 *
855
 * "certdb" is the cert database to use for finding the chain.
856
 * It can be NULL in when "include_chain" is false, or when meaning
857
 * use the default database.
858
 *
859
 * More certs and chains can be added via AddCertificate and AddCertChain.
860
 *
861
 * An error results in a return value of NULL and an error set.
862
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
863
 */
864
SEC_PKCS7ContentInfo *
865
SEC_PKCS7CreateCertsOnly(CERTCertificate *cert,
866
                         PRBool include_chain,
867
                         CERTCertDBHandle *certdb)
868
0
{
869
0
    SEC_PKCS7ContentInfo *cinfo;
870
0
    SECStatus rv;
871
0
872
0
    cinfo = sec_pkcs7_create_signed_data(NULL, NULL);
873
0
    if (cinfo == NULL)
874
0
        return NULL;
875
0
876
0
    if (include_chain)
877
0
        rv = sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
878
0
    else
879
0
        rv = sec_pkcs7_add_certificate(cinfo, cert);
880
0
881
0
    if (rv != SECSuccess) {
882
0
        SEC_PKCS7DestroyContentInfo(cinfo);
883
0
        return NULL;
884
0
    }
885
0
886
0
    return cinfo;
887
0
}
888
889
/*
890
 * Add "cert" and its entire chain to the set of certs included in "cinfo".
891
 *
892
 * "certdb" is the cert database to use for finding the chain.
893
 * It can be NULL, meaning use the default database.
894
 *
895
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
896
 * SECFailure will be returned if it is not.
897
 */
898
SECStatus
899
SEC_PKCS7AddCertChain(SEC_PKCS7ContentInfo *cinfo,
900
                      CERTCertificate *cert,
901
                      CERTCertDBHandle *certdb)
902
0
{
903
0
    SECOidTag kind;
904
0
905
0
    kind = SEC_PKCS7ContentType(cinfo);
906
0
    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
907
0
        return SECFailure; /* XXX set an error? */
908
0
909
0
    return sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
910
0
}
911
912
/*
913
 * Add "cert" to the set of certs included in "cinfo".
914
 *
915
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
916
 * SECFailure will be returned if it is not.
917
 */
918
SECStatus
919
SEC_PKCS7AddCertificate(SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
920
0
{
921
0
    SECOidTag kind;
922
0
923
0
    kind = SEC_PKCS7ContentType(cinfo);
924
0
    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
925
0
        return SECFailure; /* XXX set an error? */
926
0
927
0
    return sec_pkcs7_add_certificate(cinfo, cert);
928
0
}
929
930
static SECStatus
931
sec_pkcs7_init_encrypted_content_info(SEC_PKCS7EncryptedContentInfo *enccinfo,
932
                                      PLArenaPool *poolp,
933
                                      SECOidTag kind, PRBool detached,
934
                                      SECOidTag encalg, int keysize)
935
0
{
936
0
    SECStatus rv;
937
0
938
0
    PORT_Assert(enccinfo != NULL && poolp != NULL);
939
0
    if (enccinfo == NULL || poolp == NULL)
940
0
        return SECFailure;
941
0
942
0
    /*
943
0
     * XXX Some day we may want to allow for other kinds.  That needs
944
0
     * more work and modifications to the creation interface, etc.
945
0
     * For now, allow but notice callers who pass in other kinds.
946
0
     * They are responsible for creating the inner type and encoding,
947
0
     * if it is other than DATA.
948
0
     */
949
0
    PORT_Assert(kind == SEC_OID_PKCS7_DATA);
950
0
951
0
    enccinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
952
0
    PORT_Assert(enccinfo->contentTypeTag && enccinfo->contentTypeTag->offset == kind);
953
0
954
0
    rv = SECITEM_CopyItem(poolp, &(enccinfo->contentType),
955
0
                          &(enccinfo->contentTypeTag->oid));
956
0
    if (rv != SECSuccess)
957
0
        return rv;
958
0
959
0
    /* Save keysize and algorithm for later. */
960
0
    enccinfo->keysize = keysize;
961
0
    enccinfo->encalg = encalg;
962
0
963
0
    return SECSuccess;
964
0
}
965
966
/*
967
 * Add a recipient to a PKCS7 thing, verifying their cert first.
968
 * Any error returns SECFailure.
969
 */
970
static SECStatus
971
sec_pkcs7_add_recipient(SEC_PKCS7ContentInfo *cinfo,
972
                        CERTCertificate *cert,
973
                        SECCertUsage certusage,
974
                        CERTCertDBHandle *certdb)
975
0
{
976
0
    SECOidTag kind;
977
0
    SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
978
0
    SECItem *dummy;
979
0
    void *mark;
980
0
    int count;
981
0
982
0
    kind = SEC_PKCS7ContentType(cinfo);
983
0
    switch (kind) {
984
0
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
985
0
            SEC_PKCS7EnvelopedData *edp;
986
0
987
0
            edp = cinfo->content.envelopedData;
988
0
            recipientinfosp = &(edp->recipientInfos);
989
0
        } break;
990
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
991
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
992
0
993
0
            saedp = cinfo->content.signedAndEnvelopedData;
994
0
            recipientinfosp = &(saedp->recipientInfos);
995
0
        } break;
996
0
        default:
997
0
            return SECFailure; /* XXX set an error? */
998
0
    }
999
0
1000
0
    /*
1001
0
     * XXX I think that CERT_VerifyCert should do this if *it* is passed
1002
0
     * a NULL database.
1003
0
     */
1004
0
    if (certdb == NULL) {
1005
0
        certdb = CERT_GetDefaultCertDB();
1006
0
        if (certdb == NULL)
1007
0
            return SECFailure; /* XXX set an error? */
1008
0
    }
1009
0
1010
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
1011
0
                        cinfo->pwfn_arg, NULL) != SECSuccess) {
1012
0
        /* XXX Did CERT_VerifyCert set an error? */
1013
0
        return SECFailure;
1014
0
    }
1015
0
1016
0
    mark = PORT_ArenaMark(cinfo->poolp);
1017
0
1018
0
    recipientinfo = (SEC_PKCS7RecipientInfo *)PORT_ArenaZAlloc(cinfo->poolp,
1019
0
                                                               sizeof(SEC_PKCS7RecipientInfo));
1020
0
    if (recipientinfo == NULL) {
1021
0
        PORT_ArenaRelease(cinfo->poolp, mark);
1022
0
        return SECFailure;
1023
0
    }
1024
0
1025
0
    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &recipientinfo->version,
1026
0
                                  SEC_PKCS7_RECIPIENT_INFO_VERSION);
1027
0
    if (dummy == NULL) {
1028
0
        PORT_ArenaRelease(cinfo->poolp, mark);
1029
0
        return SECFailure;
1030
0
    }
1031
0
    PORT_Assert(dummy == &recipientinfo->version);
1032
0
1033
0
    recipientinfo->cert = CERT_DupCertificate(cert);
1034
0
    if (recipientinfo->cert == NULL) {
1035
0
        PORT_ArenaRelease(cinfo->poolp, mark);
1036
0
        return SECFailure;
1037
0
    }
1038
0
1039
0
    recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
1040
0
    if (recipientinfo->issuerAndSN == NULL) {
1041
0
        PORT_ArenaRelease(cinfo->poolp, mark);
1042
0
        return SECFailure;
1043
0
    }
1044
0
1045
0
    /*
1046
0
     * Okay, now recipientinfo is all set.  We just need to put it into
1047
0
     * the main structure.
1048
0
     *
1049
0
     * If this is the first recipient, allocate a new recipientinfos array;
1050
0
     * otherwise, reallocate the array, making room for the new entry.
1051
0
     */
1052
0
    recipientinfos = *recipientinfosp;
1053
0
    if (recipientinfos == NULL) {
1054
0
        count = 0;
1055
0
        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc(
1056
0
            cinfo->poolp,
1057
0
            2 * sizeof(SEC_PKCS7RecipientInfo *));
1058
0
    } else {
1059
0
        for (count = 0; recipientinfos[count] != NULL; count++)
1060
0
            ;
1061
0
        PORT_Assert(count); /* should be at least one already */
1062
0
        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow(
1063
0
            cinfo->poolp, recipientinfos,
1064
0
            (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
1065
0
            (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
1066
0
    }
1067
0
1068
0
    if (recipientinfos == NULL) {
1069
0
        PORT_ArenaRelease(cinfo->poolp, mark);
1070
0
        return SECFailure;
1071
0
    }
1072
0
1073
0
    recipientinfos[count] = recipientinfo;
1074
0
    recipientinfos[count + 1] = NULL;
1075
0
1076
0
    *recipientinfosp = recipientinfos;
1077
0
1078
0
    PORT_ArenaUnmark(cinfo->poolp, mark);
1079
0
    return SECSuccess;
1080
0
}
1081
1082
/*
1083
 * Start a PKCS7 enveloping context.
1084
 *
1085
 * "cert" is the cert for the recipient.  It will be checked for validity.
1086
 *
1087
 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1088
 * XXX Maybe SECCertUsage should be split so that our caller just says
1089
 * "email" and *we* add the "recipient" part -- otherwise our caller
1090
 * could be lying about the usage; we do not want to allow encryption
1091
 * certs for signing or vice versa.
1092
 *
1093
 * "certdb" is the cert database to use for verifying the cert.
1094
 * It can be NULL if a default database is available (like in the client).
1095
 *
1096
 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
1097
 *
1098
 * "keysize" specifies the bulk encryption key size, in bits.
1099
 *
1100
 * The return value can be passed to functions which add things to
1101
 * it like more recipients, then eventually to SEC_PKCS7Encode() or to
1102
 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
1103
 * SEC_PKCS7DestroyContentInfo().
1104
 *
1105
 * An error results in a return value of NULL and an error set.
1106
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1107
 */
1108
extern SEC_PKCS7ContentInfo *
1109
SEC_PKCS7CreateEnvelopedData(CERTCertificate *cert,
1110
                             SECCertUsage certusage,
1111
                             CERTCertDBHandle *certdb,
1112
                             SECOidTag encalg,
1113
                             int keysize,
1114
                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1115
0
{
1116
0
    SEC_PKCS7ContentInfo *cinfo;
1117
0
    SEC_PKCS7EnvelopedData *envd;
1118
0
    SECStatus rv;
1119
0
1120
0
    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENVELOPED_DATA,
1121
0
                                          PR_FALSE, pwfn, pwfn_arg);
1122
0
    if (cinfo == NULL)
1123
0
        return NULL;
1124
0
1125
0
    rv = sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
1126
0
    if (rv != SECSuccess) {
1127
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1128
0
        return NULL;
1129
0
    }
1130
0
1131
0
    envd = cinfo->content.envelopedData;
1132
0
    PORT_Assert(envd != NULL);
1133
0
1134
0
    /*
1135
0
     * XXX Might we want to allow content types other than data?
1136
0
     * If so, via what interface?
1137
0
     */
1138
0
    rv = sec_pkcs7_init_encrypted_content_info(&(envd->encContentInfo),
1139
0
                                               cinfo->poolp,
1140
0
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
1141
0
                                               encalg, keysize);
1142
0
    if (rv != SECSuccess) {
1143
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1144
0
        return NULL;
1145
0
    }
1146
0
1147
0
    /* XXX Anything more to do here? */
1148
0
1149
0
    return cinfo;
1150
0
}
1151
1152
/*
1153
 * Add another recipient to an encrypted message.
1154
 *
1155
 * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
1156
 * SECFailure will be returned if it is not.
1157
 *
1158
 * "cert" is the cert for the recipient.  It will be checked for validity.
1159
 *
1160
 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1161
 * XXX Maybe SECCertUsage should be split so that our caller just says
1162
 * "email" and *we* add the "recipient" part -- otherwise our caller
1163
 * could be lying about the usage; we do not want to allow encryption
1164
 * certs for signing or vice versa.
1165
 *
1166
 * "certdb" is the cert database to use for verifying the cert.
1167
 * It can be NULL if a default database is available (like in the client).
1168
 */
1169
SECStatus
1170
SEC_PKCS7AddRecipient(SEC_PKCS7ContentInfo *cinfo,
1171
                      CERTCertificate *cert,
1172
                      SECCertUsage certusage,
1173
                      CERTCertDBHandle *certdb)
1174
0
{
1175
0
    return sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
1176
0
}
1177
1178
/*
1179
 * Create an empty PKCS7 data content info.
1180
 *
1181
 * An error results in a return value of NULL and an error set.
1182
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1183
 */
1184
SEC_PKCS7ContentInfo *
1185
SEC_PKCS7CreateData(void)
1186
0
{
1187
0
    return sec_pkcs7_create_content_info(SEC_OID_PKCS7_DATA, PR_FALSE,
1188
0
                                         NULL, NULL);
1189
0
}
1190
1191
/*
1192
 * Create an empty PKCS7 encrypted content info.
1193
 *
1194
 * "algorithm" specifies the bulk encryption algorithm to use.
1195
 * 
1196
 * An error results in a return value of NULL and an error set.
1197
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1198
 */
1199
SEC_PKCS7ContentInfo *
1200
SEC_PKCS7CreateEncryptedData(SECOidTag algorithm, int keysize,
1201
                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1202
0
{
1203
0
    SEC_PKCS7ContentInfo *cinfo;
1204
0
    SECAlgorithmID *algid;
1205
0
    SEC_PKCS7EncryptedData *enc_data;
1206
0
    SECStatus rv;
1207
0
1208
0
    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
1209
0
                                          PR_FALSE, pwfn, pwfn_arg);
1210
0
    if (cinfo == NULL)
1211
0
        return NULL;
1212
0
1213
0
    enc_data = cinfo->content.encryptedData;
1214
0
    algid = &(enc_data->encContentInfo.contentEncAlg);
1215
0
1216
0
    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
1217
0
        rv = SECOID_SetAlgorithmID(cinfo->poolp, algid, algorithm, NULL);
1218
0
    } else {
1219
0
        /* Assume password-based-encryption.  
1220
0
         * Note: we can't generate pkcs5v2 from this interface.
1221
0
         * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
1222
0
         * non-PBE oids and assuming that they are pkcs5v2 oids, but
1223
0
         * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
1224
0
         * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
1225
0
         * to create pkcs5v2 PBEs */
1226
0
        SECAlgorithmID *pbe_algid;
1227
0
        pbe_algid = PK11_CreatePBEAlgorithmID(algorithm,
1228
0
                                              NSS_PBE_DEFAULT_ITERATION_COUNT,
1229
0
                                              NULL);
1230
0
        if (pbe_algid == NULL) {
1231
0
            rv = SECFailure;
1232
0
        } else {
1233
0
            rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
1234
0
            SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
1235
0
        }
1236
0
    }
1237
0
1238
0
    if (rv != SECSuccess) {
1239
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1240
0
        return NULL;
1241
0
    }
1242
0
1243
0
    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
1244
0
                                               cinfo->poolp,
1245
0
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
1246
0
                                               algorithm, keysize);
1247
0
    if (rv != SECSuccess) {
1248
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1249
0
        return NULL;
1250
0
    }
1251
0
1252
0
    return cinfo;
1253
0
}
1254
1255
SEC_PKCS7ContentInfo *
1256
SEC_PKCS7CreateEncryptedDataWithPBEV2(SECOidTag pbe_algorithm,
1257
                                      SECOidTag cipher_algorithm,
1258
                                      SECOidTag prf_algorithm,
1259
                                      int keysize,
1260
                                      SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1261
0
{
1262
0
    SEC_PKCS7ContentInfo *cinfo;
1263
0
    SECAlgorithmID *algid;
1264
0
    SEC_PKCS7EncryptedData *enc_data;
1265
0
    SECStatus rv;
1266
0
1267
0
    PORT_Assert(SEC_PKCS5IsAlgorithmPBEAlgTag(pbe_algorithm));
1268
0
1269
0
    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
1270
0
                                          PR_FALSE, pwfn, pwfn_arg);
1271
0
    if (cinfo == NULL)
1272
0
        return NULL;
1273
0
1274
0
    enc_data = cinfo->content.encryptedData;
1275
0
    algid = &(enc_data->encContentInfo.contentEncAlg);
1276
0
1277
0
    SECAlgorithmID *pbe_algid;
1278
0
    pbe_algid = PK11_CreatePBEV2AlgorithmID(pbe_algorithm,
1279
0
                                            cipher_algorithm,
1280
0
                                            prf_algorithm,
1281
0
                                            keysize,
1282
0
                                            NSS_PBE_DEFAULT_ITERATION_COUNT,
1283
0
                                            NULL);
1284
0
    if (pbe_algid == NULL) {
1285
0
        rv = SECFailure;
1286
0
    } else {
1287
0
        rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
1288
0
        SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
1289
0
    }
1290
0
1291
0
    if (rv != SECSuccess) {
1292
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1293
0
        return NULL;
1294
0
    }
1295
0
1296
0
    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
1297
0
                                               cinfo->poolp,
1298
0
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
1299
0
                                               cipher_algorithm, keysize);
1300
0
    if (rv != SECSuccess) {
1301
0
        SEC_PKCS7DestroyContentInfo(cinfo);
1302
0
        return NULL;
1303
0
    }
1304
0
1305
0
    return cinfo;
1306
0
}