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 public key crypto
36 */
37
38#include "cmslocal.h"
39
40#include "secitem.h"
41#include "secoid.h"
42#include "cryptohi.h"
43
44#include <security_asn1/secasn1.h>
45#include <security_asn1/secerr.h>
46#include <Security/SecCertificatePriv.h>
47#include <Security/SecKeyPriv.h>
48#include <Security/Security.h>
49#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
50#include <Security/SecCmsBase.h>
51#include <Security/secasn1t.h>
52#include <security_asn1/plarenas.h>
53#include <Security/keyTemplates.h>
54
55/* ====== RSA ======================================================================= */
56
57/*
58 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
59 *
60 * this function takes a symmetric key and encrypts it using an RSA public key
61 * according to PKCS#1 and RFC2633 (S/MIME)
62 */
63OSStatus
64SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
65                              SecSymmetricKeyRef bulkkey,
66                              CSSM_DATA_PTR encKey)
67{
68    OSStatus rv;
69    SecPublicKeyRef publickey;
70
71    rv = SecCertificateCopyPublicKey(cert,&publickey);
72    if (publickey == NULL)
73	return SECFailure;
74
75    rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
76    CFRelease(publickey);
77    return rv;
78}
79
80OSStatus
81SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
82				 SecPublicKeyRef publickey,
83				 SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey)
84{
85    OSStatus rv;
86    unsigned int data_len;
87    //KeyType keyType;
88    void *mark = NULL;
89
90    mark = PORT_ArenaMark(poolp);
91    if (!mark)
92	goto loser;
93
94#if 0
95    /* sanity check */
96    keyType = SECKEY_GetPublicKeyType(publickey);
97    PORT_Assert(keyType == rsaKey);
98    if (keyType != rsaKey) {
99	goto loser;
100    }
101#endif
102    /* allocate memory for the encrypted key */
103    rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len);
104    if (rv)
105	goto loser;
106
107    // Convert length to bytes;
108    data_len >>= 2;
109    encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
110    encKey->Length = data_len;
111    if (encKey->Data == NULL)
112	goto loser;
113
114    /* encrypt the key now */
115    rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey);
116    if (rv != SECSuccess)
117	goto loser;
118
119    PORT_ArenaUnmark(poolp, mark);
120    return SECSuccess;
121
122loser:
123    if (mark) {
124	PORT_ArenaRelease(poolp, mark);
125    }
126    return SECFailure;
127}
128
129/*
130 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
131 *
132 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
133 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
134 * a hardware token...
135 */
136SecSymmetricKeyRef
137SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag)
138{
139    /* that's easy */
140    return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag);
141}
142
143#if 0
144// @@@ Implement Fortezza and Diffie hellman support
145
146/* ====== MISSI (Fortezza) ========================================================== */
147
148extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[];
149
150OSStatus
151SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey,
152			SECOidTag symalgtag, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg)
153{
154    SECOidTag certalgtag;	/* the certificate's encryption algorithm */
155    SECOidTag encalgtag;	/* the algorithm used for key exchange/agreement */
156    OSStatus rv = SECFailure;
157    CSSM_DATA_PTR params = NULL;
158    OSStatus err;
159    SecSymmetricKeyRef tek;
160    SecCertificateRef ourCert;
161    SecPublicKeyRef ourPubKey, *publickey = NULL;
162    SecPrivateKeyRef ourPrivKey = NULL;
163    SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
164    SecCmsSMIMEKEAParameters keaParams;
165    PLArenaPool *arena = NULL;
166    extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate);
167    const SECAlgorithmID *algid;
168
169    /* Clear keaParams, since cleanup code checks the lengths */
170    (void) memset(&keaParams, 0, sizeof(keaParams));
171
172    SecCertificateGetAlgorithmID(cert,&algid);
173    certalgtag = SECOID_GetAlgorithmTag(algid);
174    PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD ||
175		certalgtag == SEC_OID_MISSI_KEA_DSS ||
176		certalgtag == SEC_OID_MISSI_KEA);
177
178#define SMIME_FORTEZZA_RA_LENGTH 128
179#define SMIME_FORTEZZA_IV_LENGTH 24
180#define SMIME_FORTEZZA_MAX_KEY_SIZE 256
181
182    /* We really want to show our KEA tag as the key exchange algorithm tag. */
183    encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
184
185    /* Get the public key of the recipient. */
186    publickey = CERT_ExtractPublicKey(cert);
187    if (publickey == NULL) goto loser;
188
189    /* Find our own cert, and extract its keys. */
190    ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg);
191    if (ourCert == NULL) goto loser;
192
193    arena = PORT_NewArena(1024);
194    if (arena == NULL)
195	goto loser;
196
197    ourPubKey = CERT_ExtractPublicKey(ourCert);
198    if (ourPubKey == NULL) {
199	CERT_DestroyCertificate(ourCert);
200	goto loser;
201    }
202
203    /* While we're here, copy the public key into the outgoing
204     * KEA parameters. */
205    SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey));
206    SECKEY_DestroyPublicKey(ourPubKey);
207    ourPubKey = NULL;
208
209    /* Extract our private key in order to derive the KEA key. */
210    ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg);
211    CERT_DestroyCertificate(ourCert); /* we're done with this */
212    if (!ourPrivKey)
213	goto loser;
214
215    /* Prepare raItem with 128 bytes (filled with zeros). */
216    keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
217    keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH;
218
219    /* Generate the TEK (token exchange key) which we use
220     * to wrap the bulk encryption key. (keaparams.originatorRA) will be
221     * filled with a random seed which we need to send to
222     * the recipient. (user keying material in RFC2630/DSA speak) */
223    tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
224			 &keaParams.originatorRA, NULL,
225			 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
226			 CKA_WRAP, 0,  pwfn_arg);
227
228    SECKEY_DestroyPublicKey(publickey);
229    SECKEY_DestroyPrivateKey(ourPrivKey);
230    publickey = NULL;
231    ourPrivKey = NULL;
232
233    if (!tek)
234	goto loser;
235
236    /* allocate space for the wrapped key data */
237    encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
238    encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
239
240    if (encKey->Data == NULL) {
241	CFRelease(tek);
242	goto loser;
243    }
244
245    /* Wrap the bulk key. What we do with the resulting data
246       depends on whether we're using Skipjack to wrap the key. */
247    switch (PK11_AlgtagToMechanism(symalgtag)) {
248    case CKM_SKIPJACK_CBC64:
249    case CKM_SKIPJACK_ECB64:
250    case CKM_SKIPJACK_OFB64:
251    case CKM_SKIPJACK_CFB64:
252    case CKM_SKIPJACK_CFB32:
253    case CKM_SKIPJACK_CFB16:
254    case CKM_SKIPJACK_CFB8:
255	/* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */
256	err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey);
257	whichKEA = SecCmsKEAUsesSkipjack;
258	break;
259    default:
260	/* Not SKIPJACK, we encrypt the raw key data */
261	keaParams.nonSkipjackIV.Data =
262	  (unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH);
263	keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH;
264	err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey);
265	if (err != SECSuccess)
266	    goto loser;
267
268	if (encKey->Length != PK11_GetKeyLength(bulkkey)) {
269	    /* The size of the encrypted key is not the same as
270	       that of the original bulk key, presumably due to
271	       padding. Encode and store the real size of the
272	       bulk key. */
273	    if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL)
274		err = (OSStatus)PORT_GetError();
275	    else
276		/* use full template for encoding */
277		whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey;
278	}
279	else
280	    /* enc key length == bulk key length */
281	    whichKEA = SecCmsKEAUsesNonSkipjack;
282	break;
283    }
284
285    CFRelease(tek);
286
287    if (err != SECSuccess)
288	goto loser;
289
290    PORT_Assert(whichKEA != SecCmsKEAInvalid);
291
292    /* Encode the KEA parameters into the recipient info. */
293    params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA));
294    if (params == NULL)
295	goto loser;
296
297    /* pass back the algorithm params */
298    *pparams = params;
299
300    rv = SECSuccess;
301
302loser:
303    if (arena)
304	PORT_FreeArena(arena, PR_FALSE);
305    if (publickey)
306        SECKEY_DestroyPublicKey(publickey);
307    if (ourPrivKey)
308        SECKEY_DestroyPrivateKey(ourPrivKey);
309    return rv;
310}
311
312SecSymmetricKeyRef
313SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
314{
315    /* fortezza: do a key exchange */
316    OSStatus err;
317    CK_MECHANISM_TYPE bulkType;
318    SecSymmetricKeyRef tek;
319    SecPublicKeyRef originatorPubKey;
320    SecCmsSMIMEKEAParameters keaParams;
321    SecSymmetricKeyRef bulkkey;
322    int bulkLength;
323
324    (void) memset(&keaParams, 0, sizeof(keaParams));
325
326    /* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
327       All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
328
329    /* Decode the KEA algorithm parameters. */
330    err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams,
331			     &(keyEncAlg->parameters));
332    if (err != SECSuccess)
333	goto loser;
334
335    /* get originator's public key */
336   originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
337			   keaParams.originatorKEAKey.Length);
338   if (originatorPubKey == NULL)
339	  goto loser;
340
341   /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
342      The Derive function generates a shared secret and combines it with the originatorRA
343      data to come up with an unique session key */
344   tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
345			 &keaParams.originatorRA, NULL,
346			 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
347			 CKA_WRAP, 0, pwfn_arg);
348   SECKEY_DestroyPublicKey(originatorPubKey);	/* not needed anymore */
349   if (tek == NULL)
350	goto loser;
351
352    /* Now that we have the TEK, unwrap the bulk key
353       with which to decrypt the message. We have to
354       do one of two different things depending on
355       whether Skipjack was used for *bulk* encryption
356       of the message. */
357    bulkType = PK11_AlgtagToMechanism(bulkalgtag);
358    switch (bulkType) {
359    case CKM_SKIPJACK_CBC64:
360    case CKM_SKIPJACK_ECB64:
361    case CKM_SKIPJACK_OFB64:
362    case CKM_SKIPJACK_CFB64:
363    case CKM_SKIPJACK_CFB32:
364    case CKM_SKIPJACK_CFB16:
365    case CKM_SKIPJACK_CFB8:
366	/* Skipjack is being used as the bulk encryption algorithm.*/
367	/* Unwrap the bulk key. */
368	bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
369				    encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
370	break;
371    default:
372	/* Skipjack was not used for bulk encryption of this
373	   message. Use Skipjack CBC64, with the nonSkipjackIV
374	   part of the KEA key parameters, to decrypt
375	   the bulk key. If the optional parameter bulkKeySize is present,
376	   bulk key size is different than the encrypted key size */
377	if (keaParams.bulkKeySize.Length > 0) {
378	    err = SEC_ASN1DecodeItem(NULL, &bulkLength,
379				     SEC_ASN1_GET(SEC_IntegerTemplate),
380				     &keaParams.bulkKeySize);
381	    if (err != SECSuccess)
382		goto loser;
383	}
384
385	bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV,
386				    encKey, bulkType, CKA_DECRYPT, bulkLength);
387	break;
388    }
389    return bulkkey;
390loser:
391    return NULL;
392}
393
394/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
395
396OSStatus
397SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key,
398			CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg,
399			CSSM_DATA_PTR pubKey)
400{
401#if 0 /* not yet done */
402    SECOidTag certalgtag;	/* the certificate's encryption algorithm */
403    SECOidTag encalgtag;	/* the algorithm used for key exchange/agreement */
404    OSStatus rv;
405    CSSM_DATA_PTR params = NULL;
406    int data_len;
407    OSStatus err;
408    SecSymmetricKeyRef tek;
409    SecCertificateRef ourCert;
410    SecPublicKeyRef ourPubKey;
411    SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
412
413    certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
414    PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
415
416    /* We really want to show our KEA tag as the key exchange algorithm tag. */
417    encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
418
419    /* Get the public key of the recipient. */
420    publickey = CERT_ExtractPublicKey(cert);
421    if (publickey == NULL) goto loser;
422
423    /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
424    /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
425    if (ourCert == NULL) goto loser;
426
427    arena = PORT_NewArena(1024);
428    if (arena == NULL) goto loser;
429
430    /* While we're here, extract the key pair's public key data and copy it into */
431    /* the outgoing parameters. */
432    /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
433    if (ourPubKey == NULL)
434    {
435	goto loser;
436    }
437    SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
438    SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
439    ourPubKey = NULL;
440
441    /* Extract our private key in order to derive the KEA key. */
442    ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
443    CERT_DestroyCertificate(ourCert); /* we're done with this */
444    if (!ourPrivKey) goto loser;
445
446    /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
447    if (ukm) {
448	ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
449	ukm->Length = /* XXXX */;
450    }
451
452    /* Generate the KEK (key exchange key) according to RFC2631 which we use
453     * to wrap the bulk encryption key. */
454    kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
455			 ukm, NULL,
456			 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
457			 CKA_WRAP, 0, wincx);
458
459    SECKEY_DestroyPublicKey(publickey);
460    SECKEY_DestroyPrivateKey(ourPrivKey);
461    publickey = NULL;
462    ourPrivKey = NULL;
463
464    if (!kek)
465	goto loser;
466
467    /* allocate space for the encrypted CEK (bulk key) */
468    encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
469    encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
470
471    if (encKey->Data == NULL)
472    {
473	CFRelease(kek);
474	goto loser;
475    }
476
477
478    /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
479    /* bulk encryption algorithm */
480    switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
481    {
482    case /* XXXX */CKM_SKIPJACK_CFB8:
483	err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
484	whichKEA = SecCmsKEAUsesSkipjack;
485	break;
486    case /* XXXX */CKM_SKIPJACK_CFB8:
487	err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
488	whichKEA = SecCmsKEAUsesSkipjack;
489	break;
490    default:
491	/* XXXX what do we do here? Neither RC2 nor 3DES... */
492        err = SECFailure;
493        /* set error */
494	break;
495    }
496
497    CFRelease(kek);	/* we do not need the KEK anymore */
498    if (err != SECSuccess)
499	goto loser;
500
501    PORT_Assert(whichKEA != SecCmsKEAInvalid);
502
503    /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
504    /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
505    params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
506    if (params == NULL)
507	goto loser;
508
509    /* now set keyEncAlg */
510    rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
511    if (rv != SECSuccess)
512	goto loser;
513
514    /* XXXXXXX this is not right yet */
515loser:
516    if (arena) {
517	PORT_FreeArena(arena, PR_FALSE);
518    }
519    if (publickey) {
520        SECKEY_DestroyPublicKey(publickey);
521    }
522    if (ourPrivKey) {
523        SECKEY_DestroyPrivateKey(ourPrivKey);
524    }
525#endif
526    return SECFailure;
527}
528
529SecSymmetricKeyRef
530SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
531{
532#if 0 /* not yet done */
533    OSStatus err;
534    CK_MECHANISM_TYPE bulkType;
535    SecSymmetricKeyRef tek;
536    SecPublicKeyRef originatorPubKey;
537    SecCmsSMIMEKEAParameters keaParams;
538
539   /* XXXX get originator's public key */
540   originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
541			   keaParams.originatorKEAKey.Length);
542   if (originatorPubKey == NULL)
543      goto loser;
544
545   /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
546      The Derive function generates a shared secret and combines it with the originatorRA
547      data to come up with an unique session key */
548   tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
549			 &keaParams.originatorRA, NULL,
550			 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
551			 CKA_WRAP, 0, pwfn_arg);
552   SECKEY_DestroyPublicKey(originatorPubKey);	/* not needed anymore */
553   if (tek == NULL)
554	goto loser;
555
556    /* Now that we have the TEK, unwrap the bulk key
557       with which to decrypt the message. */
558    /* Skipjack is being used as the bulk encryption algorithm.*/
559    /* Unwrap the bulk key. */
560    bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
561				encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
562
563    return bulkkey;
564
565loser:
566#endif
567    return NULL;
568}
569
570#endif	/* Fortezza, DIffie-Hellman */
571
572#define CFRELEASE(cf)	if(cf != NULL) { CFRelease(cf); }
573
574/* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
575
576#pragma mark ---- ECDH support functions ----
577
578#ifdef	NDEBUG
579#define CSSM_PERROR(f, r)
580#define dprintf(args...)
581#else
582#define CSSM_PERROR(f, r)   cssmPerror(f, r)
583#define dprintf(args...)    printf(args)
584#endif
585
586/* Length of KeyAgreeRecipientInfo.ukm we create */
587#define UKM_LENGTH	8
588
589/* KEK algorithm info we generate */
590#define ECDH_KEK_ALG_TAG	    SEC_OID_DES_EDE3_CBC
591#define ECDH_KEK_KEY_CSSM_ALGID	    CSSM_ALGID_3DES_3KEY
592#define ECDH_KEK_ENCR_CSSM_ALGID    CSSM_ALGID_3DES_3KEY_EDE
593#define ECDH_KEK_KEY_LEN_BYTES	    24
594#define ECDH_KEK_IV_LEN_BYTES	    8
595
596#define CMS_DUMP_BUFS	    0
597#if	CMS_DUMP_BUFS
598
599static void dumpBuf(
600    const char *label,
601    const CSSM_DATA *cd)
602{
603    unsigned dex;
604
605    printf("%s:\n   ", label);
606    for(dex=0; dex<cd->Length; dex++) {
607	printf("%02X ", cd->Data[dex]);
608	if(((dex % 16) == 15) && (dex != (cd->Length - 1))) {
609	    printf("\n   ");
610	}
611    }
612    putchar('\n');
613}
614
615#else
616#define dumpBuf(l, d)
617#endif	/* CMS_DUMP_BUFS */
618
619/*
620 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
621 * template for DER encoding and decoding it.
622 */
623typedef struct {
624    SECAlgorithmID  algId;	    /* KEK alg, NULL params */
625    CSSM_DATA	    entityUInfo;    /* optional, ukm */
626    CSSM_DATA	    suppPubInfo;    /* length of KEK in bits as 4-byte integer */
627} ECC_CMS_SharedInfo;
628
629static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = {
630    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) },
631    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
632	  offsetof(ECC_CMS_SharedInfo,entityUInfo),
633	  kSecAsn1OctetStringTemplate },
634    { SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2,
635	  offsetof(ECC_CMS_SharedInfo,suppPubInfo),
636	  kSecAsn1OctetStringTemplate },
637    { 0 }
638};
639
640/*
641 * Given a context specified via a CSSM_CC_HANDLE, add a new
642 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
643 * AttributeLength, and an untyped pointer.
644 */
645/* specify either 32-bit integer or a pointer as an added attribute value */
646typedef enum {
647	CAT_Uint32,
648	CAT_Ptr
649} ContextAttrType;
650
651static CSSM_RETURN cmsAddContextAttribute(
652    CSSM_CC_HANDLE CCHandle,
653    uint32 AttributeType,
654    uint32 AttributeLength,
655    ContextAttrType attrType,
656    /* specify exactly one of these */
657    const void *AttributePtr,
658    uint32 attributeInt)
659{
660    CSSM_CONTEXT_ATTRIBUTE		newAttr;
661    CSSM_RETURN					crtn;
662
663    newAttr.AttributeType     = AttributeType;
664    newAttr.AttributeLength   = AttributeLength;
665    if(attrType == CAT_Uint32) {
666	    newAttr.Attribute.Uint32  = attributeInt;
667    }
668    else {
669	    /* this is a union of a bunch of different pointers...*/
670	    newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
671    }
672    crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
673    if(crtn) {
674	CSSM_PERROR("CSSM_UpdateContextAttributes", crtn);
675    }
676    return crtn;
677}
678
679static CSSM_RETURN cmsGenRand(
680    CSSM_CSP_HANDLE cspHand,
681    CSSM_SIZE len,
682    uint8 *randOut)
683{
684    CSSM_CC_HANDLE ccHand = 0;
685    CSSM_DATA randData = {len, randOut};
686
687    CSSM_RETURN crtn = CSSM_CSP_CreateRandomGenContext(cspHand,
688	    CSSM_ALGID_APPLE_YARROW,
689	    NULL, /* seed*/
690	    len,
691	    &ccHand);
692    if(crtn) {
693	CSSM_PERROR("CSSM_CSP_CreateRandomGenContext", crtn);
694	return crtn;
695    }
696    crtn = CSSM_GenerateRandom(ccHand, &randData);
697    CSSM_DeleteContext(ccHand);
698    if(crtn) {
699	CSSM_PERROR("CSSM_GenerateRandom", crtn);
700    }
701    return crtn;
702}
703
704/* convert uint32 to big-endian 4 bytes */
705static void int32ToBytes(
706	uint32_t i,
707	unsigned char *b)
708{
709    int dex;
710    for(dex=3; dex>=0; dex--) {
711	    b[dex] = i;
712	    i >>= 8;
713    }
714}
715
716/*
717 * NULL wrap a ref key to raw key in default format.
718 */
719static OSStatus cmsNullWrapKey(
720    CSSM_CSP_HANDLE cspHand,
721    const CSSM_KEY *refKey,
722    CSSM_KEY_PTR rawKey)
723{
724    CSSM_DATA descData = {0, 0};
725    CSSM_RETURN crtn;
726    CSSM_CC_HANDLE ccHand;
727    CSSM_ACCESS_CREDENTIALS creds;
728    uint32 keyAttr;
729
730    memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
731    memset(rawKey, 0, sizeof(CSSM_KEY));
732
733    crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
734		    CSSM_ALGID_NONE,
735		    CSSM_ALGMODE_NONE,
736		    &creds,
737		    NULL,			// unwrappingKey
738		    NULL,			// initVector
739		    CSSM_PADDING_NONE,
740		    0,				// Params
741		    &ccHand);
742    if(crtn) {
743	CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", crtn);
744	return crtn;
745    }
746
747    keyAttr = rawKey->KeyHeader.KeyAttr;
748    keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
749			     CSSM_KEYATTR_MODIFIABLE);
750    keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
751    crtn = CSSM_WrapKey(ccHand,
752	    &creds,
753	    refKey,
754	    &descData,
755	    rawKey);
756    if(crtn != CSSM_OK) {
757	CSSM_PERROR("CSSM_WrapKey", crtn);
758    }
759    CSSM_DeleteContext(ccHand);
760    return crtn;
761}
762
763/*
764 * Free memory via specified plugin's app-level allocator
765 */
766static void cmsFreeCssmMemory(
767    CSSM_HANDLE	hand,
768    void	*p)
769{
770    CSSM_API_MEMORY_FUNCS memFuncs;
771    CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
772    if(crtn) {
773	    return;
774    }
775    memFuncs.free_func(p, memFuncs.AllocRef);
776}
777
778/*
779 * Given an OID tag, return key size and mode.
780 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
781 * be used here because the message does not contain a key size
782 * indication.
783 */
784static OSStatus encrAlgInfo(
785    SECOidTag		oidTag,
786    uint32		*keySizeBits,	/* RETURNED */
787    CSSM_ENCRYPT_MODE	*mode)		/* RETURNED */
788{
789    *keySizeBits = 64;		    /* default */
790    *mode = CSSM_ALGMODE_CBCPadIV8; /* default */
791
792    switch(oidTag) {
793	case SEC_OID_RC2_CBC:
794	case SEC_OID_RC4:
795	case SEC_OID_RC5_CBC_PAD:
796	    dprintf("encrAlgInfo: key size unknowable\n");
797	    return errSecDataNotAvailable;
798
799	case SEC_OID_DES_EDE3_CBC:
800	    *keySizeBits = 192;
801	    break;
802	case SEC_OID_DES_EDE:
803	    /* Not sure about this; SecCmsCipherContextStart() treats this
804	     * like SEC_OID_DES_EDE3_CBC... */
805	case SEC_OID_DES_ECB:
806	    *mode = CSSM_ALGMODE_ECB;
807	    break;
808	case SEC_OID_DES_CBC:
809	    *mode = CSSM_ALGMODE_CBC;
810	    break;
811	case SEC_OID_AES_128_CBC:
812	    *keySizeBits = 128;
813	    break;
814	case SEC_OID_AES_192_CBC:
815	    *keySizeBits = 192;
816	    break;
817	case SEC_OID_AES_256_CBC:
818	    *keySizeBits = 256;
819	    break;
820	case SEC_OID_AES_128_ECB:
821	    *keySizeBits = 128;
822	    *mode = CSSM_ALGMODE_ECB;
823	    break;
824	case SEC_OID_AES_192_ECB:
825	    *keySizeBits = 192;
826	    *mode = CSSM_ALGMODE_ECB;
827	    break;
828	case SEC_OID_AES_256_ECB:
829	    *keySizeBits = 256;
830	    *mode = CSSM_ALGMODE_ECB;
831	    break;
832	case SEC_OID_DES_OFB:
833	    *mode = CSSM_ALGMODE_OFB;
834	    break;
835	case SEC_OID_DES_CFB:
836	    *mode = CSSM_ALGMODE_CFB;
837	    break;
838	default:
839	    dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
840	    return errSecDataNotAvailable;
841    }
842    return noErr;
843}
844
845#pragma mark ---- ECDH CEK key wrap ----
846
847/*
848 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
849 */
850OSStatus
851SecCmsUtilEncryptSymKeyECDH(
852    PLArenaPool *poolp,
853    SecCertificateRef cert,	/* recipient's cert */
854    SecSymmetricKeyRef key,	/* bulk key */
855    /* remaining fields RETURNED */
856    CSSM_DATA_PTR encKey,	/* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
857    CSSM_DATA_PTR ukm,		/* random UKM --> KeyAgreeRecipientInfo.ukm */
858    SECAlgorithmID *keyEncAlg,	/* alg := dhSinglePass-stdDH-sha1kdf-scheme
859				 * params := another encoded AlgId, with the KEK alg and IV */
860    CSSM_DATA_PTR pubKey)	/* our pub key as ECPoint -->
861				 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
862{
863    OSStatus rv = noErr;
864    CSSM_KEY ourPrivKeyCssm;
865    CSSM_KEY ourPubKeyCssm;
866    SecKeyRef theirPubKeyRef = NULL;
867    CSSM_KEY_PTR theirPubKeyCssm = NULL;
868    const CSSM_KEY *cekCssmRef = NULL;
869    uint32 ecdhKeySizeBits;
870    CSSM_CSP_HANDLE rawCspHand = SecCspHandleForAlgorithm(CSSM_ALGID_ECDH);
871    CSSM_CC_HANDLE ccHand = 0;
872    CSSM_RETURN crtn;
873    CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
874    SECAlgorithmID kekAlgId;
875    uint8 iv[ECDH_KEK_IV_LEN_BYTES];
876    CSSM_DATA ivData = {ECDH_KEK_IV_LEN_BYTES, iv};
877    SECOidData *kekOid;
878    ECC_CMS_SharedInfo sharedInfo;
879    CSSM_DATA sharedInfoEnc = {0, NULL};
880    uint8 nullData[2] = {SEC_ASN1_NULL, 0};
881    uint8 keyLenAsBytes[4];
882    CSSM_KEY kekDerive;
883    CSSM_DATA certData;
884    CSSM_CL_HANDLE clHand;
885    CSSM_ACCESS_CREDENTIALS creds;
886    CSSM_DATA paramData = {0, NULL};
887    CSSM_KEY cekCssm;
888    CSSM_CSP_HANDLE refCspHand;
889    CSSM_SIZE bytesEncrypted;
890    CSSM_DATA remData = {0, NULL};
891    CSSM_DATA ctext = {0, NULL};
892    CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKey;
893
894    if(rawCspHand == 0) {
895	return internalComponentErr;
896    }
897
898    memset(&ourPrivKeyCssm, 0, sizeof(CSSM_KEY));
899    memset(&ourPubKeyCssm, 0, sizeof(CSSM_KEY));
900    memset(&cekCssm, 0, sizeof(CSSM_KEY));
901    memset(&kekDerive, 0, sizeof(kekDerive));
902
903    encKey->Data = NULL;
904    encKey->Length = 0;
905
906    /*
907     * Create our ECDH key pair matching the recipient's key.
908     * Get the public key in "read-only" OCTET_STRING format, which
909     * is the ECPoint we put in
910     * KeyAgreeRecipientInfo.originator.OriginatorPublicKey.
911     */
912    rv = SecCertificateGetData(cert, &certData);
913    if(rv) {
914	CSSM_PERROR("SecCertificateGetData", rv);
915	return rv;
916    }
917    rv = SecCertificateGetCLHandle(cert, &clHand);
918    if(rv) {
919	CSSM_PERROR("SecCertificateGetCLHandle", rv);
920	return rv;
921    }
922    rv = CSSM_CL_CertGetKeyInfo(clHand, &certData, &theirPubKeyCssm);
923     if(rv) {
924	CSSM_PERROR("CSSM_CL_CertGetKeyInfo", rv);
925	return rv;
926    }
927
928    /*
929     * Verify the EC curve of the recipient's public key. It's in the
930     * public key's AlgId.parameters as an OID. The key we were
931     * given is in CSSM_X509_SUBJECT_PUBLIC_KEY_INFO form.
932     */
933    memset(&subjPubKey, 0, sizeof(subjPubKey));
934    if(SEC_ASN1DecodeItem(poolp, &subjPubKey, kSecAsn1SubjectPublicKeyInfoTemplate,
935	    &theirPubKeyCssm->KeyData)) {
936	dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding SubjPubKey\n");
937	/* oh well, keep going */
938    }
939    else {
940	if(subjPubKey.algorithm.parameters.Data != NULL) {
941	    CSSM_DATA curveOid;
942	    if(SEC_ASN1DecodeItem(poolp, &curveOid, kSecAsn1ObjectIDTemplate,
943		    &subjPubKey.algorithm.parameters)) {
944		dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding curveOid\n");
945		/* oh well, keep going */
946	    }
947	    else {
948		/* We have the curve OID. Any other errors are fatal. */
949		SECOidTag oidTag = SECOID_FindOIDTag(&curveOid);
950		switch(oidTag) {
951		    case SEC_OID_SECP_256_R1:
952		    case SEC_OID_SECP_384_R1:
953		    case SEC_OID_SECP_521_R1:
954			break;
955		    default:
956			dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported curveOid\n");
957			rv = CSSMERR_CSP_INVALID_KEY;
958			goto loser;
959		}
960	    }
961	}
962    }
963
964    ecdhKeySizeBits = theirPubKeyCssm->KeyHeader.LogicalKeySizeInBits;
965    crtn = CSSM_CSP_CreateKeyGenContext(rawCspHand,
966	    CSSM_ALGID_ECDSA,
967	    ecdhKeySizeBits,
968	    NULL,			// Seed
969	    NULL,			// Salt
970	    NULL,			// StartDate
971	    NULL,			// EndDate
972	    NULL,			// Params
973	    &ccHand);
974    if(crtn) {
975	CSSM_PERROR("CSSM_CSP_CreateKeyGenContext", crtn);
976	rv = crtn;
977	goto loser;
978    }
979    crtn = cmsAddContextAttribute(ccHand,
980	CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
981	sizeof(uint32),
982	CAT_Uint32,
983	NULL,
984	CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
985    if(crtn) {
986	CSSM_PERROR("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
987	rv = crtn;
988	goto loser;
989    }
990
991    crtn = CSSM_GenerateKeyPair(ccHand,
992	    CSSM_KEYUSE_DERIVE,
993	    CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
994	    &keyLabel,
995	    &ourPubKeyCssm,
996	    CSSM_KEYUSE_DERIVE,
997	    CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
998	    &keyLabel,
999	    NULL,			// CredAndAclEntry
1000	    &ourPrivKeyCssm);
1001    CSSM_DeleteContext(ccHand);
1002    ccHand = 0;
1003    if(crtn) {
1004	CSSM_PERROR("CSSM_GenerateKeyPair", crtn);
1005	rv = crtn;
1006	goto loser;
1007    }
1008    pubKey->Length = ourPubKeyCssm.KeyData.Length;
1009    pubKey->Data = (uint8 *)PORT_ArenaAlloc(poolp, pubKey->Length);
1010    memmove(pubKey->Data, ourPubKeyCssm.KeyData.Data, pubKey->Length);
1011    dumpBuf("sender's public key", pubKey);
1012
1013    /*
1014     * Cook up random UKM
1015     */
1016    ukm->Data = (uint8 *)PORT_ArenaAlloc(poolp, UKM_LENGTH);
1017    ukm->Length = UKM_LENGTH;
1018    crtn = cmsGenRand(rawCspHand, UKM_LENGTH, ukm->Data);
1019    if(crtn) {
1020	goto loser;
1021    }
1022    dumpBuf("sender UKM", ukm);
1023
1024    /*
1025     * OK, we have to set up a weird SECAlgorithmID.
1026     * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
1027     * params = an encoded SECAlgorithmID representing the KEK algorithm, with
1028     *   algorithm = whatever we pick
1029     *   parameters = IV as octet string (though I haven't seen that specified
1030     *		      anywhere; it's how the CEK IV is encoded)
1031     *
1032     * First, the 8-byte random IV, encoded as octet string
1033     */
1034    crtn = cmsGenRand(rawCspHand, ECDH_KEK_IV_LEN_BYTES, iv);
1035    if(crtn) {
1036	goto loser;
1037    }
1038    dumpBuf("sender IV", &ivData);
1039
1040    memset(&kekAlgId, 0, sizeof(kekAlgId));
1041    if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
1042	    &ivData, kSecAsn1OctetStringTemplate)) {
1043	rv = internalComponentErr;
1044	goto loser;
1045    }
1046
1047    /* Drop in the KEK OID and encode the whole thing */
1048    kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
1049    if(kekOid == NULL) {
1050	dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
1051	rv = internalComponentErr;
1052	goto loser;
1053    }
1054    kekAlgId.algorithm = kekOid->oid;
1055    memset(keyEncAlg, 0, sizeof(*keyEncAlg));
1056    if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
1057	    &kekAlgId, SECOID_AlgorithmIDTemplate)) {
1058	rv = internalComponentErr;
1059	goto loser;
1060    }
1061    kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
1062    if(kekOid == NULL) {
1063	dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
1064	rv = internalComponentErr;
1065	goto loser;
1066    }
1067    keyEncAlg->algorithm = kekOid->oid;
1068
1069    /*
1070     * Now in order to derive the KEK proper, we have to create a
1071     * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1072     * encode that struct, the result of which is used as the
1073     * SharedInfo value in the KEK key derive.
1074     */
1075    memset(&sharedInfo, 0, sizeof(sharedInfo));
1076    kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
1077    sharedInfo.algId.algorithm = kekOid->oid;
1078    sharedInfo.algId.parameters.Data = nullData;
1079    sharedInfo.algId.parameters.Length = 2;
1080    sharedInfo.entityUInfo = *ukm;
1081    int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes);
1082    sharedInfo.suppPubInfo.Length = 4;
1083    sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1084    if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc,
1085	    &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1086	rv = internalComponentErr;
1087	goto loser;
1088    }
1089    dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
1090
1091    /*
1092     * Since we're using the raw CSP here, we can provide the "other" public
1093     * key as an actual CSSM_KEY. When unwrapping, we won't be able to do that
1094     * since we'll be using our private key obtained from a SecIdentityRef.
1095     */
1096    memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
1097    crtn = CSSM_CSP_CreateDeriveKeyContext(rawCspHand,
1098		CSSM_ALGID_ECDH_X963_KDF,
1099		ECDH_KEK_KEY_CSSM_ALGID,	// algorithm of the KEK
1100		ECDH_KEK_KEY_LEN_BYTES * 8,
1101		&creds,
1102		&ourPrivKeyCssm,	// BaseKey
1103		0,			// IterationCount
1104		&sharedInfoEnc,		// Salt
1105		0,			// Seed
1106		&ccHand);
1107    if(crtn) {
1108	CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
1109	rv = crtn;
1110	goto loser;
1111    }
1112
1113    /* add recipient's pub key as a context attr */
1114    crtn = cmsAddContextAttribute(ccHand,
1115	    CSSM_ATTRIBUTE_PUBLIC_KEY,
1116	    sizeof(CSSM_KEY),
1117	    CAT_Ptr,
1118	    (void *)theirPubKeyCssm,
1119	    0);
1120    if(crtn) {
1121	rv = crtn;
1122	goto loser;
1123    }
1124
1125    /* Derive the KEK */
1126    crtn = CSSM_DeriveKey(ccHand,
1127	    &paramData,
1128	    CSSM_KEYUSE_ANY,
1129	    CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
1130	    &keyLabel,
1131	    NULL,				// cread/acl
1132	    &kekDerive);
1133    if(crtn) {
1134	CSSM_PERROR("CSSM_DeriveKey", crtn);
1135	rv = crtn;
1136	goto loser;
1137    }
1138    CSSM_DeleteContext(ccHand);
1139    ccHand = 0;
1140
1141    /*
1142     * Obtain the raw CEK bits.
1143     */
1144    rv = SecKeyGetCSSMKey(key, &cekCssmRef);
1145    if(rv) {
1146	CSSM_PERROR("SecKeyGetCSSMKey", rv);
1147	goto loser;
1148    }
1149    rv = SecKeyGetCSPHandle(key, &refCspHand);
1150    if(rv) {
1151	CSSM_PERROR("SecKeyGetCSPHandle", rv);
1152	goto loser;
1153    }
1154    rv = cmsNullWrapKey(refCspHand, cekCssmRef, &cekCssm);
1155    if(rv) {
1156	goto loser;
1157    }
1158
1159    /*
1160     * Finally, encrypt the raw CEK bits with the KEK we just derived
1161     */
1162    crtn = CSSM_CSP_CreateSymmetricContext(rawCspHand,
1163	    ECDH_KEK_ENCR_CSSM_ALGID,
1164	    CSSM_ALGMODE_CBCPadIV8,
1165	    NULL,			// access cred
1166	    &kekDerive,
1167	    &ivData,			// InitVector
1168	    CSSM_PADDING_PKCS7,
1169	    NULL,			// Params
1170	    &ccHand);
1171    if(rv) {
1172	CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
1173	goto loser;
1174    }
1175    rv = CSSM_EncryptData(ccHand,
1176	    &cekCssm.KeyData,
1177	    1,
1178	    &ctext,
1179	    1,
1180	    &bytesEncrypted,
1181	    &remData);
1182    if(rv) {
1183	CSSM_PERROR("CSSM_EncryptData", rv);
1184	goto loser;
1185    }
1186    encKey->Data = PORT_ArenaAlloc(poolp, bytesEncrypted);
1187    encKey->Length = bytesEncrypted;
1188    memmove(encKey->Data, ctext.Data, ctext.Length);
1189    if(bytesEncrypted != ctext.Length) {
1190	memmove(encKey->Data + ctext.Length, remData.Data, remData.Length);
1191    }
1192    dumpBuf("sender encKey", encKey);
1193
1194loser:
1195    if(ccHand) {
1196	CSSM_DeleteContext(ccHand);
1197    }
1198    CFRELEASE(theirPubKeyRef);
1199    if(ourPubKeyCssm.KeyData.Data) {
1200	CSSM_FreeKey(rawCspHand, NULL, &ourPubKeyCssm, CSSM_FALSE);
1201    }
1202    if(ourPrivKeyCssm.KeyData.Data) {
1203	CSSM_FreeKey(rawCspHand, NULL, &ourPrivKeyCssm, CSSM_FALSE);
1204    }
1205    if(ctext.Data) {
1206	cmsFreeCssmMemory(rawCspHand, ctext.Data);
1207    }
1208    if(remData.Data) {
1209	cmsFreeCssmMemory(rawCspHand, remData.Data);
1210    }
1211    if(cekCssm.KeyData.Data) {
1212	CSSM_FreeKey(refCspHand, NULL, &cekCssm, CSSM_FALSE);
1213    }
1214    if(kekDerive.KeyData.Data) {
1215	CSSM_FreeKey(rawCspHand, NULL, &kekDerive, CSSM_FALSE);
1216    }
1217    if(theirPubKeyCssm) {
1218	/* Allocated by CL */
1219	cmsFreeCssmMemory(clHand, theirPubKeyCssm->KeyData.Data);
1220	cmsFreeCssmMemory(clHand, theirPubKeyCssm);
1221    }
1222    return rv;
1223}
1224
1225#pragma mark ---- ECDH CEK key unwrap ----
1226
1227SecSymmetricKeyRef
1228SecCmsUtilDecryptSymKeyECDH(
1229    SecPrivateKeyRef privkey,	/* our private key */
1230    CSSM_DATA_PTR encKey,	/* encrypted CEK */
1231    CSSM_DATA_PTR ukm,		/* random UKM from KeyAgreeRecipientInfo.ukm */
1232    SECAlgorithmID *keyEncAlg,	/* alg := dhSinglePass-stdDH-sha1kdf-scheme
1233				 * params := another encoded AlgId, with the KEK alg and IV */
1234    SECOidTag bulkalgtag,	/* algorithm of returned key */
1235    CSSM_DATA_PTR pubKey)	/* sender's pub key as ECPoint from
1236				 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
1237
1238{
1239    SecSymmetricKeyRef outKey = NULL;
1240    OSStatus rv = noErr;
1241    const CSSM_KEY *ourPrivKeyCssm;
1242    PLArenaPool *pool = NULL;
1243    SECAlgorithmID keyAlgParam;
1244    SECOidData *kekOid = NULL;
1245    CSSM_DATA iv = {0, NULL};
1246    ECC_CMS_SharedInfo sharedInfo;
1247    CSSM_DATA sharedInfoEnc = {0, NULL};
1248    uint8 nullData[2] = {SEC_ASN1_NULL, 0};
1249    uint8 keyLenAsBytes[4];
1250    CSSM_ENCRYPT_MODE kekMode;
1251    uint32 kekSizeBits;
1252    CSSM_KEY kekDerive;
1253    CSSM_RETURN crtn;
1254    CSSM_ACCESS_CREDENTIALS creds;
1255    CSSM_CSP_HANDLE refCspHand;
1256    CSSM_CC_HANDLE ccHand = 0;
1257    CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
1258    const CSSM_ACCESS_CREDENTIALS *accessCred;
1259    CSSM_KEY wrappedKey;
1260    CSSM_KEY unwrappedKey;
1261    CSSM_ALGORITHMS bulkAlg;
1262    CSSM_DATA descriptiveData = {0, NULL};
1263
1264    dumpBuf("receiver encKey", encKey);
1265
1266    memset(&kekDerive, 0, sizeof(kekDerive));
1267
1268    /* our private key in CSSM form */
1269    rv = SecKeyGetCSSMKey(privkey, &ourPrivKeyCssm);
1270    if(rv) {
1271	CSSM_PERROR("SecKeyGetCSSMKey", rv);
1272	goto loser;
1273    }
1274
1275    /*
1276     * Decode keyEncAlg.params to get KEK algorithm and IV
1277     */
1278    pool = PORT_NewArena(1024);
1279    if(pool == NULL) {
1280	goto loser;
1281    }
1282    memset(&keyAlgParam, 0, sizeof(keyAlgParam));
1283    if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
1284	    &keyEncAlg->parameters)) {
1285	dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1286	goto loser;
1287    }
1288    kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
1289    if(kekOid == NULL) {
1290	dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1291	goto loser;
1292    }
1293    rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &kekMode);
1294    if(rv) {
1295	goto loser;
1296    }
1297    /* IV is OCTET STRING in the alg params */
1298    if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
1299	    &keyAlgParam.parameters)) {
1300	/*
1301	 * Not sure here - is it legal to have no IV? I haven't seen this
1302	 * addressed in any spec. Maybe we should condition the behavior
1303	 * here on the KEK algorithm.
1304	 */
1305	dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1306	goto loser;
1307    }
1308
1309    /*
1310     * Now in order to derive the KEK proper, we have to create a
1311     * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1312     * encode that struct, the result of which is used as the
1313     * SharedInfo value in the KEK key derive.
1314     */
1315    memset(&sharedInfo, 0, sizeof(sharedInfo));
1316    sharedInfo.algId.algorithm = kekOid->oid;
1317    sharedInfo.algId.parameters.Data = nullData;
1318    sharedInfo.algId.parameters.Length = 2;
1319    sharedInfo.entityUInfo = *ukm;
1320    int32ToBytes(kekSizeBits, keyLenAsBytes);
1321    sharedInfo.suppPubInfo.Length = 4;
1322    sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1323    if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc,
1324	    &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1325	rv = internalComponentErr;
1326	goto loser;
1327    }
1328    dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
1329    dumpBuf("receiver IV", &iv);
1330    dumpBuf("receiver UKM", ukm);
1331    dumpBuf("sender's public key", pubKey);
1332
1333    /*
1334     * Using the Sec-layer CSPDL, "other's" public key specified as ECPOint param. Which
1335     * is fortunate because that's what we have...
1336     */
1337    memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
1338    rv = SecKeyGetCSPHandle(privkey, &refCspHand);
1339    if(rv) {
1340	CSSM_PERROR("SecKeyGetCSPHandle", rv);
1341	goto loser;
1342    }
1343    rv = SecKeyGetCredentials(privkey,
1344	    CSSM_ACL_AUTHORIZATION_DERIVE,
1345	    kSecCredentialTypeDefault,
1346	    &accessCred);
1347    if (rv) {
1348	CSSM_PERROR("SecKeyGetCredentials", rv);
1349	goto loser;
1350    }
1351    crtn = CSSM_CSP_CreateDeriveKeyContext(refCspHand,
1352		CSSM_ALGID_ECDH_X963_KDF,
1353		kekOid->cssmAlgorithm,	// algorithm of the KEK
1354		kekSizeBits,
1355		&creds,
1356		ourPrivKeyCssm,		// BaseKey
1357		0,			// IterationCount
1358		&sharedInfoEnc,		// Salt
1359		0,			// Seed
1360		&ccHand);
1361    if(crtn) {
1362	CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
1363	goto loser;
1364    }
1365    crtn = CSSM_DeriveKey(ccHand,
1366	    pubKey,			// param
1367	    CSSM_KEYUSE_ANY,
1368	    CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
1369	    &keyLabel,
1370	    NULL,			// cred/acl
1371	    &kekDerive);
1372    CSSM_DeleteContext(ccHand);
1373    ccHand = 0;
1374    if(crtn) {
1375	CSSM_PERROR("CSSM_DeriveKey", crtn);
1376	goto loser;
1377    }
1378
1379    /*
1380     * Decrypt the encrypted key bits with the KEK key.
1381     */
1382    crtn = CSSM_CSP_CreateSymmetricContext(refCspHand,
1383	    kekOid->cssmAlgorithm,
1384	    kekMode,
1385	    NULL,			// access cred
1386	    &kekDerive,
1387	    &iv,			// InitVector
1388	    /* FIXME is this variable too? */
1389	    CSSM_PADDING_PKCS7,
1390	    NULL,			// Params
1391	    &ccHand);
1392    if(rv) {
1393	CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
1394	goto loser;
1395    }
1396
1397    memset(&wrappedKey, 0, sizeof(CSSM_KEY));
1398    memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
1399
1400    bulkAlg = SECOID_FindyCssmAlgorithmByTag(bulkalgtag);
1401    if(bulkAlg == CSSM_ALGID_NONE) {
1402	dprintf("SecCmsUtilDecryptSymKeyECDH: unknown bulk alg\n");
1403	goto loser;
1404    }
1405
1406    wrappedKey.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
1407    wrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
1408    wrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
1409    wrappedKey.KeyHeader.AlgorithmId = bulkAlg;
1410    wrappedKey.KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
1411    wrappedKey.KeyHeader.WrapAlgorithmId = kekOid->cssmAlgorithm;
1412    wrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE;
1413    wrappedKey.KeyData = *encKey;
1414
1415    crtn = CSSM_UnwrapKey(ccHand,
1416	    NULL, /* publicKey */
1417	    &wrappedKey,
1418	    CSSM_KEYUSE_DECRYPT,
1419	    CSSM_KEYATTR_EXTRACTABLE,
1420	    &keyLabel,
1421	    NULL, /* rcc */
1422	    &unwrappedKey,
1423	    &descriptiveData);
1424    CSSM_DeleteContext(ccHand);
1425    ccHand = 0;
1426    if(crtn) {
1427	CSSM_PERROR("CSSM_UnwrapKey", crtn);
1428	goto loser;
1429    }
1430    rv = SecKeyCreateWithCSSMKey(&unwrappedKey, &outKey);
1431    if (rv) {
1432	CSSM_PERROR("SecKeyCreateWithCSSMKey", rv);
1433    }
1434
1435loser:
1436    if(pool != NULL) {
1437	PORT_FreeArena(pool, PR_FALSE);
1438    }
1439    if(kekDerive.KeyData.Data) {
1440	CSSM_FreeKey(refCspHand, NULL, &kekDerive, CSSM_FALSE);
1441    }
1442    if(outKey == NULL) {
1443	PORT_SetError(SEC_ERROR_NO_KEY);
1444    }
1445    return outKey;
1446}
1447
1448
1449
1450