summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/smime/cmsutil.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/smime/cmsutil.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/security/nss/lib/smime/cmsutil.c b/security/nss/lib/smime/cmsutil.c
new file mode 100644
index 0000000000..844fd22a93
--- /dev/null
+++ b/security/nss/lib/smime/cmsutil.c
@@ -0,0 +1,371 @@
+/* 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/. */
+
+/*
+ * CMS miscellaneous utility functions.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+#include "sechash.h"
+
+/*
+ * NSS_CMSArray_SortByDER - sort array of objects by objects' DER encoding
+ *
+ * make sure that the order of the objects guarantees valid DER (which must be
+ * in lexigraphically ascending order for a SET OF); if reordering is necessary it
+ * will be done in place (in objs).
+ */
+SECStatus
+NSS_CMSArray_SortByDER(void **objs, const SEC_ASN1Template *objtemplate, void **objs2)
+{
+ PLArenaPool *poolp;
+ int num_objs;
+ SECItem **enc_objs;
+ SECStatus rv = SECFailure;
+ int i;
+
+ if (objs == NULL) /* already sorted */
+ return SECSuccess;
+
+ num_objs = NSS_CMSArray_Count((void **)objs);
+ if (num_objs == 0 || num_objs == 1) /* already sorted. */
+ return SECSuccess;
+
+ poolp = PORT_NewArena(1024); /* arena for temporaries */
+ if (poolp == NULL)
+ return SECFailure; /* no memory; nothing we can do... */
+
+ /*
+ * Allocate arrays to hold the individual encodings which we will use
+ * for comparisons and the reordered attributes as they are sorted.
+ */
+ enc_objs = (SECItem **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SECItem *));
+ if (enc_objs == NULL)
+ goto loser;
+
+ /* DER encode each individual object. */
+ for (i = 0; i < num_objs; i++) {
+ enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate);
+ if (enc_objs[i] == NULL)
+ goto loser;
+ }
+ enc_objs[num_objs] = NULL;
+
+ /* now compare and sort objs by the order of enc_objs */
+ NSS_CMSArray_Sort((void **)enc_objs, NSS_CMSUtil_DERCompare, objs, objs2);
+
+ rv = SECSuccess;
+
+loser:
+ PORT_FreeArena(poolp, PR_FALSE);
+ return rv;
+}
+
+/*
+ * NSS_CMSUtil_DERCompare - for use with NSS_CMSArray_Sort to
+ * sort arrays of SECItems containing DER
+ */
+int
+NSS_CMSUtil_DERCompare(void *a, void *b)
+{
+ SECItem *der1 = (SECItem *)a;
+ SECItem *der2 = (SECItem *)b;
+ unsigned int j;
+
+ /*
+ * Find the lowest (lexigraphically) encoding. One that is
+ * shorter than all the rest is known to be "less" because each
+ * attribute is of the same type (a SEQUENCE) and so thus the
+ * first octet of each is the same, and the second octet is
+ * the length (or the length of the length with the high bit
+ * set, followed by the length, which also works out to always
+ * order the shorter first). Two (or more) that have the
+ * same length need to be compared byte by byte until a mismatch
+ * is found.
+ */
+ if (der1->len != der2->len)
+ return (der1->len < der2->len) ? -1 : 1;
+
+ for (j = 0; j < der1->len; j++) {
+ if (der1->data[j] == der2->data[j])
+ continue;
+ return (der1->data[j] < der2->data[j]) ? -1 : 1;
+ }
+ return 0;
+}
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgID - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algid - algorithmid of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+int
+NSS_CMSAlgArray_GetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid)
+{
+ int i;
+
+ if (algorithmArray == NULL || algorithmArray[0] == NULL)
+ return -1;
+
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual)
+ break; /* bingo */
+ }
+
+ if (algorithmArray[i] == NULL)
+ return -1; /* not found */
+
+ return i;
+}
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgTag - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algtag - algorithm tag of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+int
+NSS_CMSAlgArray_GetIndexByAlgTag(SECAlgorithmID **algorithmArray,
+ SECOidTag algtag)
+{
+ SECOidData *algid;
+ int i = -1;
+
+ if (algorithmArray == NULL || algorithmArray[0] == NULL)
+ return i;
+
+#ifdef ORDER_N_SQUARED
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ algid = SECOID_FindOID(&(algorithmArray[i]->algorithm));
+ if (algid->offset == algtag)
+ break; /* bingo */
+ }
+#else
+ algid = SECOID_FindOIDByTag(algtag);
+ if (!algid)
+ return i;
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid))
+ break; /* bingo */
+ }
+#endif
+
+ if (algorithmArray[i] == NULL)
+ return -1; /* not found */
+
+ return i;
+}
+
+/*
+ * Map a sign algorithm to a digest algorithm.
+ * This is used to handle incorrectly formatted packages sent to us
+ * from Windows 2003.
+ */
+SECOidTag
+NSS_CMSUtil_MapSignAlgs(SECOidTag signAlg)
+{
+ switch (signAlg) {
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ return SEC_OID_MD2;
+ break;
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ return SEC_OID_MD5;
+ break;
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
+ case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ return SEC_OID_SHA1;
+ break;
+ case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
+ return SEC_OID_SHA256;
+ break;
+ case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
+ return SEC_OID_SHA384;
+ break;
+ case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
+ return SEC_OID_SHA512;
+ break;
+ default:
+ break;
+ }
+ /* not one of the algtags incorrectly sent to us*/
+ return signAlg;
+}
+
+const SECHashObject *
+NSS_CMSUtil_GetHashObjByAlgID(SECAlgorithmID *algid)
+{
+ SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm));
+ const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
+
+ return digobj;
+}
+
+const SEC_ASN1Template *
+NSS_CMSUtil_GetTemplateByTypeTag(SECOidTag type)
+{
+ const SEC_ASN1Template *template;
+ extern const SEC_ASN1Template NSSCMSSignedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSEnvelopedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSEncryptedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSDigestedDataTemplate[];
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ template = NSSCMSSignedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ template = NSSCMSEnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ template = NSSCMSEncryptedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ template = NSSCMSDigestedDataTemplate;
+ break;
+ default:
+ template = NSS_CMSType_GetTemplate(type);
+ break;
+ }
+ return template;
+}
+
+size_t
+NSS_CMSUtil_GetSizeByTypeTag(SECOidTag type)
+{
+ size_t size;
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ size = sizeof(NSSCMSSignedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ size = sizeof(NSSCMSEnvelopedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ size = sizeof(NSSCMSEncryptedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ size = sizeof(NSSCMSDigestedData);
+ break;
+ default:
+ size = NSS_CMSType_GetContentSize(type);
+ break;
+ }
+ return size;
+}
+
+NSSCMSContentInfo *
+NSS_CMSContent_GetContentInfo(void *msg, SECOidTag type)
+{
+ NSSCMSContent c;
+ NSSCMSContentInfo *cinfo = NULL;
+
+ if (!msg)
+ return cinfo;
+ c.pointer = msg;
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ cinfo = &(c.signedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ cinfo = &(c.envelopedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ cinfo = &(c.encryptedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ cinfo = &(c.digestedData->contentInfo);
+ break;
+ default:
+ cinfo = NULL;
+ if (NSS_CMSType_IsWrapper(type)) {
+ cinfo = &(c.genericData->contentInfo);
+ }
+ }
+ /* We are using a union as a form of 'safe casting'. This
+ * syntax confuses cppcheck, so tell it it's OK (and any human
+ * who happens along to verify any other scanner warnings) */
+ /* cppcheck-suppress returnDanglingLifetime */
+ return cinfo;
+}
+
+const char *
+NSS_CMSUtil_VerificationStatusToString(NSSCMSVerificationStatus vs)
+{
+ switch (vs) {
+ case NSSCMSVS_Unverified:
+ return "Unverified";
+ case NSSCMSVS_GoodSignature:
+ return "GoodSignature";
+ case NSSCMSVS_BadSignature:
+ return "BadSignature";
+ case NSSCMSVS_DigestMismatch:
+ return "DigestMismatch";
+ case NSSCMSVS_SigningCertNotFound:
+ return "SigningCertNotFound";
+ case NSSCMSVS_SigningCertNotTrusted:
+ return "SigningCertNotTrusted";
+ case NSSCMSVS_SignatureAlgorithmUnknown:
+ return "SignatureAlgorithmUnknown";
+ case NSSCMSVS_SignatureAlgorithmUnsupported:
+ return "SignatureAlgorithmUnsupported";
+ case NSSCMSVS_MalformedSignature:
+ return "MalformedSignature";
+ case NSSCMSVS_ProcessingError:
+ return "ProcessingError";
+ default:
+ return "Unknown";
+ }
+}
+
+SECStatus
+NSS_CMSDEREncode(NSSCMSMessage *cmsg, SECItem *input, SECItem *derOut,
+ PLArenaPool *arena)
+{
+ NSSCMSEncoderContext *ecx;
+ SECStatus rv = SECSuccess;
+ if (!cmsg || !derOut || !arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ ecx = NSS_CMSEncoder_Start(cmsg, 0, 0, derOut, arena, 0, 0, 0, 0, 0, 0);
+ if (!ecx) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (input) {
+ rv = NSS_CMSEncoder_Update(ecx, (const char *)input->data, input->len);
+ if (rv) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ }
+ }
+ rv |= NSS_CMSEncoder_Finish(ecx);
+ if (rv) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+ return rv;
+}