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