Mercurial > trustbridge > nss-cmake-static
diff nss/lib/pkcs7/p7common.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nss/lib/pkcs7/p7common.c Mon Jul 28 10:47:06 2014 +0200 @@ -0,0 +1,691 @@ +/* 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 * result = 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(result != NULL) + SECITEM_ZfreeItem(result, 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 *result = NULL, *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(result != NULL) + SECITEM_ZfreeItem(result, 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; +} + +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; +} +