1/* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34/* 35 * CMS miscellaneous utility functions. 36 */ 37 38#include <Security/SecCmsEncoder.h> /* @@@ Remove this when we move the Encoder method. */ 39#include <Security/SecCmsSignerInfo.h> 40 41#include "cmslocal.h" 42 43#include "SecAsn1Item.h" 44#include "secoid.h" 45#include "cryptohi.h" 46 47#include <security_asn1/secasn1.h> 48#include <security_asn1/secerr.h> 49#include <security_asn1/secport.h> 50 51#if USE_CDSA_CRYPTO 52#include <Security/cssmapi.h> 53#include <Security/cssmapple.h> 54#include <Security/SecBase.h> 55 56#else 57#include <CommonCrypto/CommonDigest.h> 58#include <Security/SecBase.h> 59 60#endif 61 62 63/* 64 * SecCmsArraySortByDER - sort array of objects by objects' DER encoding 65 * 66 * make sure that the order of the objects guarantees valid DER (which must be 67 * in lexigraphically ascending order for a SET OF); if reordering is necessary it 68 * will be done in place (in objs). 69 */ 70OSStatus 71SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2) 72{ 73 PRArenaPool *poolp; 74 int num_objs; 75 SecAsn1Item **enc_objs; 76 OSStatus rv = SECFailure; 77 int i; 78 79 if (objs == NULL) /* already sorted */ 80 return SECSuccess; 81 82 num_objs = SecCmsArrayCount((void **)objs); 83 if (num_objs == 0 || num_objs == 1) /* already sorted. */ 84 return SECSuccess; 85 86 poolp = PORT_NewArena (1024); /* arena for temporaries */ 87 if (poolp == NULL) 88 return SECFailure; /* no memory; nothing we can do... */ 89 90 /* 91 * Allocate arrays to hold the individual encodings which we will use 92 * for comparisons and the reordered attributes as they are sorted. 93 */ 94 enc_objs = (SecAsn1Item **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SecAsn1Item *)); 95 if (enc_objs == NULL) 96 goto loser; 97 98 /* DER encode each individual object. */ 99 for (i = 0; i < num_objs; i++) { 100 enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate); 101 if (enc_objs[i] == NULL) 102 goto loser; 103 } 104 enc_objs[num_objs] = NULL; 105 106 /* now compare and sort objs by the order of enc_objs */ 107 SecCmsArraySort((void **)enc_objs, SecCmsUtilDERCompare, objs, objs2); 108 109 rv = SECSuccess; 110 111loser: 112 PORT_FreeArena (poolp, PR_FALSE); 113 return rv; 114} 115 116/* 117 * SecCmsUtilDERCompare - for use with SecCmsArraySort to 118 * sort arrays of SecAsn1Items containing DER 119 */ 120int 121SecCmsUtilDERCompare(void *a, void *b) 122{ 123 SecAsn1Item * der1 = (SecAsn1Item *)a; 124 SecAsn1Item * der2 = (SecAsn1Item *)b; 125 126 /* 127 * Find the lowest (lexigraphically) encoding. One that is 128 * shorter than all the rest is known to be "less" because each 129 * attribute is of the same type (a SEQUENCE) and so thus the 130 * first octet of each is the same, and the second octet is 131 * the length (or the length of the length with the high bit 132 * set, followed by the length, which also works out to always 133 * order the shorter first). Two (or more) that have the 134 * same length need to be compared byte by byte until a mismatch 135 * is found. 136 */ 137 if (der1->Length != der2->Length) 138 return (der1->Length < der2->Length) ? -1 : 1; 139 140#if 1 141 return memcmp(der1->Data, der2->Data, der1->Length); 142#else 143 size_t j; 144 for (j = 0; j < der1->Length; j++) { 145 if (der1->Data[j] == der2->Data[j]) 146 continue; 147 return (der1->Data[j] < der2->Data[j]) ? -1 : 1; 148 } 149 return 0; 150#endif 151} 152 153/* 154 * SecCmsAlgArrayGetIndexByAlgID - find a specific algorithm in an array of 155 * algorithms. 156 * 157 * algorithmArray - array of algorithm IDs 158 * algid - algorithmid of algorithm to pick 159 * 160 * Returns: 161 * An integer containing the index of the algorithm in the array or -1 if 162 * algorithm was not found. 163 */ 164int 165SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid) 166{ 167 int i; 168 169 if (algorithmArray == NULL || algorithmArray[0] == NULL) 170 return -1; 171 172 for (i = 0; algorithmArray[i] != NULL; i++) { 173 if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual) 174 break; /* bingo */ 175 } 176 177 if (algorithmArray[i] == NULL) 178 return -1; /* not found */ 179 180 return i; 181} 182 183/* 184 * SecCmsAlgArrayGetIndexByAlgTag - find a specific algorithm in an array of 185 * algorithms. 186 * 187 * algorithmArray - array of algorithm IDs 188 * algtag - algorithm tag of algorithm to pick 189 * 190 * Returns: 191 * An integer containing the index of the algorithm in the array or -1 if 192 * algorithm was not found. 193 */ 194int 195SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, 196 SECOidTag algtag) 197{ 198 SECOidData *algid; 199 int i = -1; 200 201 if (algorithmArray == NULL || algorithmArray[0] == NULL) 202 return i; 203 204#ifdef ORDER_N_SQUARED 205 for (i = 0; algorithmArray[i] != NULL; i++) { 206 algid = SECOID_FindOID(&(algorithmArray[i]->algorithm)); 207 if (algid->offset == algtag) 208 break; /* bingo */ 209 } 210#else 211 algid = SECOID_FindOIDByTag(algtag); 212 if (!algid) 213 return i; 214 for (i = 0; algorithmArray[i] != NULL; i++) { 215 if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid)) 216 break; /* bingo */ 217 } 218#endif 219 220 if (algorithmArray[i] == NULL) 221 return -1; /* not found */ 222 223 return i; 224} 225 226#if USE_CDSA_CRYPTO 227CSSM_CC_HANDLE 228#else 229void * 230#endif 231SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid) 232{ 233 SECOidData *oidData = SECOID_FindOID(&(algid->algorithm)); 234 if (oidData) 235 { 236#if USE_CDSA_CRYPTO 237 CSSM_ALGORITHMS alg = oidData->cssmAlgorithm; 238 if (alg) 239 { 240 CSSM_CC_HANDLE digobj; 241 CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg); 242 243 if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj)) 244 return digobj; 245 } 246#else 247 void *digobj = NULL; 248 switch (oidData->offset) { 249 case SEC_OID_SHA1: 250 digobj = calloc(1, sizeof(CC_SHA1_CTX)); 251 CC_SHA1_Init(digobj); 252 break; 253 case SEC_OID_MD5: 254 digobj = calloc(1, sizeof(CC_MD5_CTX)); 255 CC_MD5_Init(digobj); 256 break; 257 case SEC_OID_SHA224: 258 digobj = calloc(1, sizeof(CC_SHA256_CTX)); 259 CC_SHA224_Init(digobj); 260 break; 261 case SEC_OID_SHA256: 262 digobj = calloc(1, sizeof(CC_SHA256_CTX)); 263 CC_SHA256_Init(digobj); 264 break; 265 case SEC_OID_SHA384: 266 digobj = calloc(1, sizeof(CC_SHA512_CTX)); 267 CC_SHA384_Init(digobj); 268 break; 269 case SEC_OID_SHA512: 270 digobj = calloc(1, sizeof(CC_SHA512_CTX)); 271 CC_SHA512_Init(digobj); 272 break; 273 default: 274 break; 275 } 276 return digobj; 277#endif 278 } 279 280 return 0; 281} 282 283/* 284 * XXX I would *really* like to not have to do this, but the current 285 * signing interface gives me little choice. 286 */ 287SECOidTag 288SecCmsUtilMakeSignatureAlgorithm(SECOidTag hashalg, SECOidTag encalg) 289{ 290 switch (encalg) { 291 case SEC_OID_PKCS1_RSA_ENCRYPTION: 292 switch (hashalg) { 293 case SEC_OID_MD2: 294 return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; 295 case SEC_OID_MD5: 296 return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; 297 case SEC_OID_SHA1: 298 return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; 299 case SEC_OID_SHA256: 300 return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; 301 case SEC_OID_SHA384: 302 return SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; 303 case SEC_OID_SHA512: 304 return SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; 305 default: 306 return SEC_OID_UNKNOWN; 307 } 308 case SEC_OID_ANSIX9_DSA_SIGNATURE: 309 case SEC_OID_MISSI_KEA_DSS: 310 case SEC_OID_MISSI_DSS: 311 switch (hashalg) { 312 case SEC_OID_SHA1: 313 return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; 314 default: 315 return SEC_OID_UNKNOWN; 316 } 317 default: 318 break; 319 } 320 321 return encalg; /* maybe it is already the right algid */ 322} 323 324const SecAsn1Template * 325SecCmsUtilGetTemplateByTypeTag(SECOidTag type) 326{ 327 const SecAsn1Template *template; 328 extern const SecAsn1Template SecCmsSignedDataTemplate[]; 329 extern const SecAsn1Template SecCmsEnvelopedDataTemplate[]; 330 extern const SecAsn1Template SecCmsEncryptedDataTemplate[]; 331 extern const SecAsn1Template SecCmsDigestedDataTemplate[]; 332 333 switch (type) { 334 case SEC_OID_PKCS7_SIGNED_DATA: 335 template = SecCmsSignedDataTemplate; 336 break; 337 case SEC_OID_PKCS7_ENVELOPED_DATA: 338 template = SecCmsEnvelopedDataTemplate; 339 break; 340 case SEC_OID_PKCS7_ENCRYPTED_DATA: 341 template = SecCmsEncryptedDataTemplate; 342 break; 343 case SEC_OID_PKCS7_DIGESTED_DATA: 344 template = SecCmsDigestedDataTemplate; 345 break; 346 default: 347 case SEC_OID_PKCS7_DATA: 348 template = NULL; 349 break; 350 } 351 return template; 352} 353 354size_t 355SecCmsUtilGetSizeByTypeTag(SECOidTag type) 356{ 357 size_t size; 358 359 switch (type) { 360 case SEC_OID_PKCS7_SIGNED_DATA: 361 size = sizeof(SecCmsSignedData); 362 break; 363 case SEC_OID_PKCS7_ENVELOPED_DATA: 364 size = sizeof(SecCmsEnvelopedData); 365 break; 366 case SEC_OID_PKCS7_ENCRYPTED_DATA: 367 size = sizeof(SecCmsEncryptedData); 368 break; 369 case SEC_OID_PKCS7_DIGESTED_DATA: 370 size = sizeof(SecCmsDigestedData); 371 break; 372 default: 373 case SEC_OID_PKCS7_DATA: 374 size = 0; 375 break; 376 } 377 return size; 378} 379 380SecCmsContentInfoRef 381SecCmsContentGetContentInfo(void *msg, SECOidTag type) 382{ 383 SecCmsContent c; 384 SecCmsContentInfoRef cinfo; 385 386 if (!msg) 387 return NULL; 388 c.pointer = msg; 389 switch (type) { 390 case SEC_OID_PKCS7_SIGNED_DATA: 391 cinfo = &(c.signedData->contentInfo); 392 break; 393 case SEC_OID_PKCS7_ENVELOPED_DATA: 394 cinfo = &(c.envelopedData->contentInfo); 395 break; 396 case SEC_OID_PKCS7_ENCRYPTED_DATA: 397 cinfo = &(c.encryptedData->contentInfo); 398 break; 399 case SEC_OID_PKCS7_DIGESTED_DATA: 400 cinfo = &(c.digestedData->contentInfo); 401 break; 402 default: 403 cinfo = NULL; 404 } 405 return cinfo; 406} 407 408// @@@ Return CFStringRef and do localization. 409const char * 410SecCmsUtilVerificationStatusToString(SecCmsVerificationStatus vs) 411{ 412 switch (vs) { 413 case SecCmsVSUnverified: return "Unverified"; 414 case SecCmsVSGoodSignature: return "GoodSignature"; 415 case SecCmsVSBadSignature: return "BadSignature"; 416 case SecCmsVSDigestMismatch: return "DigestMismatch"; 417 case SecCmsVSSigningCertNotFound: return "SigningCertNotFound"; 418 case SecCmsVSSigningCertNotTrusted: return "SigningCertNotTrusted"; 419 case SecCmsVSSignatureAlgorithmUnknown: return "SignatureAlgorithmUnknown"; 420 case SecCmsVSSignatureAlgorithmUnsupported: return "SignatureAlgorithmUnsupported"; 421 case SecCmsVSMalformedSignature: return "MalformedSignature"; 422 case SecCmsVSProcessingError: return "ProcessingError"; 423 default: return "Unknown"; 424 } 425} 426