--- /dev/null
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * CMS miscellaneous utility functions.
+ */
+
+#include <Security/SecCmsEncoder.h> /* @@@ Remove this when we move the Encoder method. */
+#include <Security/SecCmsSignerInfo.h>
+#include "cmslocal.h"
+
+#include "secitem.h"
+#include "secoid.h"
+#include "cryptohi.h"
+
+#include <security_asn1/secasn1.h>
+#include <security_asn1/secerr.h>
+#include <Security/cssmapi.h>
+#include <Security/cssmapple.h>
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+
+/*
+ * SecCmsArraySortByDER - 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).
+ */
+OSStatus
+SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2)
+{
+ PRArenaPool *poolp;
+ int num_objs;
+ CSSM_DATA_PTR *enc_objs;
+ OSStatus rv = SECFailure;
+ int i;
+
+ if (objs == NULL) /* already sorted */
+ return SECSuccess;
+
+ num_objs = SecCmsArrayCount((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.
+ */
+ // Security check to prevent under-allocation
+ if (num_objs<0 || num_objs>=(int)((INT_MAX/sizeof(CSSM_DATA_PTR))-1)) {
+ goto loser;
+ }
+ enc_objs = (CSSM_DATA_PTR *)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(CSSM_DATA_PTR));
+ 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 */
+ SecCmsArraySort((void **)enc_objs, SecCmsUtilDERCompare, objs, objs2);
+
+ rv = SECSuccess;
+
+loser:
+ PORT_FreeArena (poolp, PR_FALSE);
+ return rv;
+}
+
+/*
+ * SecCmsUtilDERCompare - for use with SecCmsArraySort to
+ * sort arrays of CSSM_DATAs containing DER
+ */
+int
+SecCmsUtilDERCompare(void *a, void *b)
+{
+ CSSM_DATA_PTR der1 = (CSSM_DATA_PTR)a;
+ CSSM_DATA_PTR der2 = (CSSM_DATA_PTR)b;
+ 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->Length != der2->Length)
+ return (der1->Length < der2->Length) ? -1 : 1;
+
+ for (j = 0; j < der1->Length; j++) {
+ if (der1->Data[j] == der2->Data[j])
+ continue;
+ return (der1->Data[j] < der2->Data[j]) ? -1 : 1;
+ }
+ return 0;
+}
+
+/*
+ * SecCmsAlgArrayGetIndexByAlgID - 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
+SecCmsAlgArrayGetIndexByAlgID(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;
+}
+
+/*
+ * SecCmsAlgArrayGetIndexByAlgTag - 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
+SecCmsAlgArrayGetIndexByAlgTag(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;
+}
+
+CSSM_CC_HANDLE
+SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
+{
+ SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
+ if (oidData)
+ {
+ CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
+ if (alg)
+ {
+ CSSM_CC_HANDLE digobj;
+ CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
+
+ if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
+ return digobj;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * XXX I would *really* like to not have to do this, but the current
+ * signing interface gives me little choice.
+ */
+SECOidTag
+SecCmsUtilMakeSignatureAlgorithm(SECOidTag hashalg, SECOidTag encalg)
+{
+ switch (encalg) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ switch (hashalg) {
+ case SEC_OID_MD2:
+ return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
+ case SEC_OID_MD5:
+ return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
+ case SEC_OID_SHA1:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ case SEC_OID_SHA256:
+ return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ case SEC_OID_SHA384:
+ return SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
+ case SEC_OID_SHA512:
+ return SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_DSS:
+ switch (hashalg) {
+ case SEC_OID_SHA1:
+ return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+ case SEC_OID_EC_PUBLIC_KEY:
+ switch(hashalg) {
+ /*
+ * Note this is only used when signing and verifying signed attributes,
+ * In which case we really do want the combined ECDSA_WithSHA1 alg...
+ */
+ case SEC_OID_SHA1:
+ return SEC_OID_ECDSA_WithSHA1;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+ default:
+ break;
+ }
+
+ return encalg; /* maybe it is already the right algid */
+}
+
+const SecAsn1Template *
+SecCmsUtilGetTemplateByTypeTag(SECOidTag type)
+{
+ const SecAsn1Template *template;
+ extern const SecAsn1Template SecCmsSignedDataTemplate[];
+ extern const SecAsn1Template SecCmsEnvelopedDataTemplate[];
+ extern const SecAsn1Template SecCmsEncryptedDataTemplate[];
+ extern const SecAsn1Template SecCmsDigestedDataTemplate[];
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ template = SecCmsSignedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ template = SecCmsEnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ template = SecCmsEncryptedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ template = SecCmsDigestedDataTemplate;
+ break;
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_OTHER:
+ template = NULL;
+ break;
+ }
+ return template;
+}
+
+size_t
+SecCmsUtilGetSizeByTypeTag(SECOidTag type)
+{
+ size_t size;
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ size = sizeof(SecCmsSignedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ size = sizeof(SecCmsEnvelopedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ size = sizeof(SecCmsEncryptedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ size = sizeof(SecCmsDigestedData);
+ break;
+ default:
+ case SEC_OID_PKCS7_DATA:
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+SecCmsContentInfoRef
+SecCmsContentGetContentInfo(void *msg, SECOidTag type)
+{
+ SecCmsContent c;
+ SecCmsContentInfoRef cinfo;
+
+ if (!msg)
+ return NULL;
+ 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;
+ }
+ return cinfo;
+}
+
+// @@@ Return CFStringRef and do localization.
+const char *
+SecCmsUtilVerificationStatusToString(SecCmsVerificationStatus vs)
+{
+ switch (vs) {
+ case SecCmsVSUnverified: return "Unverified";
+ case SecCmsVSGoodSignature: return "GoodSignature";
+ case SecCmsVSBadSignature: return "BadSignature";
+ case SecCmsVSDigestMismatch: return "DigestMismatch";
+ case SecCmsVSSigningCertNotFound: return "SigningCertNotFound";
+ case SecCmsVSSigningCertNotTrusted: return "SigningCertNotTrusted";
+ case SecCmsVSSignatureAlgorithmUnknown: return "SignatureAlgorithmUnknown";
+ case SecCmsVSSignatureAlgorithmUnsupported: return "SignatureAlgorithmUnsupported";
+ case SecCmsVSMalformedSignature: return "MalformedSignature";
+ case SecCmsVSProcessingError: return "ProcessingError";
+ default: return "Unknown";
+ }
+}
+
+OSStatus
+SecArenaPoolCreate(size_t chunksize, SecArenaPoolRef *outArena)
+{
+ OSStatus status;
+
+ if (!outArena) {
+ status = paramErr;
+ goto loser;
+ }
+
+ *outArena = (SecArenaPoolRef)PORT_NewArena(chunksize);
+ if (*outArena)
+ status = 0;
+ else
+ status = PORT_GetError();
+
+loser:
+ return status;
+}
+
+void
+SecArenaPoolFree(SecArenaPoolRef arena, Boolean zero)
+{
+ PORT_FreeArena((PLArenaPool *)arena, zero);
+}