1/*
2 * crypto.h - public data structures and prototypes for the crypto library
3 *
4 * The contents of this file are subject to the Mozilla Public
5 * License Version 1.1 (the "License"); you may not use this file
6 * except in compliance with the License. You may obtain a copy of
7 * the License at http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS
10 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * rights and limitations under the License.
13 *
14 * The Original Code is the Netscape security libraries.
15 *
16 * The Initial Developer of the Original Code is Netscape
17 * Communications Corporation.  Portions created by Netscape are
18 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
19 * Rights Reserved.
20 *
21 * Contributor(s):
22 *
23 * Alternatively, the contents of this file may be used under the
24 * terms of the GNU General Public License Version 2 or later (the
25 * "GPL"), in which case the provisions of the GPL are applicable
26 * instead of those above.  If you wish to allow use of your
27 * version of this file only under the terms of the GPL and not to
28 * allow others to use your version of this file under the MPL,
29 * indicate your decision by deleting the provisions above and
30 * replace them with the notice and other provisions required by
31 * the GPL.  If you do not delete the provisions above, a recipient
32 * may use your version of this file under either the MPL or the
33 * GPL.
34 */
35
36#include "cryptohi.h"
37
38#include "secoid.h"
39#include "cmspriv.h"
40#include <security_asn1/secerr.h>
41#include <Security/cssmapi.h>
42#include <Security/cssmapi.h>
43#include <Security/SecKeyPriv.h>
44#include <Security/cssmapple.h>
45
46static CSSM_CSP_HANDLE gCsp = 0;
47static char gCssmInitialized = 0;
48
49/* @@@ Ugly hack casting, but the extra argument at the end will be ignored. */
50static CSSM_API_MEMORY_FUNCS memFuncs =
51{
52    (CSSM_MALLOC)malloc,
53    (CSSM_FREE)free,
54    (CSSM_REALLOC)realloc,
55    (CSSM_CALLOC)calloc,
56    NULL
57};
58
59/*
60 *
61 * SecCspHandleForAlgorithm
62 * @@@ This function should get more parameters like keysize and operation required and use mds.
63 *
64 */
65CSSM_CSP_HANDLE
66SecCspHandleForAlgorithm(CSSM_ALGORITHMS algorithm)
67{
68
69    if (!gCsp)
70    {
71	CSSM_VERSION version = { 2, 0 };
72	CSSM_RETURN rv;
73
74	if (!gCssmInitialized)
75	{
76	    CSSM_GUID myGuid = { 0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 } };
77	    CSSM_PVC_MODE pvcPolicy = CSSM_PVC_NONE;
78
79	    rv = CSSM_Init (&version, CSSM_PRIVILEGE_SCOPE_NONE, &myGuid, CSSM_KEY_HIERARCHY_NONE, &pvcPolicy, NULL);
80	    if (rv)
81		goto loser;
82	    gCssmInitialized = 1;
83	}
84
85	rv = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL);
86	if (rv)
87	    goto loser;
88	rv = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &memFuncs, 0, CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, NULL, 0, NULL, &gCsp);
89    }
90
91loser:
92    return gCsp;
93}
94
95CSSM_ALGORITHMS
96SECOID_FindyCssmAlgorithmByTag(SECOidTag algTag)
97{
98    const SECOidData *oidData = SECOID_FindOIDByTag(algTag);
99    return oidData ? oidData->cssmAlgorithm : CSSM_ALGID_NONE;
100}
101
102static SECStatus SEC_CssmRtnToSECStatus(CSSM_RETURN rv)
103{
104    CSSM_RETURN crtn = CSSM_ERRCODE(rv);
105    switch(crtn) {
106	case CSSM_ERRCODE_USER_CANCELED:
107	case CSSM_ERRCODE_OPERATION_AUTH_DENIED:
108	case CSSM_ERRCODE_OBJECT_USE_AUTH_DENIED:
109	    return SEC_ERROR_USER_CANCELLED;
110	case CSSM_ERRCODE_NO_USER_INTERACTION:
111	    return SEC_ERROR_NO_USER_INTERACTION;
112	case CSSMERR_CSP_KEY_USAGE_INCORRECT:
113	    return SEC_ERROR_INADEQUATE_KEY_USAGE;
114	default:
115	    fprintf(stderr, "CSSM_SignData returned: %08X\n", (uint32_t)rv);
116	    return SEC_ERROR_LIBRARY_FAILURE;
117    }
118}
119
120SECStatus
121SEC_SignData(SECItem *result, unsigned char *buf, int len,
122	    SecPrivateKeyRef pk, SECOidTag digAlgTag, SECOidTag sigAlgTag)
123{
124    const CSSM_ACCESS_CREDENTIALS *accessCred;
125    CSSM_ALGORITHMS algorithm;
126    CSSM_CC_HANDLE cc = 0;
127    CSSM_CSP_HANDLE csp;
128    OSStatus rv;
129    CSSM_DATA dataBuf = { (uint32)len, (uint8 *)buf };
130    CSSM_DATA sig = {};
131    const CSSM_KEY *key;
132
133    algorithm = SECOID_FindyCssmAlgorithmByTag(SecCmsUtilMakeSignatureAlgorithm(digAlgTag, sigAlgTag));
134    if (!algorithm)
135    {
136        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
137	rv = SECFailure;
138	goto loser;
139    }
140
141    rv = SecKeyGetCSPHandle(pk, &csp);
142    if (rv) {
143        PORT_SetError(SEC_ERROR_BAD_KEY);
144	goto loser;
145    }
146    rv = SecKeyGetCSSMKey(pk, &key);
147    if (rv) {
148        PORT_SetError(SEC_ERROR_BAD_KEY);
149	goto loser;
150    }
151    rv = SecKeyGetCredentials(pk, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &accessCred);
152    if (rv) {
153        PORT_SetError(SEC_ERROR_BAD_KEY);
154	goto loser;
155    }
156
157    rv = CSSM_CSP_CreateSignatureContext(csp, algorithm, accessCred, key, &cc);
158    if (rv) {
159        PORT_SetError(SEC_ERROR_NO_MEMORY);
160    	goto loser;
161    }
162
163    rv = CSSM_SignData(cc, &dataBuf, 1, CSSM_ALGID_NONE, &sig);
164    if (rv) {
165        SECErrorCodes code = SEC_CssmRtnToSECStatus(rv);
166        PORT_SetError(code);
167	goto loser;
168    }
169
170    result->Length = sig.Length;
171    result->Data = sig.Data;
172
173loser:
174    if (cc)
175	CSSM_DeleteContext(cc);
176
177    return rv;
178}
179
180SECStatus
181SGN_Digest(SecPrivateKeyRef pk, SECOidTag digAlgTag, SECOidTag sigAlgTag, SECItem *result, SECItem *digest)
182{
183    const CSSM_ACCESS_CREDENTIALS *accessCred;
184    CSSM_ALGORITHMS digalg, sigalg;
185    CSSM_CC_HANDLE cc = 0;
186    CSSM_CSP_HANDLE csp;
187    const CSSM_KEY *key;
188    CSSM_DATA sig = {};
189    OSStatus rv;
190
191    digalg = SECOID_FindyCssmAlgorithmByTag(digAlgTag);
192    sigalg = SECOID_FindyCssmAlgorithmByTag(sigAlgTag);
193    if (!digalg || !sigalg)
194    {
195        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
196	rv = SECFailure;
197	goto loser;
198    }
199
200    rv = SecKeyGetCSPHandle(pk, &csp);
201    if (rv) {
202        PORT_SetError(SEC_ERROR_BAD_KEY);
203	goto loser;
204    }
205    rv = SecKeyGetCSSMKey(pk, &key);
206    if (rv) {
207        PORT_SetError(SEC_ERROR_BAD_KEY);
208	goto loser;
209    }
210    rv = SecKeyGetCredentials(pk, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &accessCred);
211    if (rv) {
212        PORT_SetError(SEC_ERROR_BAD_KEY);
213	goto loser;
214    }
215
216    rv = CSSM_CSP_CreateSignatureContext(csp, sigalg, accessCred, key, &cc);
217    if (rv) {
218        PORT_SetError(SEC_ERROR_NO_MEMORY);
219	goto loser;
220    }
221
222    rv = CSSM_SignData(cc, digest, 1, digalg, &sig);
223    if (rv) {
224        SECErrorCodes code = SEC_CssmRtnToSECStatus(rv);
225        PORT_SetError(code);
226	goto loser;
227    }
228
229    result->Length = sig.Length;
230    result->Data = sig.Data;
231
232loser:
233    if (cc)
234	CSSM_DeleteContext(cc);
235
236    return rv;
237}
238
239SECStatus
240VFY_VerifyData(unsigned char *buf, int len,
241		SecPublicKeyRef pk, SECItem *sig,
242		SECOidTag digAlgTag, SECOidTag sigAlgTag, void *wincx)
243{
244    SECOidTag algTag;
245    CSSM_ALGORITHMS algorithm;
246    CSSM_CC_HANDLE cc = 0;
247    CSSM_CSP_HANDLE csp;
248    OSStatus rv = SECFailure;
249    CSSM_DATA dataBuf = { (uint32)len, (uint8 *)buf };
250    const CSSM_KEY *key;
251
252    algTag = SecCmsUtilMakeSignatureAlgorithm(digAlgTag, sigAlgTag);
253    algorithm = SECOID_FindyCssmAlgorithmByTag(algTag);
254    if (!algorithm)
255    {
256	rv = algTag == SEC_OID_UNKNOWN ? SecCmsVSSignatureAlgorithmUnknown : SecCmsVSSignatureAlgorithmUnsupported;
257	goto loser;
258    }
259
260    rv = SecKeyGetCSPHandle(pk, &csp);
261    if (rv)
262	goto loser;
263    rv = SecKeyGetCSSMKey(pk, &key);
264    if (rv)
265	goto loser;
266
267    rv = CSSM_CSP_CreateSignatureContext(csp, algorithm, NULL, key, &cc);
268    if (rv)
269	goto loser;
270
271    rv = CSSM_VerifyData(cc, &dataBuf, 1, CSSM_ALGID_NONE, sig);
272
273loser:
274    if (cc)
275	CSSM_DeleteContext(cc);
276
277    return rv;
278}
279
280SECStatus
281VFY_VerifyDigest(SECItem *digest, SecPublicKeyRef pk,
282		SECItem *sig, SECOidTag digAlgTag, SECOidTag sigAlgTag, void *wincx)
283{
284    CSSM_ALGORITHMS sigalg, digalg;
285    CSSM_CC_HANDLE cc = 0;
286    CSSM_CSP_HANDLE csp;
287    const CSSM_KEY *key;
288    OSStatus rv;
289
290    digalg = SECOID_FindyCssmAlgorithmByTag(digAlgTag);
291    sigalg = SECOID_FindyCssmAlgorithmByTag(sigAlgTag);
292    if (!digalg || !sigalg)
293    {
294	rv = digAlgTag == SEC_OID_UNKNOWN  || sigAlgTag == SEC_OID_UNKNOWN ? SecCmsVSSignatureAlgorithmUnknown : SecCmsVSSignatureAlgorithmUnsupported;
295	goto loser;
296    }
297
298    rv = SecKeyGetCSPHandle(pk, &csp);
299    if (rv)
300	goto loser;
301    rv = SecKeyGetCSSMKey(pk, &key);
302    if (rv)
303	goto loser;
304
305    rv = CSSM_CSP_CreateSignatureContext(csp, sigalg, NULL, key, &cc);
306    if (rv)
307	goto loser;
308
309    rv = CSSM_VerifyData(cc, digest, 1, digalg, sig);
310
311loser:
312    if (cc)
313	CSSM_DeleteContext(cc);
314
315    return rv;
316}
317
318SECStatus
319WRAP_PubWrapSymKey(SecPublicKeyRef publickey,
320		   SecSymmetricKeyRef bulkkey,
321		   CSSM_DATA_PTR encKey)
322{
323    CSSM_WRAP_KEY wrappedKey = {};
324    //CSSM_WRAP_KEY wrappedPk = {}
325    //CSSM_KEY upk = {};
326    CSSM_CC_HANDLE cc = 0;
327    CSSM_CSP_HANDLE pkCsp, bkCsp;
328    const CSSM_KEY *pk, *bk, *pubkey;
329    OSStatus rv;
330    CSSM_ACCESS_CREDENTIALS accessCred = {};
331
332    rv = SecKeyGetCSPHandle(publickey, &pkCsp);
333    if (rv)
334	goto loser;
335    rv = SecKeyGetCSSMKey(publickey, &pk);
336    if (rv)
337	goto loser;
338
339    rv = SecKeyGetCSPHandle(bulkkey, &bkCsp);
340    if (rv)
341	goto loser;
342    rv = SecKeyGetCSSMKey(bulkkey, &bk);
343    if (rv)
344	goto loser;
345
346#if 1
347    pubkey = pk;
348#else
349    /* We need to get the publickey out of it's pkCsp and into the bkCsp so we can operate with it. */
350
351    /* Make a NULL wrap symmetric context to extract the public key from pkCsp. */
352    rv = CSSM_CSP_CreateSymmetricContext(pkCsp,
353	    CSSM_ALGID_NONE,
354	    CSSM_MODE_NONE,
355	    NULL, /* accessCred */
356	    NULL, /* key */
357	    NULL, /* iv */
358	    CSSM_PADDING_NONE,
359	    NULL, /* reserved */
360	    &cc);
361    if (rv)
362	goto loser;
363    rv = CSSM_WrapKey(cc,
364	    NULL /* accessCred */,
365	    pk,
366	    NULL /* descriptiveData */,
367	    &wrappedPk);
368    CSSM_DeleteContext(cc);
369    cc = 0;
370
371    /* Make a NULL unwrap symmetric context to import the public key into bkCsp. */
372    rv = CSSM_CSP_CreateSymmetricContext(bkCsp,
373	    CSSM_ALGID_NONE,
374	    CSSM_MODE_NONE,
375	    NULL, /* accessCred */
376	    NULL, /* key */
377	    NULL, /* iv */
378	    CSSM_PADDING_NONE,
379	    NULL, /* reserved */
380	    &cc);
381    if (rv)
382	goto loser;
383    rv = CSSM_UnwrapKey(cc, NULL, &wrappedPk, usage, attr, NULL /* label */, NULL /* rcc */, &upk, NULL /* descriptiveData */);
384    CSSM_DeleteContext(cc);
385    cc = 0;
386
387    pubkey  = &upk;
388#endif
389
390    rv = CSSM_CSP_CreateAsymmetricContext(bkCsp,
391	    pubkey->KeyHeader.AlgorithmId,
392	    &accessCred,
393	    pubkey,
394	    CSSM_PADDING_PKCS1,
395	    &cc);
396    if (rv)
397	goto loser;
398
399    {
400	/* Set the wrapped key format to indicate we want just the raw bits encrypted. */
401	CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, sizeof(uint32) };
402	contextAttribute.Attribute.Uint32 = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
403	rv = CSSM_UpdateContextAttributes(cc, 1, &contextAttribute);
404	if (rv)
405	    goto loser;
406    }
407
408    {
409	/* Set the mode to CSSM_ALGMODE_PKCS1_EME_V15. */
410	CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_MODE, sizeof(uint32) };
411	contextAttribute.Attribute.Uint32 = CSSM_ALGMODE_NONE; /* CSSM_ALGMODE_PKCS1_EME_V15 */
412	rv = CSSM_UpdateContextAttributes(cc, 1, &contextAttribute);
413	if (rv)
414	    goto loser;
415    }
416
417    {
418	// @@@ Stick in an empty initVector to work around a csp bug.
419	CSSM_DATA initVector = {};
420	CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_INIT_VECTOR, sizeof(CSSM_DATA_PTR) };
421	contextAttribute.Attribute.Data = &initVector;
422	rv = CSSM_UpdateContextAttributes(cc, 1, &contextAttribute);
423	if (rv)
424	    goto loser;
425    }
426
427    rv = CSSM_WrapKey(cc,
428	    &accessCred,
429	    bk,
430	    NULL, /* descriptiveData */
431	    &wrappedKey);
432    if (rv)
433	goto loser;
434
435    // @@@ Fix leaks!
436    if (encKey->Length < wrappedKey.KeyData.Length)
437	abort();
438    encKey->Length = wrappedKey.KeyData.Length;
439    memcpy(encKey->Data, wrappedKey.KeyData.Data, encKey->Length);
440    CSSM_FreeKey(bkCsp, NULL /* credentials */, &wrappedKey, FALSE);
441
442loser:
443    if (cc)
444	CSSM_DeleteContext(cc);
445
446    return rv;
447}
448
449SecSymmetricKeyRef
450WRAP_PubUnwrapSymKey(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag)
451{
452    SecSymmetricKeyRef bulkkey = NULL;
453    CSSM_WRAP_KEY wrappedKey = {};
454    CSSM_CC_HANDLE cc = 0;
455    CSSM_CSP_HANDLE pkCsp;
456    const CSSM_KEY *pk;
457    CSSM_KEY unwrappedKey = {};
458    const CSSM_ACCESS_CREDENTIALS *accessCred;
459    CSSM_DATA descriptiveData = {};
460    CSSM_ALGORITHMS bulkalg;
461    OSStatus rv;
462
463    rv = SecKeyGetCSPHandle(privkey, &pkCsp);
464    if (rv)
465	goto loser;
466    rv = SecKeyGetCSSMKey(privkey, &pk);
467    if (rv)
468	goto loser;
469    rv = SecKeyGetCredentials(privkey,
470	    CSSM_ACL_AUTHORIZATION_DECRYPT, /* @@@ Should be UNWRAP */
471	    kSecCredentialTypeDefault,
472	    &accessCred);
473    if (rv)
474	goto loser;
475
476    bulkalg = SECOID_FindyCssmAlgorithmByTag(bulkalgtag);
477    if (!bulkalg)
478    {
479	rv = SEC_ERROR_INVALID_ALGORITHM;
480	goto loser;
481    }
482
483    rv = CSSM_CSP_CreateAsymmetricContext(pkCsp,
484	    pk->KeyHeader.AlgorithmId,
485	    accessCred,
486	    pk,
487	    CSSM_PADDING_PKCS1,
488	    &cc);
489    if (rv)
490	goto loser;
491
492    {
493	// @@@ Stick in an empty initvector to work around a csp bug.
494	CSSM_DATA initVector = {};
495	CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_INIT_VECTOR, sizeof(CSSM_DATA_PTR) };
496	contextAttribute.Attribute.Data = &initVector;
497	rv = CSSM_UpdateContextAttributes(cc, 1, &contextAttribute);
498	if (rv)
499	    goto loser;
500    }
501
502    wrappedKey.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
503    wrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
504    wrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
505    wrappedKey.KeyHeader.AlgorithmId = bulkalg;
506    wrappedKey.KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
507    wrappedKey.KeyHeader.WrapAlgorithmId = pk->KeyHeader.AlgorithmId;
508    wrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE; /* CSSM_ALGMODE_PKCS1_EME_V15 */
509    wrappedKey.KeyData = *encKey;
510
511    rv = CSSM_UnwrapKey(cc,
512	    NULL, /* publicKey */
513	    &wrappedKey,
514	    CSSM_KEYUSE_DECRYPT,
515	    CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */,
516	    NULL, /* keyLabel */
517	    NULL, /* rcc */
518	    &unwrappedKey,
519	    &descriptiveData);
520    if (rv) {
521        SECErrorCodes code;
522        if (CSSM_ERRCODE(rv) == CSSM_ERRCODE_USER_CANCELED
523            || CSSM_ERRCODE(rv) == CSSM_ERRCODE_OPERATION_AUTH_DENIED
524	    || CSSM_ERRCODE(rv) == CSSM_ERRCODE_OBJECT_USE_AUTH_DENIED)
525            code = SEC_ERROR_USER_CANCELLED;
526        else if (CSSM_ERRCODE(rv) == CSSM_ERRCODE_NO_USER_INTERACTION
527                 || rv == CSSMERR_CSP_KEY_USAGE_INCORRECT)
528            code = SEC_ERROR_INADEQUATE_KEY_USAGE;
529        else
530	{
531	    fprintf(stderr, "CSSM_UnwrapKey returned: %08X\n", (uint32_t)rv);
532            code = SEC_ERROR_LIBRARY_FAILURE;
533	}
534
535        PORT_SetError(code);
536	goto loser;
537    }
538
539    // @@@ Export this key from the csp/dl and import it to the standard csp
540    rv = SecKeyCreateWithCSSMKey(&unwrappedKey, &bulkkey);
541    if (rv)
542	goto loser;
543
544loser:
545    if (rv)
546	PORT_SetError(rv);
547
548    if (cc)
549	CSSM_DeleteContext(cc);
550
551    return bulkkey;
552}
553