summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pkcs7/p7common.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/pkcs7/p7common.c')
-rw-r--r--security/nss/lib/pkcs7/p7common.c663
1 files changed, 663 insertions, 0 deletions
diff --git a/security/nss/lib/pkcs7/p7common.c b/security/nss/lib/pkcs7/p7common.c
new file mode 100644
index 000000000..8a6ac033e
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7common.c
@@ -0,0 +1,663 @@
+/* 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
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * PKCS7 implementation -- the exported parts that are used whether
+ * creating or decoding.
+ */
+
+#include "p7local.h"
+
+#include "cert.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+
+/*
+ * Find out (saving pointer to lookup result for future reference)
+ * and return the inner content type.
+ */
+SECOidTag
+SEC_PKCS7ContentType(SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo->contentTypeTag == NULL)
+ cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+
+ if (cinfo->contentTypeTag == NULL)
+ return SEC_OID_UNKNOWN;
+
+ return cinfo->contentTypeTag->offset;
+}
+
+/*
+ * Destroy a PKCS7 contentInfo and all of its sub-pieces.
+ */
+void
+SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+ CERTCertificate **certs;
+ CERTCertificateList **certlists;
+ SEC_PKCS7SignerInfo **signerinfos;
+ SEC_PKCS7RecipientInfo **recipientinfos;
+
+ PORT_Assert(cinfo->refCount > 0);
+ if (cinfo->refCount <= 0)
+ return;
+
+ cinfo->refCount--;
+ if (cinfo->refCount > 0)
+ return;
+
+ certs = NULL;
+ certlists = NULL;
+ recipientinfos = NULL;
+ signerinfos = NULL;
+
+ kind = SEC_PKCS7ContentType(cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_ENVELOPED_DATA: {
+ SEC_PKCS7EnvelopedData *edp;
+
+ edp = cinfo->content.envelopedData;
+ if (edp != NULL) {
+ recipientinfos = edp->recipientInfos;
+ }
+ } break;
+ case SEC_OID_PKCS7_SIGNED_DATA: {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ if (sdp != NULL) {
+ certs = sdp->certs;
+ certlists = sdp->certLists;
+ signerinfos = sdp->signerInfos;
+ }
+ } break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ if (saedp != NULL) {
+ certs = saedp->certs;
+ certlists = saedp->certLists;
+ recipientinfos = saedp->recipientInfos;
+ signerinfos = saedp->signerInfos;
+ if (saedp->sigKey != NULL)
+ PK11_FreeSymKey(saedp->sigKey);
+ }
+ } break;
+ default:
+ /* XXX Anything else that needs to be "manually" freed/destroyed? */
+ break;
+ }
+
+ if (certs != NULL) {
+ CERTCertificate *cert;
+
+ while ((cert = *certs++) != NULL) {
+ CERT_DestroyCertificate(cert);
+ }
+ }
+
+ if (certlists != NULL) {
+ CERTCertificateList *certlist;
+
+ while ((certlist = *certlists++) != NULL) {
+ CERT_DestroyCertificateList(certlist);
+ }
+ }
+
+ if (recipientinfos != NULL) {
+ SEC_PKCS7RecipientInfo *ri;
+
+ while ((ri = *recipientinfos++) != NULL) {
+ if (ri->cert != NULL)
+ CERT_DestroyCertificate(ri->cert);
+ }
+ }
+
+ if (signerinfos != NULL) {
+ SEC_PKCS7SignerInfo *si;
+
+ while ((si = *signerinfos++) != NULL) {
+ if (si->cert != NULL)
+ CERT_DestroyCertificate(si->cert);
+ if (si->certList != NULL)
+ CERT_DestroyCertificateList(si->certList);
+ }
+ }
+
+ if (cinfo->poolp != NULL) {
+ PORT_FreeArena(cinfo->poolp, PR_FALSE); /* XXX clear it? */
+ }
+}
+
+/*
+ * Return a copy of the given contentInfo. The copy may be virtual
+ * or may be real -- either way, the result needs to be passed to
+ * SEC_PKCS7DestroyContentInfo later (as does the original).
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo == NULL)
+ return NULL;
+
+ PORT_Assert(cinfo->refCount > 0);
+
+ if (cinfo->created) {
+ /*
+ * Want to do a real copy of these; otherwise subsequent
+ * changes made to either copy are likely to be a surprise.
+ * XXX I suspect that this will not actually be called for yet,
+ * which is why the assert, so to notice if it is...
+ */
+ PORT_Assert(0);
+ /*
+ * XXX Create a new pool here, and copy everything from
+ * within. For cert stuff, need to call the appropriate
+ * copy functions, etc.
+ */
+ }
+
+ cinfo->refCount++;
+ return cinfo;
+}
+
+/*
+ * Return a pointer to the actual content. In the case of those types
+ * which are encrypted, this returns the *plain* content.
+ * XXX Needs revisiting if/when we handle nested encrypted types.
+ */
+SECItem *
+SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType(cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_DATA:
+ return cinfo->content.data;
+ case SEC_OID_PKCS7_DIGESTED_DATA: {
+ SEC_PKCS7DigestedData *digd;
+
+ digd = cinfo->content.digestedData;
+ if (digd == NULL)
+ break;
+ return SEC_PKCS7GetContent(&(digd->contentInfo));
+ }
+ case SEC_OID_PKCS7_ENCRYPTED_DATA: {
+ SEC_PKCS7EncryptedData *encd;
+
+ encd = cinfo->content.encryptedData;
+ if (encd == NULL)
+ break;
+ return &(encd->encContentInfo.plainContent);
+ }
+ case SEC_OID_PKCS7_ENVELOPED_DATA: {
+ SEC_PKCS7EnvelopedData *envd;
+
+ envd = cinfo->content.envelopedData;
+ if (envd == NULL)
+ break;
+ return &(envd->encContentInfo.plainContent);
+ }
+ case SEC_OID_PKCS7_SIGNED_DATA: {
+ SEC_PKCS7SignedData *sigd;
+
+ sigd = cinfo->content.signedData;
+ if (sigd == NULL)
+ break;
+ return SEC_PKCS7GetContent(&(sigd->contentInfo));
+ }
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
+ SEC_PKCS7SignedAndEnvelopedData *saed;
+
+ saed = cinfo->content.signedAndEnvelopedData;
+ if (saed == NULL)
+ break;
+ return &(saed->encContentInfo.plainContent);
+ }
+ default:
+ PORT_Assert(0);
+ break;
+ }
+
+ return NULL;
+}
+
+/*
+ * XXX Fix the placement and formatting of the
+ * following routines (i.e. make them consistent with the rest of
+ * the pkcs7 code -- I think some/many belong in other files and
+ * they all need a formatting/style rehaul)
+ */
+
+/* retrieve the algorithm identifier for encrypted data.
+ * the identifier returned is a copy of the algorithm identifier
+ * in the content info and needs to be freed after being used.
+ *
+ * cinfo is the content info for which to retrieve the
+ * encryption algorithm.
+ *
+ * if the content info is not encrypted data or an error
+ * occurs NULL is returned.
+ */
+SECAlgorithmID *
+SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECAlgorithmID *alg = 0;
+ switch (SEC_PKCS7ContentType(cinfo)) {
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ alg = &cinfo->content.signedAndEnvelopedData
+ ->encContentInfo.contentEncAlg;
+ break;
+ default:
+ alg = 0;
+ break;
+ }
+
+ return alg;
+}
+
+/* set the content of the content info. For data content infos,
+ * the data is set. For encrytped content infos, the plainContent
+ * is set, and is expected to be encrypted later.
+ *
+ * cinfo is the content info where the data will be set
+ *
+ * buf is a buffer of the data to set
+ *
+ * len is the length of the data being set.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates the content was successfully set.
+ */
+SECStatus
+SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo,
+ const char *buf,
+ unsigned long len)
+{
+ SECOidTag cinfo_type;
+ SECStatus rv;
+ SECItem content;
+ SECOidData *contentTypeTag = NULL;
+
+ content.type = siBuffer;
+ content.data = (unsigned char *)buf;
+ content.len = len;
+
+ cinfo_type = SEC_PKCS7ContentType(cinfo);
+
+ /* set inner content */
+ switch (cinfo_type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (content.len > 0) {
+ /* we "leak" the old content here, but as it's all in the pool */
+ /* it does not really matter */
+
+ /* create content item if necessary */
+ if (cinfo->content.signedData->contentInfo.content.data == NULL)
+ cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0);
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ cinfo->content.signedData->contentInfo.content.data,
+ &content);
+ } else {
+ cinfo->content.signedData->contentInfo.content.data->data = NULL;
+ cinfo->content.signedData->contentInfo.content.data->len = 0;
+ rv = SECSuccess;
+ }
+ if (rv == SECFailure)
+ goto loser;
+
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ /* XXX this forces the inner content type to be "data" */
+ /* do we really want to override without asking or reason? */
+ contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA);
+ if (contentTypeTag == NULL)
+ goto loser;
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ &(cinfo->content.encryptedData->encContentInfo.contentType),
+ &(contentTypeTag->oid));
+ if (rv == SECFailure)
+ goto loser;
+ if (content.len > 0) {
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ &(cinfo->content.encryptedData->encContentInfo.plainContent),
+ &content);
+ } else {
+ cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL;
+ cinfo->content.encryptedData->encContentInfo.encContent.data = NULL;
+ cinfo->content.encryptedData->encContentInfo.plainContent.len = 0;
+ cinfo->content.encryptedData->encContentInfo.encContent.len = 0;
+ rv = SECSuccess;
+ }
+ if (rv == SECFailure)
+ goto loser;
+ break;
+ case SEC_OID_PKCS7_DATA:
+ cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp,
+ sizeof(SECItem));
+ if (cinfo->content.data == NULL)
+ goto loser;
+ if (content.len > 0) {
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ cinfo->content.data, &content);
+ } else {
+ /* handle case with NULL content */
+ rv = SECSuccess;
+ }
+ if (rv == SECFailure)
+ goto loser;
+ break;
+ default:
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+
+ return SECFailure;
+}
+
+/* the content of an encrypted data content info is encrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "plainContent" field of the content info.
+ *
+ * cinfo is the content info to encrypt
+ *
+ * key is the key with which to perform the encryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+SECStatus
+SEC_PKCS7EncryptContents(PLArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx)
+{
+ SECAlgorithmID *algid = NULL;
+ SECItem *src;
+ SECItem *dest;
+ SECItem *blocked_data = NULL;
+ void *mark;
+ void *cx;
+ PK11SymKey *eKey = NULL;
+ PK11SlotInfo *slot = NULL;
+
+ CK_MECHANISM_TYPE cryptoMechType;
+ int bs;
+ SECStatus rv = SECFailure;
+ SECItem *c_param = NULL;
+
+ if ((cinfo == NULL) || (key == NULL))
+ return SECFailure;
+
+ if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
+ return SECFailure;
+
+ algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
+ if (algid == NULL)
+ return SECFailure;
+
+ if (poolp == NULL)
+ poolp = cinfo->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ src = &cinfo->content.encryptedData->encContentInfo.plainContent;
+ dest = &cinfo->content.encryptedData->encContentInfo.encContent;
+ dest->data = (unsigned char *)PORT_ArenaZAlloc(poolp, (src->len + 64));
+ dest->len = (src->len + 64);
+ if (dest->data == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ if (slot == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
+ if (eKey == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* block according to PKCS 8 */
+ bs = PK11_GetBlockSize(cryptoMechType, c_param);
+ rv = SECSuccess;
+ if (bs) {
+ char pad_char;
+ pad_char = (char)(bs - (src->len % bs));
+ if (src->len % bs) {
+ rv = SECSuccess;
+ blocked_data = PK11_BlockData(src, bs);
+ if (blocked_data) {
+ PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char),
+ pad_char, (int)pad_char);
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ } else {
+ blocked_data = SECITEM_DupItem(src);
+ if (blocked_data) {
+ blocked_data->data = (unsigned char *)PORT_Realloc(
+ blocked_data->data,
+ blocked_data->len + bs);
+ if (blocked_data->data) {
+ blocked_data->len += bs;
+ PORT_Memset((blocked_data->data + src->len), (char)bs, bs);
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+ } else {
+ blocked_data = SECITEM_DupItem(src);
+ if (!blocked_data) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
+ eKey, c_param);
+ if (cx == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_CipherOp((PK11Context *)cx, dest->data, (int *)(&dest->len),
+ (int)(src->len + 64), blocked_data->data,
+ (int)blocked_data->len);
+ PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
+
+loser:
+ /* let success fall through */
+ if (blocked_data != NULL)
+ SECITEM_ZfreeItem(blocked_data, PR_TRUE);
+
+ if (rv == SECFailure)
+ PORT_ArenaRelease(poolp, mark);
+ else
+ PORT_ArenaUnmark(poolp, mark);
+
+ if (eKey != NULL)
+ PK11_FreeSymKey(eKey);
+
+ if (slot != NULL)
+ PK11_FreeSlot(slot);
+
+ if (c_param != NULL)
+ SECITEM_ZfreeItem(c_param, PR_TRUE);
+
+ return rv;
+}
+
+/* the content of an encrypted data content info is decrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "encContent" field of the content info.
+ *
+ * cinfo is the content info to decrypt
+ *
+ * key is the key with which to perform the decryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+SECStatus
+SEC_PKCS7DecryptContents(PLArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx)
+{
+ SECAlgorithmID *algid = NULL;
+ SECStatus rv = SECFailure;
+ SECItem *dest, *src;
+ void *mark;
+
+ PK11SymKey *eKey = NULL;
+ PK11SlotInfo *slot = NULL;
+ CK_MECHANISM_TYPE cryptoMechType;
+ void *cx;
+ SECItem *c_param = NULL;
+ int bs;
+
+ if ((cinfo == NULL) || (key == NULL))
+ return SECFailure;
+
+ if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
+ return SECFailure;
+
+ algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
+ if (algid == NULL)
+ return SECFailure;
+
+ if (poolp == NULL)
+ poolp = cinfo->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ src = &cinfo->content.encryptedData->encContentInfo.encContent;
+ dest = &cinfo->content.encryptedData->encContentInfo.plainContent;
+ dest->data = (unsigned char *)PORT_ArenaZAlloc(poolp, (src->len + 64));
+ dest->len = (src->len + 64);
+ if (dest->data == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ if (slot == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
+ if (eKey == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
+ eKey, c_param);
+ if (cx == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_CipherOp((PK11Context *)cx, dest->data, (int *)(&dest->len),
+ (int)(src->len + 64), src->data, (int)src->len);
+ PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
+
+ bs = PK11_GetBlockSize(cryptoMechType, c_param);
+ if (bs) {
+ /* check for proper badding in block algorithms. this assumes
+ * RC2 cbc or a DES cbc variant. and the padding is thus defined
+ */
+ if (((int)dest->data[dest->len - 1] <= bs) &&
+ ((int)dest->data[dest->len - 1] > 0)) {
+ dest->len -= (int)dest->data[dest->len - 1];
+ } else {
+ rv = SECFailure;
+ /* set an error ? */
+ }
+ }
+
+loser:
+ /* let success fall through */
+ if (rv == SECFailure)
+ PORT_ArenaRelease(poolp, mark);
+ else
+ PORT_ArenaUnmark(poolp, mark);
+
+ if (eKey != NULL)
+ PK11_FreeSymKey(eKey);
+
+ if (slot != NULL)
+ PK11_FreeSlot(slot);
+
+ if (c_param != NULL)
+ SECITEM_ZfreeItem(c_param, PR_TRUE);
+
+ return rv;
+}
+
+SECItem **
+SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo)
+{
+ switch (SEC_PKCS7ContentType(cinfo)) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ return cinfo->content.signedData->rawCerts;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+
+int
+SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA)
+ return cinfo->content.envelopedData->encContentInfo.keysize;
+ else
+ return 0;
+}