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