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#include "cmslocal.h"
41
42#include "secitem.h"
43#include "secoid.h"
44#include "cryptohi.h"
45
46#include <security_asn1/secasn1.h>
47#include <security_asn1/secerr.h>
48#include <Security/cssmapi.h>
49#include <Security/cssmapple.h>
50#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
51
52
53/*
54 * SecCmsArraySortByDER - sort array of objects by objects' DER encoding
55 *
56 * make sure that the order of the objects guarantees valid DER (which must be
57 * in lexigraphically ascending order for a SET OF); if reordering is necessary it
58 * will be done in place (in objs).
59 */
60OSStatus
61SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2)
62{
63    PRArenaPool *poolp;
64    int num_objs;
65    CSSM_DATA_PTR *enc_objs;
66    OSStatus rv = SECFailure;
67    int i;
68
69    if (objs == NULL)					/* already sorted */
70	return SECSuccess;
71
72    num_objs = SecCmsArrayCount((void **)objs);
73    if (num_objs == 0 || num_objs == 1)		/* already sorted. */
74	return SECSuccess;
75
76    poolp = PORT_NewArena (1024);	/* arena for temporaries */
77    if (poolp == NULL)
78	return SECFailure;		/* no memory; nothing we can do... */
79
80    /*
81     * Allocate arrays to hold the individual encodings which we will use
82     * for comparisons and the reordered attributes as they are sorted.
83     */
84    enc_objs = (CSSM_DATA_PTR *)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(CSSM_DATA_PTR));
85    if (enc_objs == NULL)
86	goto loser;
87
88    /* DER encode each individual object. */
89    for (i = 0; i < num_objs; i++) {
90	enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate);
91	if (enc_objs[i] == NULL)
92	    goto loser;
93    }
94    enc_objs[num_objs] = NULL;
95
96    /* now compare and sort objs by the order of enc_objs */
97    SecCmsArraySort((void **)enc_objs, SecCmsUtilDERCompare, objs, objs2);
98
99    rv = SECSuccess;
100
101loser:
102    PORT_FreeArena (poolp, PR_FALSE);
103    return rv;
104}
105
106/*
107 * SecCmsUtilDERCompare - for use with SecCmsArraySort to
108 *  sort arrays of CSSM_DATAs containing DER
109 */
110int
111SecCmsUtilDERCompare(void *a, void *b)
112{
113    CSSM_DATA_PTR der1 = (CSSM_DATA_PTR)a;
114    CSSM_DATA_PTR der2 = (CSSM_DATA_PTR)b;
115    int j;
116
117    /*
118     * Find the lowest (lexigraphically) encoding.  One that is
119     * shorter than all the rest is known to be "less" because each
120     * attribute is of the same type (a SEQUENCE) and so thus the
121     * first octet of each is the same, and the second octet is
122     * the length (or the length of the length with the high bit
123     * set, followed by the length, which also works out to always
124     * order the shorter first).  Two (or more) that have the
125     * same length need to be compared byte by byte until a mismatch
126     * is found.
127     */
128    if (der1->Length != der2->Length)
129	return (der1->Length < der2->Length) ? -1 : 1;
130
131    for (j = 0; j < der1->Length; j++) {
132	if (der1->Data[j] == der2->Data[j])
133	    continue;
134	return (der1->Data[j] < der2->Data[j]) ? -1 : 1;
135    }
136    return 0;
137}
138
139/*
140 * SecCmsAlgArrayGetIndexByAlgID - find a specific algorithm in an array of
141 * algorithms.
142 *
143 * algorithmArray - array of algorithm IDs
144 * algid - algorithmid of algorithm to pick
145 *
146 * Returns:
147 *  An integer containing the index of the algorithm in the array or -1 if
148 *  algorithm was not found.
149 */
150int
151SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid)
152{
153    int i;
154
155    if (algorithmArray == NULL || algorithmArray[0] == NULL)
156	return -1;
157
158    for (i = 0; algorithmArray[i] != NULL; i++) {
159	if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual)
160	    break;	/* bingo */
161    }
162
163    if (algorithmArray[i] == NULL)
164	return -1;	/* not found */
165
166    return i;
167}
168
169/*
170 * SecCmsAlgArrayGetIndexByAlgTag - find a specific algorithm in an array of
171 * algorithms.
172 *
173 * algorithmArray - array of algorithm IDs
174 * algtag - algorithm tag of algorithm to pick
175 *
176 * Returns:
177 *  An integer containing the index of the algorithm in the array or -1 if
178 *  algorithm was not found.
179 */
180int
181SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray,
182                                 SECOidTag algtag)
183{
184    SECOidData *algid;
185    int i = -1;
186
187    if (algorithmArray == NULL || algorithmArray[0] == NULL)
188	return i;
189
190#ifdef ORDER_N_SQUARED
191    for (i = 0; algorithmArray[i] != NULL; i++) {
192	algid = SECOID_FindOID(&(algorithmArray[i]->algorithm));
193	if (algid->offset == algtag)
194	    break;	/* bingo */
195    }
196#else
197    algid = SECOID_FindOIDByTag(algtag);
198    if (!algid)
199    	return i;
200    for (i = 0; algorithmArray[i] != NULL; i++) {
201	if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid))
202	    break;	/* bingo */
203    }
204#endif
205
206    if (algorithmArray[i] == NULL)
207	return -1;	/* not found */
208
209    return i;
210}
211
212CSSM_CC_HANDLE
213SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
214{
215    SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
216    if (oidData)
217    {
218	CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
219	if (alg)
220	{
221	    CSSM_CC_HANDLE digobj;
222	    CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
223
224	    if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
225		return digobj;
226	}
227    }
228
229    return 0;
230}
231
232/*
233 * XXX I would *really* like to not have to do this, but the current
234 * signing interface gives me little choice.
235 */
236SECOidTag
237SecCmsUtilMakeSignatureAlgorithm(SECOidTag hashalg, SECOidTag encalg)
238{
239    switch (encalg) {
240      case SEC_OID_PKCS1_RSA_ENCRYPTION:
241	switch (hashalg) {
242	  case SEC_OID_MD2:
243	    return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
244	  case SEC_OID_MD5:
245	    return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
246	  case SEC_OID_SHA1:
247	    return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
248	  case SEC_OID_SHA256:
249	    return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
250	  case SEC_OID_SHA384:
251	    return SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
252	  case SEC_OID_SHA512:
253	    return SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
254	  default:
255	    return SEC_OID_UNKNOWN;
256	}
257      case SEC_OID_ANSIX9_DSA_SIGNATURE:
258      case SEC_OID_MISSI_KEA_DSS:
259      case SEC_OID_MISSI_DSS:
260	switch (hashalg) {
261	  case SEC_OID_SHA1:
262	    return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
263	  default:
264	    return SEC_OID_UNKNOWN;
265	}
266      case SEC_OID_EC_PUBLIC_KEY:
267	switch(hashalg) {
268	  /*
269	   * Note this is only used when signing and verifying signed attributes,
270	   * In which case we really do want the combined ECDSA_WithSHA1 alg...
271	   */
272	  case SEC_OID_SHA1:
273	    return SEC_OID_ECDSA_WithSHA1;
274	  default:
275	    return SEC_OID_UNKNOWN;
276	}
277      default:
278	break;
279    }
280
281    return encalg;		/* maybe it is already the right algid */
282}
283
284const SecAsn1Template *
285SecCmsUtilGetTemplateByTypeTag(SECOidTag type)
286{
287    const SecAsn1Template *template;
288    extern const SecAsn1Template SecCmsSignedDataTemplate[];
289    extern const SecAsn1Template SecCmsEnvelopedDataTemplate[];
290    extern const SecAsn1Template SecCmsEncryptedDataTemplate[];
291    extern const SecAsn1Template SecCmsDigestedDataTemplate[];
292
293    switch (type) {
294    case SEC_OID_PKCS7_SIGNED_DATA:
295	template = SecCmsSignedDataTemplate;
296	break;
297    case SEC_OID_PKCS7_ENVELOPED_DATA:
298	template = SecCmsEnvelopedDataTemplate;
299	break;
300    case SEC_OID_PKCS7_ENCRYPTED_DATA:
301	template = SecCmsEncryptedDataTemplate;
302	break;
303    case SEC_OID_PKCS7_DIGESTED_DATA:
304	template = SecCmsDigestedDataTemplate;
305	break;
306    default:
307    case SEC_OID_PKCS7_DATA:
308    case SEC_OID_OTHER:
309	template = NULL;
310	break;
311    }
312    return template;
313}
314
315size_t
316SecCmsUtilGetSizeByTypeTag(SECOidTag type)
317{
318    size_t size;
319
320    switch (type) {
321    case SEC_OID_PKCS7_SIGNED_DATA:
322	size = sizeof(SecCmsSignedData);
323	break;
324    case SEC_OID_PKCS7_ENVELOPED_DATA:
325	size = sizeof(SecCmsEnvelopedData);
326	break;
327    case SEC_OID_PKCS7_ENCRYPTED_DATA:
328	size = sizeof(SecCmsEncryptedData);
329	break;
330    case SEC_OID_PKCS7_DIGESTED_DATA:
331	size = sizeof(SecCmsDigestedData);
332	break;
333    default:
334    case SEC_OID_PKCS7_DATA:
335	size = 0;
336	break;
337    }
338    return size;
339}
340
341SecCmsContentInfoRef
342SecCmsContentGetContentInfo(void *msg, SECOidTag type)
343{
344    SecCmsContent c;
345    SecCmsContentInfoRef cinfo;
346
347    if (!msg)
348	return NULL;
349    c.pointer = msg;
350    switch (type) {
351    case SEC_OID_PKCS7_SIGNED_DATA:
352	cinfo = &(c.signedData->contentInfo);
353	break;
354    case SEC_OID_PKCS7_ENVELOPED_DATA:
355	cinfo = &(c.envelopedData->contentInfo);
356	break;
357    case SEC_OID_PKCS7_ENCRYPTED_DATA:
358	cinfo = &(c.encryptedData->contentInfo);
359	break;
360    case SEC_OID_PKCS7_DIGESTED_DATA:
361	cinfo = &(c.digestedData->contentInfo);
362	break;
363    default:
364	cinfo = NULL;
365    }
366    return cinfo;
367}
368
369// @@@ Return CFStringRef and do localization.
370const char *
371SecCmsUtilVerificationStatusToString(SecCmsVerificationStatus vs)
372{
373    switch (vs) {
374    case SecCmsVSUnverified:			return "Unverified";
375    case SecCmsVSGoodSignature:			return "GoodSignature";
376    case SecCmsVSBadSignature:			return "BadSignature";
377    case SecCmsVSDigestMismatch:		return "DigestMismatch";
378    case SecCmsVSSigningCertNotFound:		return "SigningCertNotFound";
379    case SecCmsVSSigningCertNotTrusted:		return "SigningCertNotTrusted";
380    case SecCmsVSSignatureAlgorithmUnknown:	return "SignatureAlgorithmUnknown";
381    case SecCmsVSSignatureAlgorithmUnsupported: return "SignatureAlgorithmUnsupported";
382    case SecCmsVSMalformedSignature:		return "MalformedSignature";
383    case SecCmsVSProcessingError:		return "ProcessingError";
384    default:					return "Unknown";
385    }
386}
387
388OSStatus
389SecArenaPoolCreate(size_t chunksize, SecArenaPoolRef *outArena)
390{
391    OSStatus status;
392
393    if (!outArena) {
394        status = paramErr;
395        goto loser;
396    }
397
398    *outArena = (SecArenaPoolRef)PORT_NewArena(chunksize);
399    if (*outArena)
400        status = 0;
401    else
402        status = PORT_GetError();
403
404loser:
405    return status;
406}
407
408void
409SecArenaPoolFree(SecArenaPoolRef arena, Boolean zero)
410{
411    PORT_FreeArena((PLArenaPool *)arena, zero);
412}
413