Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/*
* PKCS7 encoding.
*/
#include "p7local.h"
#include "cert.h"
#include "cryptohi.h"
#include "keyhi.h"
#include "secasn1.h"
#include "secoid.h"
#include "secitem.h"
#include "pk11func.h"
#include "secerr.h"
#include "sechash.h" /* for HASH_GetHashObject() */
struct sec_pkcs7_encoder_output {
SEC_PKCS7EncoderOutputCallback outputfn;
void *outputarg;
};
struct SEC_PKCS7EncoderContextStr {
SEC_ASN1EncoderContext *ecx;
SEC_PKCS7ContentInfo *cinfo;
struct sec_pkcs7_encoder_output output;
sec_PKCS7CipherObject *encryptobj;
const SECHashObject *digestobj;
void *digestcx;
};
/*
* The little output function that the ASN.1 encoder calls to hand
* us bytes which we in turn hand back to our caller (via the callback
* they gave us).
*/
static void
sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind)
{
struct sec_pkcs7_encoder_output *output;
output = (struct sec_pkcs7_encoder_output *)arg;
output->outputfn(output->outputarg, buf, len);
}
static sec_PKCS7CipherObject *
sec_pkcs7_encoder_start_encrypt(SEC_PKCS7ContentInfo *cinfo,
PK11SymKey *orig_bulkkey)
{
SECOidTag kind;
sec_PKCS7CipherObject *encryptobj;
SEC_PKCS7RecipientInfo **recipientinfos, *ri;
SEC_PKCS7EncryptedContentInfo *enccinfo;
SECKEYPublicKey *publickey = NULL;
SECKEYPrivateKey *ourPrivKey = NULL;
PK11SymKey *bulkkey;
void *mark;
int i;
PLArenaPool *arena = NULL;
kind = SEC_PKCS7ContentType(cinfo);
switch (kind) {
default:
case SEC_OID_PKCS7_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_SIGNED_DATA:
recipientinfos = NULL;
enccinfo = NULL;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA: {
SEC_PKCS7EncryptedData *encdp;
/* To do EncryptedData we *must* be given a bulk key. */
PORT_Assert(orig_bulkkey != NULL);
if (orig_bulkkey == NULL) {
/* XXX error? */
return NULL;
}
encdp = cinfo->content.encryptedData;
recipientinfos = NULL;
enccinfo = &(encdp->encContentInfo);
} break;
case SEC_OID_PKCS7_ENVELOPED_DATA: {
SEC_PKCS7EnvelopedData *envdp;
envdp = cinfo->content.envelopedData;
recipientinfos = envdp->recipientInfos;
enccinfo = &(envdp->encContentInfo);
} break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
SEC_PKCS7SignedAndEnvelopedData *saedp;
saedp = cinfo->content.signedAndEnvelopedData;
recipientinfos = saedp->recipientInfos;
enccinfo = &(saedp->encContentInfo);
} break;
}
if (enccinfo == NULL)
return NULL;
bulkkey = orig_bulkkey;
if (bulkkey == NULL) {
CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg);
PK11SlotInfo *slot;
slot = PK11_GetBestSlot(type, cinfo->pwfn_arg);
if (slot == NULL) {
return NULL;
}
bulkkey = PK11_KeyGen(slot, type, NULL, enccinfo->keysize / 8,
cinfo->pwfn_arg);
PK11_FreeSlot(slot);
if (bulkkey == NULL) {
return NULL;
}
}
encryptobj = NULL;
mark = PORT_ArenaMark(cinfo->poolp);
/*
* Encrypt the bulk key with the public key of each recipient.
*/
for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) {
CERTCertificate *cert;
SECOidTag certalgtag, encalgtag;
SECStatus rv;
int data_len;
SECItem *params = NULL;
cert = ri->cert;
PORT_Assert(cert != NULL);
if (cert == NULL)
continue;
/*
* XXX Want an interface that takes a cert and some data and
* fills in an algorithmID and encrypts the data with the public
* key from the cert. Or, give me two interfaces -- one which
* gets the algorithm tag from a cert (I should not have to go
* down into the subjectPublicKeyInfo myself) and another which
* takes a public key and algorithm tag and data and encrypts
* the data. Or something like that. The point is that all
* of the following hardwired RSA stuff should be done elsewhere.
*/
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
switch (certalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
encalgtag = certalgtag;
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL)
goto loser;
data_len = SECKEY_PublicKeyStrength(publickey);
ri->encKey.data =
(unsigned char *)PORT_ArenaAlloc(cinfo->poolp, data_len);
ri->encKey.len = data_len;
if (ri->encKey.data == NULL)
goto loser;
rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag), publickey,
bulkkey, &ri->encKey);
SECKEY_DestroyPublicKey(publickey);
publickey = NULL;
if (rv != SECSuccess)
goto loser;
params = NULL; /* paranoia */
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
goto loser;
}
rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag,
params);
if (rv != SECSuccess)
goto loser;
if (arena)
PORT_FreeArena(arena, PR_FALSE);
arena = NULL;
}
encryptobj = sec_PKCS7CreateEncryptObject(cinfo->poolp, bulkkey,
enccinfo->encalg,
&(enccinfo->contentEncAlg));
if (encryptobj != NULL) {
PORT_ArenaUnmark(cinfo->poolp, mark);
mark = NULL; /* good one; do not want to release */
}
/* fallthru */
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (publickey) {
SECKEY_DestroyPublicKey(publickey);
}
if (ourPrivKey) {
SECKEY_DestroyPrivateKey(ourPrivKey);
}
if (mark != NULL) {
PORT_ArenaRelease(cinfo->poolp, mark);
}
if (orig_bulkkey == NULL) {
if (bulkkey)
PK11_FreeSymKey(bulkkey);
}
return encryptobj;
}
static void
sec_pkcs7_encoder_notify(void *arg, PRBool before, void *dest, int depth)
{
SEC_PKCS7EncoderContext *p7ecx;
SEC_PKCS7ContentInfo *cinfo;
SECOidTag kind;
PRBool before_content;
/*
* We want to notice just before the content field. After fields are
* not interesting to us.
*/
if (!before)
return;
p7ecx = (SEC_PKCS7EncoderContext *)arg;
cinfo = p7ecx->cinfo;
before_content = PR_FALSE;
/*
* Watch for the content field, at which point we want to instruct
* the ASN.1 encoder to start taking bytes from the buffer.
*
* XXX The following assumes the inner content type is data;
* if/when we want to handle fully nested types, this will have
* to recurse until reaching the innermost data content.
*/
kind = SEC_PKCS7ContentType(cinfo);
switch (kind) {
default:
case SEC_OID_PKCS7_DATA:
if (dest == &(cinfo->content.data))
before_content = PR_TRUE;
break;
case SEC_OID_PKCS7_DIGESTED_DATA: {
SEC_PKCS7DigestedData *digd;
digd = cinfo->content.digestedData;
if (digd == NULL)
break;
if (dest == &(digd->contentInfo.content))
before_content = PR_TRUE;
} break;
case SEC_OID_PKCS7_ENCRYPTED_DATA: {
SEC_PKCS7EncryptedData *encd;
encd = cinfo->content.encryptedData;
if (encd == NULL)
break;
if (dest == &(encd->encContentInfo.encContent))
before_content = PR_TRUE;
} break;
case SEC_OID_PKCS7_ENVELOPED_DATA: {
SEC_PKCS7EnvelopedData *envd;
envd = cinfo->content.envelopedData;
if (envd == NULL)
break;
if (dest == &(envd->encContentInfo.encContent))
before_content = PR_TRUE;
} break;
case SEC_OID_PKCS7_SIGNED_DATA: {
SEC_PKCS7SignedData *sigd;
sigd = cinfo->content.signedData;
if (sigd == NULL)
break;
if (dest == &(sigd->contentInfo.content))
before_content = PR_TRUE;
} break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
SEC_PKCS7SignedAndEnvelopedData *saed;
saed = cinfo->content.signedAndEnvelopedData;
if (saed == NULL)
break;
if (dest == &(saed->encContentInfo.encContent))
before_content = PR_TRUE;
} break;
}
if (before_content) {
/*
* This will cause the next SEC_ASN1EncoderUpdate to take the
* contents bytes from the passed-in buffer.
*/
SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
/*
* And that is all we needed this notify function for.
*/
SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);
}
}
static SEC_PKCS7EncoderContext *
sec_pkcs7_encoder_start_contexts(SEC_PKCS7ContentInfo *cinfo,
PK11SymKey *bulkkey)
{
SEC_PKCS7EncoderContext *p7ecx;
SECOidTag kind;
PRBool encrypt;
SECItem **digests;
SECAlgorithmID *digestalg, **digestalgs;
p7ecx =
(SEC_PKCS7EncoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7EncoderContext));
if (p7ecx == NULL)
return NULL;
digests = NULL;
digestalg = NULL;
digestalgs = NULL;
encrypt = PR_FALSE;
kind = SEC_PKCS7ContentType(cinfo);
switch (kind) {
default:
case SEC_OID_PKCS7_DATA:
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
digestalg = &(cinfo->content.digestedData->digestAlg);
break;
case SEC_OID_PKCS7_SIGNED_DATA:
digests = cinfo->content.signedData->digests;
digestalgs = cinfo->content.signedData->digestAlgorithms;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
encrypt = PR_TRUE;
break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
digests = cinfo->content.signedAndEnvelopedData->digests;
digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms;
encrypt = PR_TRUE;
break;
}
if (encrypt) {
p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt(cinfo, bulkkey);
if (p7ecx->encryptobj == NULL) {
PORT_Free(p7ecx);
return NULL;
}
}
if (digestalgs != NULL) {
if (digests != NULL) {
/* digests already created (probably for detached data) */
digestalg = NULL;
} else {
/*
* XXX Some day we should handle multiple digests; for now,
* assume only one will be done.
*/
PORT_Assert(digestalgs[0] != NULL && digestalgs[1] == NULL);
digestalg = digestalgs[0];
}
}
if (digestalg != NULL) {
SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm));
p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag);
if (p7ecx->digestobj != NULL) {
p7ecx->digestcx = (*p7ecx->digestobj->create)();
if (p7ecx->digestcx == NULL)
p7ecx->digestobj = NULL;
else
(*p7ecx->digestobj->begin)(p7ecx->digestcx);
}
if (p7ecx->digestobj == NULL) {
if (p7ecx->encryptobj != NULL)
sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
PORT_Free(p7ecx);
return NULL;
}
}
p7ecx->cinfo = cinfo;
return p7ecx;
}
SEC_PKCS7EncoderContext *
SEC_PKCS7EncoderStart(SEC_PKCS7ContentInfo *cinfo,
SEC_PKCS7EncoderOutputCallback outputfn,
void *outputarg,
PK11SymKey *bulkkey)
{
SEC_PKCS7EncoderContext *p7ecx;
SECStatus rv;
p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
if (p7ecx == NULL)
return NULL;
p7ecx->output.outputfn = outputfn;
p7ecx->output.outputarg = outputarg;
/*
* Initialize the BER encoder.
*/
p7ecx->ecx = SEC_ASN1EncoderStart(cinfo, sec_PKCS7ContentInfoTemplate,
sec_pkcs7_encoder_out, &(p7ecx->output));
if (p7ecx->ecx == NULL) {
PORT_Free(p7ecx);
return NULL;
}
/*
* Indicate that we are streaming. We will be streaming until we
* get past the contents bytes.
*/
SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
/*
* The notify function will watch for the contents field.
*/
SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx);
/*
* This will encode everything up to the content bytes. (The notify
* function will then cause the encoding to stop there.) Then our
* caller can start passing contents bytes to our Update, which we
* will pass along.
*/
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
if (rv != SECSuccess) {
PORT_Free(p7ecx);
return NULL;
}
return p7ecx;
}
/*
* XXX If/when we support nested contents, this needs to be revised.
*/
static SECStatus
sec_pkcs7_encoder_work_data(SEC_PKCS7EncoderContext *p7ecx, SECItem *dest,
const unsigned char *data, unsigned long len,
PRBool final)
{
unsigned char *buf = NULL;
SECStatus rv;
rv = SECSuccess; /* may as well be optimistic */
/*
* We should really have data to process, or we should be trying
* to finish/flush the last block. (This is an overly paranoid
* check since all callers are in this file and simple inspection
* proves they do it right. But it could find a bug in future
* modifications/development, that is why it is here.)
*/
PORT_Assert((data != NULL && len) || final);
/*
* Update the running digest.
* XXX This needs modification if/when we handle multiple digests.
*/
if (len && p7ecx->digestobj != NULL) {
(*p7ecx->digestobj->update)(p7ecx->digestcx, data, len);
}
/*
* Encrypt this chunk.
*/
if (p7ecx->encryptobj != NULL) {
/* XXX the following lengths should all be longs? */
unsigned int inlen; /* length of data being encrypted */
unsigned int outlen; /* length of encrypted data */
unsigned int buflen; /* length available for encrypted data */
inlen = len;
buflen = sec_PKCS7EncryptLength(p7ecx->encryptobj, inlen, final);
if (buflen == 0) {
/*
* No output is expected, but the input data may be buffered
* so we still have to call Encrypt.
*/
rv = sec_PKCS7Encrypt(p7ecx->encryptobj, NULL, &outlen, 0,
data, inlen, final);
if (final) {
len = 0;
goto done;
}
return rv;
}
if (dest != NULL)
buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen);
else
buf = (unsigned char *)PORT_Alloc(buflen);
if (buf == NULL) {
rv = SECFailure;
} else {
rv = sec_PKCS7Encrypt(p7ecx->encryptobj, buf, &outlen, buflen,
data, inlen, final);
data = buf;
len = outlen;
}
if (rv != SECSuccess) {
if (final)
goto done;
return rv;
}
}
if (p7ecx->ecx != NULL) {
/*
* Encode the contents bytes.
*/
if (len) {
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
}
}
done:
if (p7ecx->encryptobj != NULL) {
if (final)
sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
if (dest != NULL) {
dest->data = buf;
dest->len = len;
} else if (buf != NULL) {
PORT_Free(buf);
}
}
if (final && p7ecx->digestobj != NULL) {
SECItem *digest, **digests, ***digestsp;
unsigned char *digdata;
SECOidTag kind;
kind = SEC_PKCS7ContentType(p7ecx->cinfo);
switch (kind) {
default:
PORT_Assert(0);
return SECFailure;
case SEC_OID_PKCS7_DIGESTED_DATA:
digest = &(p7ecx->cinfo->content.digestedData->digest);
digestsp = NULL;
break;
case SEC_OID_PKCS7_SIGNED_DATA:
digest = NULL;
digestsp = &(p7ecx->cinfo->content.signedData->digests);
break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
digest = NULL;
digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests);
break;
}
digdata = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
p7ecx->digestobj->length);
if (digdata == NULL)
return SECFailure;
if (digestsp != NULL) {
PORT_Assert(digest == NULL);
digest = (SECItem *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
sizeof(SECItem));
digests = (SECItem **)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
2 * sizeof(SECItem *));
if (digests == NULL || digest == NULL)
return SECFailure;
digests[0] = digest;
digests[1] = NULL;
*digestsp = digests;
}
PORT_Assert(digest != NULL);
digest->data = digdata;
digest->len = p7ecx->digestobj->length;
(*p7ecx->digestobj->end)(p7ecx->digestcx, digest->data,
&(digest->len), digest->len);
(*p7ecx->digestobj->destroy)(p7ecx->digestcx, PR_TRUE);
}
return rv;
}
SECStatus
SEC_PKCS7EncoderUpdate(SEC_PKCS7EncoderContext *p7ecx,
const char *data, unsigned long len)
{
/* XXX Error handling needs help. Return what? Do "Finish" on failure? */
return sec_pkcs7_encoder_work_data(p7ecx, NULL,
(const unsigned char *)data, len,
PR_FALSE);
}
static SECStatus
sec_pkcs7_encoder_sig_and_certs(SEC_PKCS7ContentInfo *cinfo,
SECKEYGetPasswordKey pwfn, void *pwfnarg)
{
SECOidTag kind;
CERTCertificate **certs;
CERTCertificateList **certlists;
SECAlgorithmID **digestalgs;
SECItem **digests;
SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
SECItem **rawcerts, ***rawcertsp;
PLArenaPool *poolp;
int certcount;
int ci, cli, rci, si;
kind = SEC_PKCS7ContentType(cinfo);
switch (kind) {
default:
case SEC_OID_PKCS7_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
certs = NULL;
certlists = NULL;
digestalgs = NULL;
digests = NULL;
signerinfos = NULL;
rawcertsp = NULL;
break;
case SEC_OID_PKCS7_SIGNED_DATA: {
SEC_PKCS7SignedData *sdp;
sdp = cinfo->content.signedData;
certs = sdp->certs;
certlists = sdp->certLists;
digestalgs = sdp->digestAlgorithms;
digests = sdp->digests;
signerinfos = sdp->signerInfos;
rawcertsp = &(sdp->rawCerts);
} break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
SEC_PKCS7SignedAndEnvelopedData *saedp;
saedp = cinfo->content.signedAndEnvelopedData;
certs = saedp->certs;
certlists = saedp->certLists;
digestalgs = saedp->digestAlgorithms;
digests = saedp->digests;
signerinfos = saedp->signerInfos;
rawcertsp = &(saedp->rawCerts);
} break;
}
if (certs == NULL && certlists == NULL && signerinfos == NULL)
return SECSuccess; /* nothing for us to do! */
poolp = cinfo->poolp;
certcount = 0;
if (signerinfos != NULL) {
SECOidTag digestalgtag;
int di;
SECStatus rv;
CERTCertificate *cert;
SECKEYPrivateKey *privkey;
SECItem signature;
SECOidTag signalgtag;
PORT_Assert(digestalgs != NULL && digests != NULL);
/*
* If one fails, we bail right then. If we want to continue and
* try to do subsequent signatures, this loop, and the departures
* from it, will need to be reworked.
*/
for (si = 0; signerinfos[si] != NULL; si++) {
signerinfo = signerinfos[si];
/* find right digest */
digestalgtag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
for (di = 0; digestalgs[di] != NULL; di++) {
/* XXX Should I be comparing more than the tag? */
if (digestalgtag == SECOID_GetAlgorithmTag(digestalgs[di]))
break;
}
if (digestalgs[di] == NULL) {
/* XXX oops; do what? set an error? */
return SECFailure;
}
PORT_Assert(digests[di] != NULL);
cert = signerinfo->cert;
privkey = PK11_FindKeyByAnyCert(cert, pwfnarg);
if (privkey == NULL)
return SECFailure;
/*
* XXX I think there should be a cert-level interface for this,
* so that I do not have to know about subjectPublicKeyInfo...
*/
signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
if (signerinfo->authAttr != NULL) {
SEC_PKCS7Attribute *attr;
SECItem encoded_attrs;
SECItem *dummy;
SECOidTag algid;
/*
* First, find and fill in the message digest attribute.
*/
attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
SEC_OID_PKCS9_MESSAGE_DIGEST,
PR_TRUE);
PORT_Assert(attr != NULL);
if (attr == NULL) {
SECKEY_DestroyPrivateKey(privkey);
return SECFailure;
}
/*
* XXX The second half of the following assertion prevents
* the encoder from being called twice on the same content.
* Either just remove the second half the assertion, or
* change the code to check if the value already there is
* the same as digests[di], whichever seems more right.
*/
PORT_Assert(attr->values != NULL && attr->values[0] == NULL);
attr->values[0] = digests[di];
/*
* Before encoding, reorder the attributes so that when they
* are encoded, they will be conforming DER, which is required
* to have a specific order and that is what must be used for
* the hash/signature. We do this here, rather than building
* it into EncodeAttributes, because we do not want to do
* such reordering on incoming messages (which also uses
* EncodeAttributes) or our old signatures (and other "broken"
* implementations) will not verify. So, we want to guarantee
* that we send out good DER encodings of attributes, but not
* to expect to receive them.
*/
rv = sec_PKCS7ReorderAttributes(signerinfo->authAttr);
if (rv != SECSuccess) {
SECKEY_DestroyPrivateKey(privkey);
return SECFailure;
}
encoded_attrs.data = NULL;
encoded_attrs.len = 0;
dummy = sec_PKCS7EncodeAttributes(NULL, &encoded_attrs,
&(signerinfo->authAttr));
if (dummy == NULL) {
SECKEY_DestroyPrivateKey(privkey);
return SECFailure;
}
algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType,
digestalgtag);
if (algid == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
SECKEY_DestroyPrivateKey(privkey);
return SECFailure;
}
rv = SEC_SignData(&signature,
encoded_attrs.data, encoded_attrs.len,
privkey,
algid);
SECITEM_FreeItem(&encoded_attrs, PR_FALSE);
} else {
rv = SGN_Digest(privkey, digestalgtag, &signature,
digests[di]);
}
SECKEY_DestroyPrivateKey(privkey);
if (rv != SECSuccess)
return rv;
rv = SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature);
if (rv != SECSuccess)
return rv;
SECITEM_FreeItem(&signature, PR_FALSE);
rv = SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
signalgtag, NULL);
if (rv != SECSuccess)
return SECFailure;
/*
* Count the cert chain for this signer.
*/
if (signerinfo->certList != NULL)
certcount += signerinfo->certList->len;
}
}
if (certs != NULL) {
for (ci = 0; certs[ci] != NULL; ci++)
certcount++;
}
if (certlists != NULL) {
for (cli = 0; certlists[cli] != NULL; cli++)
certcount += certlists[cli]->len;
}
if (certcount == 0)
return SECSuccess; /* signing done; no certs */
/*
* Combine all of the certs and cert chains into rawcerts.
* Note: certcount is an upper bound; we may not need that many slots
* but we will allocate anyway to avoid having to do another pass.
* (The temporary space saving is not worth it.)
*/
rawcerts = (SECItem **)PORT_ArenaAlloc(poolp,
(certcount + 1) * sizeof(SECItem *));
if (rawcerts == NULL)
return SECFailure;
/*
* XXX Want to check for duplicates and not add *any* cert that is
* already in the set. This will be more important when we start
* dealing with larger sets of certs, dual-key certs (signing and
* encryption), etc. For the time being we can slide by...
*/
rci = 0;
if (signerinfos != NULL) {
for (si = 0; signerinfos[si] != NULL; si++) {
signerinfo = signerinfos[si];
for (ci = 0; signerinfo->certList && ci < signerinfo->certList->len; ci++)
rawcerts[rci++] = &(signerinfo->certList->certs[ci]);
}
}
if (certs != NULL) {
for (ci = 0; certs[ci] != NULL; ci++)
rawcerts[rci++] = &(certs[ci]->derCert);
}
if (certlists != NULL) {
for (cli = 0; certlists[cli] != NULL; cli++) {
for (ci = 0; ci < certlists[cli]->len; ci++)
rawcerts[rci++] = &(certlists[cli]->certs[ci]);
}
}
rawcerts[rci] = NULL;
*rawcertsp = rawcerts;
return SECSuccess;
}
SECStatus
SEC_PKCS7EncoderFinish(SEC_PKCS7EncoderContext *p7ecx,
SECKEYGetPasswordKey pwfn, void *pwfnarg)
{
SECStatus rv;
/*
* Flush out any remaining data.
*/
rv = sec_pkcs7_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE);
/*
* Turn off streaming stuff.
*/
SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
if (rv != SECSuccess)
goto loser;
rv = sec_pkcs7_encoder_sig_and_certs(p7ecx->cinfo, pwfn, pwfnarg);
if (rv != SECSuccess)
goto loser;
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
loser:
SEC_ASN1EncoderFinish(p7ecx->ecx);
PORT_Free(p7ecx);
return rv;
}
/*
* Abort the ASN.1 stream. Used by pkcs 12
*/
void
SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error)
{
PORT_Assert(p7ecx);
SEC_ASN1EncoderAbort(p7ecx->ecx, error);
}
/*
* After this routine is called, the entire PKCS7 contentInfo is ready
* to be encoded. This is used internally, but can also be called from
* elsewhere for those who want to be able to just have pointers to
* the ASN1 template for pkcs7 contentInfo built into their own encodings.
*/
SECStatus
SEC_PKCS7PrepareForEncode(SEC_PKCS7ContentInfo *cinfo,
PK11SymKey *bulkkey,
SECKEYGetPasswordKey pwfn,
void *pwfnarg)
{
SEC_PKCS7EncoderContext *p7ecx;
SECItem *content, *enc_content;
SECStatus rv;
p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
if (p7ecx == NULL)
return SECFailure;
content = SEC_PKCS7GetContent(cinfo);
if (p7ecx->encryptobj != NULL) {
SECOidTag kind;
SEC_PKCS7EncryptedContentInfo *enccinfo;
kind = SEC_PKCS7ContentType(p7ecx->cinfo);
switch (kind) {
default:
PORT_Assert(0);
rv = SECFailure;
goto loser;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo);
break;
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo);
break;
}
enc_content = &(enccinfo->encContent);
} else {
enc_content = NULL;
}
if (content != NULL && content->data != NULL && content->len) {
rv = sec_pkcs7_encoder_work_data(p7ecx, enc_content,
content->data, content->len, PR_TRUE);
if (rv != SECSuccess)
goto loser;
}
rv = sec_pkcs7_encoder_sig_and_certs(cinfo, pwfn, pwfnarg);
loser:
PORT_Free(p7ecx);
return rv;
}
/*
* Encode a PKCS7 object, in one shot. All necessary components
* of the object must already be specified. Either the data has
* already been included (via SetContent), or the data is detached,
* or there is no data at all (certs-only).
*
* "cinfo" specifies the object to be encoded.
*
* "outputfn" is where the encoded bytes will be passed.
*
* "outputarg" is an opaque argument to the above callback.
*
* "bulkkey" specifies the bulk encryption key to use. This argument
* can be NULL if no encryption is being done, or if the bulk key should
* be generated internally (usually the case for EnvelopedData but never
* for EncryptedData, which *must* provide a bulk encryption key).
*
* "pwfn" is a callback for getting the password which protects the
* private key of the signer. This argument can be NULL if it is known
* that no signing is going to be done.
*
* "pwfnarg" is an opaque argument to the above callback.
*/
SECStatus
SEC_PKCS7Encode(SEC_PKCS7ContentInfo *cinfo,
SEC_PKCS7EncoderOutputCallback outputfn,
void *outputarg,
PK11SymKey *bulkkey,
SECKEYGetPasswordKey pwfn,
void *pwfnarg)
{
SECStatus rv;
rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
if (rv == SECSuccess) {
struct sec_pkcs7_encoder_output outputcx;
outputcx.outputfn = outputfn;
outputcx.outputarg = outputarg;
rv = SEC_ASN1Encode(cinfo, sec_PKCS7ContentInfoTemplate,
sec_pkcs7_encoder_out, &outputcx);
}
return rv;
}
/*
* Encode a PKCS7 object, in one shot. All necessary components
* of the object must already be specified. Either the data has
* already been included (via SetContent), or the data is detached,
* or there is no data at all (certs-only). The output, rather than
* being passed to an output function as is done above, is all put
* into a SECItem.
*
* "pool" specifies a pool from which to allocate the result.
* It can be NULL, in which case memory is allocated generically.
*
* "dest" specifies a SECItem in which to put the result data.
* It can be NULL, in which case the entire item is allocated, too.
*
* "cinfo" specifies the object to be encoded.
*
* "bulkkey" specifies the bulk encryption key to use. This argument
* can be NULL if no encryption is being done, or if the bulk key should
* be generated internally (usually the case for EnvelopedData but never
* for EncryptedData, which *must* provide a bulk encryption key).
*
* "pwfn" is a callback for getting the password which protects the
* private key of the signer. This argument can be NULL if it is known
* that no signing is going to be done.
*
* "pwfnarg" is an opaque argument to the above callback.
*/
SECItem *
SEC_PKCS7EncodeItem(PLArenaPool *pool,
SECItem *dest,
SEC_PKCS7ContentInfo *cinfo,
PK11SymKey *bulkkey,
SECKEYGetPasswordKey pwfn,
void *pwfnarg)
{
SECStatus rv;
rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
if (rv != SECSuccess)
return NULL;
return SEC_ASN1EncodeItem(pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);
}