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/
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.
12 * The Original Code is the Netscape security libraries.
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
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
35 * CMS public key crypto
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/SecItemPriv.h>
49 #include <Security/Security.h>
50 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
51 #include <Security/SecCmsBase.h>
52 #include <Security/secasn1t.h>
53 #include <security_asn1/plarenas.h>
54 #include <Security/keyTemplates.h>
55 #include <CommonCrypto/CommonCryptor.h>
56 #include <CommonCrypto/CommonRandomSPI.h>
57 #include <CommonCrypto/CommonRandom.h>
59 /* ====== RSA ======================================================================= */
62 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
64 * this function takes a symmetric key and encrypts it using an RSA public key
65 * according to PKCS#1 and RFC2633 (S/MIME)
68 SecCmsUtilEncryptSymKeyRSA(PLArenaPool
*poolp
, SecCertificateRef cert
,
69 SecSymmetricKeyRef bulkkey
,
73 SecPublicKeyRef publickey
= SecCertificateCopyKey(cert
);
74 if (publickey
== NULL
)
77 rv
= SecCmsUtilEncryptSymKeyRSAPubKey(poolp
, publickey
, bulkkey
, encKey
);
83 SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool
*poolp
,
84 SecPublicKeyRef publickey
,
85 SecSymmetricKeyRef bulkkey
, CSSM_DATA_PTR encKey
)
88 unsigned int data_len
;
91 CFDictionaryRef theirKeyAttrs
= NULL
;
93 mark
= PORT_ArenaMark(poolp
);
96 /* allocate memory for the encrypted key */
97 theirKeyAttrs
= SecKeyCopyAttributes(publickey
);
102 CFNumberRef keySizeNum
= CFDictionaryGetValue(theirKeyAttrs
, kSecAttrKeySizeInBits
);
103 if (!CFNumberGetValue(keySizeNum
, kCFNumberIntType
, &data_len
)) {
106 // Convert length to bytes;
109 encKey
->Data
= (unsigned char*)PORT_ArenaAlloc(poolp
, data_len
);
110 encKey
->Length
= data_len
;
111 if (encKey
->Data
== NULL
)
114 /* encrypt the key now */
115 rv
= WRAP_PubWrapSymKey(publickey
, bulkkey
, encKey
);
116 if (rv
!= SECSuccess
)
119 PORT_ArenaUnmark(poolp
, mark
);
124 CFRelease(theirKeyAttrs
);
127 PORT_ArenaRelease(poolp
, mark
);
133 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
135 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
136 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
137 * a hardware token...
140 SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey
, CSSM_DATA_PTR encKey
, SECOidTag bulkalgtag
)
143 return WRAP_PubUnwrapSymKey(privkey
, encKey
, bulkalgtag
);
147 // @@@ Implement Fortezza and Diffie hellman support
149 /* ====== MISSI (Fortezza) ========================================================== */
151 extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams
[];
154 SecCmsUtilEncryptSymKeyMISSI(PLArenaPool
*poolp
, SecCertificateRef cert
, SecSymmetricKeyRef bulkkey
,
155 SECOidTag symalgtag
, CSSM_DATA_PTR encKey
, CSSM_DATA_PTR
*pparams
, void *pwfn_arg
)
157 SECOidTag certalgtag
; /* the certificate's encryption algorithm */
158 SECOidTag encalgtag
; /* the algorithm used for key exchange/agreement */
159 OSStatus rv
= SECFailure
;
160 CSSM_DATA_PTR params
= NULL
;
162 SecSymmetricKeyRef tek
;
163 SecCertificateRef ourCert
;
164 SecPublicKeyRef ourPubKey
, *publickey
= NULL
;
165 SecPrivateKeyRef ourPrivKey
= NULL
;
166 SecCmsKEATemplateSelector whichKEA
= SecCmsKEAInvalid
;
167 SecCmsSMIMEKEAParameters keaParams
;
168 PLArenaPool
*arena
= NULL
;
169 extern const SecAsn1Template
*nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate
);
170 const SECAlgorithmID
*algid
;
172 /* Clear keaParams, since cleanup code checks the lengths */
173 (void) memset(&keaParams
, 0, sizeof(keaParams
));
175 SecCertificateGetAlgorithmID(cert
,&algid
);
176 certalgtag
= SECOID_GetAlgorithmTag(algid
);
177 PORT_Assert(certalgtag
== SEC_OID_MISSI_KEA_DSS_OLD
||
178 certalgtag
== SEC_OID_MISSI_KEA_DSS
||
179 certalgtag
== SEC_OID_MISSI_KEA
);
181 #define SMIME_FORTEZZA_RA_LENGTH 128
182 #define SMIME_FORTEZZA_IV_LENGTH 24
183 #define SMIME_FORTEZZA_MAX_KEY_SIZE 256
185 /* We really want to show our KEA tag as the key exchange algorithm tag. */
186 encalgtag
= SEC_OID_NETSCAPE_SMIME_KEA
;
188 /* Get the public key of the recipient. */
189 publickey
= CERT_ExtractPublicKey(cert
);
190 if (publickey
== NULL
) goto loser
;
192 /* Find our own cert, and extract its keys. */
193 ourCert
= PK11_FindBestKEAMatch(cert
, pwfn_arg
);
194 if (ourCert
== NULL
) goto loser
;
196 arena
= PORT_NewArena(1024);
200 ourPubKey
= CERT_ExtractPublicKey(ourCert
);
201 if (ourPubKey
== NULL
) {
202 CERT_DestroyCertificate(ourCert
);
206 /* While we're here, copy the public key into the outgoing
208 SECITEM_CopyItem(arena
, &(keaParams
.originatorKEAKey
), &(ourPubKey
->u
.fortezza
.KEAKey
));
209 SECKEY_DestroyPublicKey(ourPubKey
);
212 /* Extract our private key in order to derive the KEA key. */
213 ourPrivKey
= PK11_FindKeyByAnyCert(ourCert
, pwfn_arg
);
214 CERT_DestroyCertificate(ourCert
); /* we're done with this */
218 /* Prepare raItem with 128 bytes (filled with zeros). */
219 keaParams
.originatorRA
.Data
= (unsigned char *)PORT_ArenaAlloc(arena
,SMIME_FORTEZZA_RA_LENGTH
);
220 keaParams
.originatorRA
.Length
= SMIME_FORTEZZA_RA_LENGTH
;
222 /* Generate the TEK (token exchange key) which we use
223 * to wrap the bulk encryption key. (keaparams.originatorRA) will be
224 * filled with a random seed which we need to send to
225 * the recipient. (user keying material in RFC2630/DSA speak) */
226 tek
= PK11_PubDerive(ourPrivKey
, publickey
, PR_TRUE
,
227 &keaParams
.originatorRA
, NULL
,
228 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
229 CKA_WRAP
, 0, pwfn_arg
);
231 SECKEY_DestroyPublicKey(publickey
);
232 SECKEY_DestroyPrivateKey(ourPrivKey
);
239 /* allocate space for the wrapped key data */
240 encKey
->Data
= (unsigned char *)PORT_ArenaAlloc(poolp
, SMIME_FORTEZZA_MAX_KEY_SIZE
);
241 encKey
->Length
= SMIME_FORTEZZA_MAX_KEY_SIZE
;
243 if (encKey
->Data
== NULL
) {
248 /* Wrap the bulk key. What we do with the resulting data
249 depends on whether we're using Skipjack to wrap the key. */
250 switch (PK11_AlgtagToMechanism(symalgtag
)) {
251 case CKM_SKIPJACK_CBC64
:
252 case CKM_SKIPJACK_ECB64
:
253 case CKM_SKIPJACK_OFB64
:
254 case CKM_SKIPJACK_CFB64
:
255 case CKM_SKIPJACK_CFB32
:
256 case CKM_SKIPJACK_CFB16
:
257 case CKM_SKIPJACK_CFB8
:
258 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */
259 err
= PK11_WrapSymKey(CKM_SKIPJACK_WRAP
, NULL
, tek
, bulkkey
, encKey
);
260 whichKEA
= SecCmsKEAUsesSkipjack
;
263 /* Not SKIPJACK, we encrypt the raw key data */
264 keaParams
.nonSkipjackIV
.Data
=
265 (unsigned char *)PORT_ArenaAlloc(arena
, SMIME_FORTEZZA_IV_LENGTH
);
266 keaParams
.nonSkipjackIV
.Length
= SMIME_FORTEZZA_IV_LENGTH
;
267 err
= PK11_WrapSymKey(CKM_SKIPJACK_CBC64
, &keaParams
.nonSkipjackIV
, tek
, bulkkey
, encKey
);
268 if (err
!= SECSuccess
)
271 if (encKey
->Length
!= PK11_GetKeyLength(bulkkey
)) {
272 /* The size of the encrypted key is not the same as
273 that of the original bulk key, presumably due to
274 padding. Encode and store the real size of the
276 if (SEC_ASN1EncodeInteger(arena
, &keaParams
.bulkKeySize
, PK11_GetKeyLength(bulkkey
)) == NULL
)
277 err
= (OSStatus
)PORT_GetError();
279 /* use full template for encoding */
280 whichKEA
= SecCmsKEAUsesNonSkipjackWithPaddedEncKey
;
283 /* enc key length == bulk key length */
284 whichKEA
= SecCmsKEAUsesNonSkipjack
;
290 if (err
!= SECSuccess
)
293 PORT_Assert(whichKEA
!= SecCmsKEAInvalid
);
295 /* Encode the KEA parameters into the recipient info. */
296 params
= SEC_ASN1EncodeItem(poolp
, NULL
, &keaParams
, nss_cms_get_kea_template(whichKEA
));
300 /* pass back the algorithm params */
307 PORT_FreeArena(arena
, PR_FALSE
);
309 SECKEY_DestroyPublicKey(publickey
);
311 SECKEY_DestroyPrivateKey(ourPrivKey
);
316 SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey
, CSSM_DATA_PTR encKey
, SECAlgorithmID
*keyEncAlg
, SECOidTag bulkalgtag
, void *pwfn_arg
)
318 /* fortezza: do a key exchange */
320 CK_MECHANISM_TYPE bulkType
;
321 SecSymmetricKeyRef tek
;
322 SecPublicKeyRef originatorPubKey
;
323 SecCmsSMIMEKEAParameters keaParams
;
324 SecSymmetricKeyRef bulkkey
;
327 (void) memset(&keaParams
, 0, sizeof(keaParams
));
329 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
330 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
332 /* Decode the KEA algorithm parameters. */
333 err
= SEC_ASN1DecodeItem(NULL
, &keaParams
, NSS_SMIMEKEAParamTemplateAllParams
,
334 &(keyEncAlg
->parameters
));
335 if (err
!= SECSuccess
)
338 /* get originator's public key */
339 originatorPubKey
= PK11_MakeKEAPubKey(keaParams
.originatorKEAKey
.Data
,
340 keaParams
.originatorKEAKey
.Length
);
341 if (originatorPubKey
== NULL
)
344 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
345 The Derive function generates a shared secret and combines it with the originatorRA
346 data to come up with an unique session key */
347 tek
= PK11_PubDerive(privkey
, originatorPubKey
, PR_FALSE
,
348 &keaParams
.originatorRA
, NULL
,
349 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
350 CKA_WRAP
, 0, pwfn_arg
);
351 SECKEY_DestroyPublicKey(originatorPubKey
); /* not needed anymore */
355 /* Now that we have the TEK, unwrap the bulk key
356 with which to decrypt the message. We have to
357 do one of two different things depending on
358 whether Skipjack was used for *bulk* encryption
360 bulkType
= PK11_AlgtagToMechanism(bulkalgtag
);
362 case CKM_SKIPJACK_CBC64
:
363 case CKM_SKIPJACK_ECB64
:
364 case CKM_SKIPJACK_OFB64
:
365 case CKM_SKIPJACK_CFB64
:
366 case CKM_SKIPJACK_CFB32
:
367 case CKM_SKIPJACK_CFB16
:
368 case CKM_SKIPJACK_CFB8
:
369 /* Skipjack is being used as the bulk encryption algorithm.*/
370 /* Unwrap the bulk key. */
371 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_WRAP
, NULL
,
372 encKey
, CKM_SKIPJACK_CBC64
, CKA_DECRYPT
, 0);
375 /* Skipjack was not used for bulk encryption of this
376 message. Use Skipjack CBC64, with the nonSkipjackIV
377 part of the KEA key parameters, to decrypt
378 the bulk key. If the optional parameter bulkKeySize is present,
379 bulk key size is different than the encrypted key size */
380 if (keaParams
.bulkKeySize
.Length
> 0) {
381 err
= SEC_ASN1DecodeItem(NULL
, &bulkLength
,
382 SEC_ASN1_GET(SEC_IntegerTemplate
),
383 &keaParams
.bulkKeySize
);
384 if (err
!= SECSuccess
)
388 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_CBC64
, &keaParams
.nonSkipjackIV
,
389 encKey
, bulkType
, CKA_DECRYPT
, bulkLength
);
397 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
400 SecCmsUtilEncryptSymKeyESDH(PLArenaPool
*poolp
, SecCertificateRef cert
, SecSymmetricKeyRef key
,
401 CSSM_DATA_PTR encKey
, CSSM_DATA_PTR ukm
, SECAlgorithmID
*keyEncAlg
,
402 CSSM_DATA_PTR pubKey
)
404 #if 0 /* not yet done */
405 SECOidTag certalgtag
; /* the certificate's encryption algorithm */
406 SECOidTag encalgtag
; /* the algorithm used for key exchange/agreement */
408 CSSM_DATA_PTR params
= NULL
;
411 SecSymmetricKeyRef tek
;
412 SecCertificateRef ourCert
;
413 SecPublicKeyRef ourPubKey
;
414 SecCmsKEATemplateSelector whichKEA
= SecCmsKEAInvalid
;
416 certalgtag
= SECOID_GetAlgorithmTag(&(cert
->subjectPublicKeyInfo
.algorithm
));
417 PORT_Assert(certalgtag
== SEC_OID_X942_DIFFIE_HELMAN_KEY
);
419 /* We really want to show our KEA tag as the key exchange algorithm tag. */
420 encalgtag
= SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN
;
422 /* Get the public key of the recipient. */
423 publickey
= CERT_ExtractPublicKey(cert
);
424 if (publickey
== NULL
) goto loser
;
426 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
427 /* XXXX */ourCert
= PK11_FindBestKEAMatch(cert
, wincx
);
428 if (ourCert
== NULL
) goto loser
;
430 arena
= PORT_NewArena(1024);
431 if (arena
== NULL
) goto loser
;
433 /* While we're here, extract the key pair's public key data and copy it into */
434 /* the outgoing parameters. */
435 /* XXXX */ourPubKey
= CERT_ExtractPublicKey(ourCert
);
436 if (ourPubKey
== NULL
)
440 SECITEM_CopyItem(arena
, pubKey
, /* XXX */&(ourPubKey
->u
.fortezza
.KEAKey
));
441 SECKEY_DestroyPublicKey(ourPubKey
); /* we only need the private key from now on */
444 /* Extract our private key in order to derive the KEA key. */
445 ourPrivKey
= PK11_FindKeyByAnyCert(ourCert
,wincx
);
446 CERT_DestroyCertificate(ourCert
); /* we're done with this */
447 if (!ourPrivKey
) goto loser
;
449 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
451 ukm
->Data
= (unsigned char*)PORT_ArenaZAlloc(arena
,/* XXXX */);
452 ukm
->Length
= /* XXXX */;
455 /* Generate the KEK (key exchange key) according to RFC2631 which we use
456 * to wrap the bulk encryption key. */
457 kek
= PK11_PubDerive(ourPrivKey
, publickey
, PR_TRUE
,
459 /* XXXX */CKM_KEA_KEY_DERIVE
, /* XXXX */CKM_SKIPJACK_WRAP
,
462 SECKEY_DestroyPublicKey(publickey
);
463 SECKEY_DestroyPrivateKey(ourPrivKey
);
470 /* allocate space for the encrypted CEK (bulk key) */
471 encKey
->Data
= (unsigned char*)PORT_ArenaAlloc(poolp
, SMIME_FORTEZZA_MAX_KEY_SIZE
);
472 encKey
->Length
= SMIME_FORTEZZA_MAX_KEY_SIZE
;
474 if (encKey
->Data
== NULL
)
481 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
482 /* bulk encryption algorithm */
483 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo
->encalg
))
485 case /* XXXX */CKM_SKIPJACK_CFB8
:
486 err
= PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP
, NULL
, kek
, bulkkey
, encKey
);
487 whichKEA
= SecCmsKEAUsesSkipjack
;
489 case /* XXXX */CKM_SKIPJACK_CFB8
:
490 err
= PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP
, NULL
, kek
, bulkkey
, encKey
);
491 whichKEA
= SecCmsKEAUsesSkipjack
;
494 /* XXXX what do we do here? Neither RC2 nor 3DES... */
500 CFRelease(kek
); /* we do not need the KEK anymore */
501 if (err
!= SECSuccess
)
504 PORT_Assert(whichKEA
!= SecCmsKEAInvalid
);
506 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
507 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
508 params
= SEC_ASN1EncodeItem(arena
, NULL
, &keaParams
, sec_pkcs7_get_kea_template(whichKEA
));
512 /* now set keyEncAlg */
513 rv
= SECOID_SetAlgorithmID(poolp
, keyEncAlg
, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN
, params
);
514 if (rv
!= SECSuccess
)
517 /* XXXXXXX this is not right yet */
520 PORT_FreeArena(arena
, PR_FALSE
);
523 SECKEY_DestroyPublicKey(publickey
);
526 SECKEY_DestroyPrivateKey(ourPrivKey
);
533 SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey
, CSSM_DATA_PTR encKey
, SECAlgorithmID
*keyEncAlg
, SECOidTag bulkalgtag
, void *pwfn_arg
)
535 #if 0 /* not yet done */
537 CK_MECHANISM_TYPE bulkType
;
538 SecSymmetricKeyRef tek
;
539 SecPublicKeyRef originatorPubKey
;
540 SecCmsSMIMEKEAParameters keaParams
;
542 /* XXXX get originator's public key */
543 originatorPubKey
= PK11_MakeKEAPubKey(keaParams
.originatorKEAKey
.Data
,
544 keaParams
.originatorKEAKey
.Length
);
545 if (originatorPubKey
== NULL
)
548 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
549 The Derive function generates a shared secret and combines it with the originatorRA
550 data to come up with an unique session key */
551 tek
= PK11_PubDerive(privkey
, originatorPubKey
, PR_FALSE
,
552 &keaParams
.originatorRA
, NULL
,
553 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
554 CKA_WRAP
, 0, pwfn_arg
);
555 SECKEY_DestroyPublicKey(originatorPubKey
); /* not needed anymore */
559 /* Now that we have the TEK, unwrap the bulk key
560 with which to decrypt the message. */
561 /* Skipjack is being used as the bulk encryption algorithm.*/
562 /* Unwrap the bulk key. */
563 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_WRAP
, NULL
,
564 encKey
, CKM_SKIPJACK_CBC64
, CKA_DECRYPT
, 0);
573 #endif /* Fortezza, DIffie-Hellman */
575 #define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
577 /* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
579 #pragma mark ---- ECDH support functions ----
582 #define CSSM_PERROR(f, r)
583 #define dprintf(args...)
585 #define CSSM_PERROR(f, r) cssmPerror(f, r)
586 #define dprintf(args...) fprintf(stderr, args)
589 /* Length of KeyAgreeRecipientInfo.ukm we create */
592 /* KEK algorithm info we generate */
593 #define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
594 #define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
595 #define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
596 #define ECDH_KEK_KEY_LEN_BYTES 24
597 #define ECDH_KEK_IV_LEN_BYTES 8
599 #define CMS_DUMP_BUFS 0
608 printf("%s:\n ", label
);
609 for(dex
=0; dex
<cd
->Length
; dex
++) {
610 printf("%02X ", cd
->Data
[dex
]);
611 if(((dex
% 16) == 15) && (dex
!= (cd
->Length
- 1))) {
619 #define dumpBuf(l, d)
620 #endif /* CMS_DUMP_BUFS */
623 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
624 * template for DER encoding and decoding it.
627 SECAlgorithmID algId
; /* KEK alg, NULL params */
628 CSSM_DATA entityUInfo
; /* optional, ukm */
629 CSSM_DATA suppPubInfo
; /* length of KEK in bits as 4-byte integer */
630 } ECC_CMS_SharedInfo
;
632 static const SecAsn1Template ECC_CMS_SharedInfoTemplate
[] = {
633 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(ECC_CMS_SharedInfo
) },
634 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
635 offsetof(ECC_CMS_SharedInfo
,entityUInfo
),
636 kSecAsn1OctetStringTemplate
},
637 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| SEC_ASN1_CONTEXT_SPECIFIC
| 2,
638 offsetof(ECC_CMS_SharedInfo
,suppPubInfo
),
639 kSecAsn1OctetStringTemplate
},
644 * Given a context specified via a CSSM_CC_HANDLE, add a new
645 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
646 * AttributeLength, and an untyped pointer.
648 /* specify either 32-bit integer or a pointer as an added attribute value */
654 /* convert uint32 to big-endian 4 bytes */
655 static void int32ToBytes(
660 for(dex
=3; dex
>=0; dex
--) {
667 * Given an OID tag, return key size and mode.
668 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
669 * be used here because the message does not contain a key size
672 static OSStatus
encrAlgInfo(
674 uint32
*keySizeBits
, /* RETURNED */
675 CCAlgorithm
*algorithm
, /* RETURNED */
676 CCOptions
*options
) /* RETURNED */
678 *keySizeBits
= 64; /* default */
679 *options
= kCCOptionPKCS7Padding
; /* default */
682 case SEC_OID_RC2_CBC
:
684 case SEC_OID_RC5_CBC_PAD
:
685 dprintf("encrAlgInfo: key size unknowable\n");
686 return errSecDataNotAvailable
;
687 case SEC_OID_DES_EDE
:
688 /* Not sure about this; SecCmsCipherContextStart() treats this
689 * like SEC_OID_DES_EDE3_CBC... */
690 *options
= kCCOptionECBMode
;
692 case SEC_OID_DES_EDE3_CBC
:
694 *algorithm
= kCCAlgorithm3DES
;
696 case SEC_OID_DES_ECB
:
697 *options
= kCCOptionECBMode
;
699 case SEC_OID_DES_CBC
:
700 *algorithm
= kCCAlgorithmDES
;
702 case SEC_OID_AES_128_CBC
:
704 *algorithm
= kCCAlgorithmAES
;
706 case SEC_OID_AES_192_CBC
:
708 *algorithm
= kCCAlgorithmAES
;
710 case SEC_OID_AES_256_CBC
:
712 *algorithm
= kCCAlgorithmAES
;
714 case SEC_OID_AES_128_ECB
:
716 *algorithm
= kCCAlgorithmAES
;
717 *options
= kCCOptionECBMode
;
719 case SEC_OID_AES_192_ECB
:
721 *algorithm
= kCCAlgorithmAES
;
722 *options
= kCCOptionECBMode
;
724 case SEC_OID_AES_256_ECB
:
726 *algorithm
= kCCAlgorithmAES
;
727 *options
= kCCOptionECBMode
;
730 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag
);
731 return errSecDataNotAvailable
;
736 #pragma mark ---- ECDH CEK key wrap ----
739 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
742 SecCmsUtilEncryptSymKeyECDH(
744 SecCertificateRef cert
, /* recipient's cert */
745 SecSymmetricKeyRef key
, /* bulk key */
746 /* remaining fields RETURNED */
747 CSSM_DATA_PTR encKey
, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
748 CSSM_DATA_PTR ukm
, /* random UKM --> KeyAgreeRecipientInfo.ukm */
749 SECAlgorithmID
*keyEncAlg
, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
750 * params := another encoded AlgId, with the KEK alg and IV */
751 CSSM_DATA_PTR pubKey
) /* our pub key as ECPoint -->
752 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
755 SecKeyRef theirPubKey
= NULL
, ourPubKey
= NULL
, ourPrivKey
= NULL
;
756 CFDictionaryRef theirKeyAttrs
= NULL
, ourKeyParams
= NULL
, kekParams
= NULL
;
757 uint8_t iv
[ECDH_KEK_IV_LEN_BYTES
];
758 CSSM_DATA ivData
= { ECDH_KEK_IV_LEN_BYTES
, iv
};
759 SECAlgorithmID kekAlgId
;
761 ECC_CMS_SharedInfo sharedInfo
;
762 CSSM_DATA sharedInfoEnc
= {0, NULL
};
763 uint8 nullData
[2] = {SEC_ASN1_NULL
, 0};
764 uint8 keyLenAsBytes
[4];
765 CFDataRef sharedInfoData
= NULL
, kekData
= NULL
, ourPubData
= NULL
;
766 CFNumberRef kekLen
= NULL
;
767 CFErrorRef error
= NULL
;
768 CCCryptorRef ciphercc
= NULL
;
773 /* Copy the recipient's static public ECDH key */
774 theirPubKey
= SecCertificateCopyKey(cert
);
775 if (rv
|| !theirPubKey
) {
776 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get public key from cert, %d\n", (int)rv
);
780 theirKeyAttrs
= SecKeyCopyAttributes(theirPubKey
);
781 if (!theirKeyAttrs
) {
782 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get key attributes\n");
786 CFStringRef keyType
= NULL
;
787 CFNumberRef keySizeNum
= NULL
;
788 keyType
= CFDictionaryGetValue(theirKeyAttrs
, kSecAttrKeyType
);
789 keySizeNum
= CFDictionaryGetValue(theirKeyAttrs
, kSecAttrKeySizeInBits
);
791 if (!CFEqual(kSecAttrKeyTypeECSECPrimeRandom
, keyType
)) {
792 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported key type\n");
793 rv
= CSSMERR_CSP_INVALID_KEY
;
797 /* Generate ephemeral ECDH key */
798 const void *keys
[] = { kSecAttrKeyType
, kSecAttrKeySizeInBits
, kSecAttrNoLegacy
};
799 const void *values
[] = { keyType
, keySizeNum
, kCFBooleanTrue
};
800 ourKeyParams
= CFDictionaryCreate(NULL
, keys
, values
, 3,
801 &kCFTypeDictionaryKeyCallBacks
,
802 &kCFTypeDictionaryValueCallBacks
);
803 rv
= SecKeyGeneratePair(ourKeyParams
, &ourPubKey
, &ourPrivKey
);
804 if (rv
|| !ourPubKey
|| !ourPrivKey
) {
805 dprintf("SecKeyGeneratePair: unable to generate ECDH key pair, %d\n", (int)rv
);
810 ukm
->Data
= PORT_Alloc(UKM_LENGTH
);
811 ukm
->Length
= UKM_LENGTH
;
812 rv
= CCRandomCopyBytes(kCCRandomDefault
, ukm
->Data
, UKM_LENGTH
);
813 if (rv
|| !ukm
->Data
) {
814 dprintf("CCRandomGenerateBytes failed, %d", (int)rv
);
819 * OK, we have to set up a weird SECAlgorithmID.
820 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
821 * params = an encoded SECAlgorithmID representing the KEK algorithm, with
822 * algorithm = whatever we pick
823 * parameters = IV as octet string (though I haven't seen that specified
824 * anywhere; it's how the CEK IV is encoded)
827 /* Generate 8-byte IV */
828 rv
= CCRandomCopyBytes(kCCRandomDefault
, iv
, ECDH_KEK_IV_LEN_BYTES
);
830 dprintf("CCRandomGenerateBytes failed, %d", (int)rv
);
833 dumpBuf("sender IV", &ivData
);
835 memset(&kekAlgId
, 0, sizeof(kekAlgId
));
836 if (!SEC_ASN1EncodeItem(poolp
, &kekAlgId
.parameters
,
837 &ivData
, kSecAsn1OctetStringTemplate
)) {
838 rv
= internalComponentErr
;
842 /* Drop in the KEK OID and encode the whole thing */
843 kekOid
= SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG
);
845 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
846 rv
= internalComponentErr
;
849 kekAlgId
.algorithm
= kekOid
->oid
;
850 memset(keyEncAlg
, 0, sizeof(*keyEncAlg
));
851 if (!SEC_ASN1EncodeItem(poolp
, &keyEncAlg
->parameters
,
852 &kekAlgId
, SECOID_AlgorithmIDTemplate
)) {
853 rv
= internalComponentErr
;
856 kekOid
= SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF
);
858 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
859 rv
= internalComponentErr
;
862 keyEncAlg
->algorithm
= kekOid
->oid
;
865 * Now in order to derive the KEK proper, we have to create a
866 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
867 * encode that struct, the result of which is used as the
868 * SharedInfo value in the KEK key derive.
870 memset(&sharedInfo
, 0, sizeof(sharedInfo
));
871 kekOid
= SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG
);
872 sharedInfo
.algId
.algorithm
= kekOid
->oid
;
873 sharedInfo
.algId
.parameters
.Data
= nullData
;
874 sharedInfo
.algId
.parameters
.Length
= 2;
875 sharedInfo
.entityUInfo
= *ukm
;
876 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES
<< 3, keyLenAsBytes
);
877 sharedInfo
.suppPubInfo
.Length
= 4;
878 sharedInfo
.suppPubInfo
.Data
= keyLenAsBytes
;
879 if (!SEC_ASN1EncodeItem(poolp
, &sharedInfoEnc
,
880 &sharedInfo
, ECC_CMS_SharedInfoTemplate
)) {
881 rv
= internalComponentErr
;
884 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc
);
887 sharedInfoData
= CFDataCreate(NULL
, sharedInfoEnc
.Data
, sharedInfoEnc
.Length
);
888 int32_t ecdh_key_key_len
= ECDH_KEK_KEY_LEN_BYTES
;
889 kekLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ecdh_key_key_len
);
890 const void *kekKeys
[] = { kSecKeyKeyExchangeParameterRequestedSize
, kSecKeyKeyExchangeParameterSharedInfo
};
891 const void *kekValues
[] = { kekLen
, sharedInfoData
};
892 kekParams
= CFDictionaryCreate(NULL
, kekKeys
, kekValues
, 2,
893 &kCFTypeDictionaryKeyCallBacks
,
894 &kCFTypeDictionaryValueCallBacks
);
895 kekData
= SecKeyCopyKeyExchangeResult(ourPrivKey
, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
,
896 theirPubKey
, kekParams
, &error
);
898 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
903 * Encrypt the raw CEK bits with the KEK we just derived
905 rv
= CCCryptorCreate(kCCEncrypt
, kCCAlgorithm3DES
, kCCOptionPKCS7Padding
,
906 CFDataGetBytePtr(kekData
), CFDataGetLength(kekData
), iv
, &ciphercc
);
908 dprintf("CCCryptorCreate failed: %d\n", (int)rv
);
912 rv
= cmsNullWrapKey(key
, &cek
);
914 dprintf("SecKeyGetCSSMKey failed: %d\n", (int)rv
);
917 size_t expectedEncKeyLength
= CCCryptorGetOutputLength(ciphercc
, cek
.KeyData
.Length
, true);
918 encKey
->Data
= PORT_ArenaAlloc(poolp
, expectedEncKeyLength
);
919 size_t bytes_output
= 0;
920 rv
= CCCryptorUpdate(ciphercc
, cek
.KeyData
.Data
, cek
.KeyData
.Length
, encKey
->Data
, expectedEncKeyLength
, &bytes_output
);
922 dprintf("CCCryptorUpdate failed: %d\n", (int)rv
);
925 size_t final_bytes_output
= 0;
926 rv
= CCCryptorFinal(ciphercc
, encKey
->Data
+bytes_output
, expectedEncKeyLength
- bytes_output
, &final_bytes_output
);
928 dprintf("CCCryptorFinal failed: %d\n", (int)rv
);
931 encKey
->Length
= bytes_output
+ final_bytes_output
;
933 /* Provide our ephemeral public key to the caller */
934 ourPubData
= SecKeyCopyExternalRepresentation(ourPubKey
, &error
);
936 dprintf("SecKeyCopyExternalRepresentation failed\n");
939 pubKey
->Length
= CFDataGetLength(ourPubData
);
940 pubKey
->Data
= malloc(pubKey
->Length
);
942 memcpy(pubKey
->Data
, CFDataGetBytePtr(ourPubData
), pubKey
->Length
);
946 /* pubKey is bit string, convert here */
947 pubKey
->Length
<<= 3;
950 if (theirPubKey
) { CFRelease(theirPubKey
); }
951 if (theirKeyAttrs
) { CFRelease(theirKeyAttrs
); }
952 if (ourKeyParams
) { CFRelease(ourKeyParams
); }
953 if (ourPubKey
) { CFRelease(ourPubKey
); }
954 if (ourPrivKey
) { CFRelease(ourPrivKey
); }
955 if (sharedInfoData
) { CFRelease(sharedInfoData
); }
956 if (kekLen
) { CFRelease(kekLen
); }
957 if (kekParams
) { CFRelease(kekParams
); }
958 if (kekData
) { CFRelease(kekData
); }
959 if (error
) { CFRelease(error
); }
960 if (ciphercc
) { CCCryptorRelease(ciphercc
); }
961 if (ourPubData
) { CFRelease(ourPubData
); }
962 if (rv
&& encKey
->Data
) {
963 PORT_Free(encKey
->Data
);
967 if (rv
&& ukm
->Data
) {
968 PORT_Free(ukm
->Data
);
976 #pragma mark ---- ECDH CEK key unwrap ----
979 SecCmsUtilDecryptSymKeyECDH(
980 SecPrivateKeyRef privkey
, /* our private key */
981 CSSM_DATA_PTR encKey
, /* encrypted CEK */
982 CSSM_DATA_PTR ukm
, /* random UKM from KeyAgreeRecipientInfo.ukm */
983 SECAlgorithmID
*keyEncAlg
, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
984 * params := another encoded AlgId, with the KEK alg and IV */
985 SECOidTag bulkalgtag
, /* algorithm of returned key */
986 CSSM_DATA_PTR pubKey
) /* sender's pub key as ECPoint from
987 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
989 SecSymmetricKeyRef outKey
= NULL
;
991 PLArenaPool
*pool
= NULL
;
992 SECAlgorithmID keyAlgParam
;
993 SECOidData
*kekOid
= NULL
;
994 CSSM_DATA iv
= {0, NULL
};
995 ECC_CMS_SharedInfo sharedInfo
;
996 CSSM_DATA sharedInfoEnc
= {0, NULL
};
997 uint8 nullData
[2] = {SEC_ASN1_NULL
, 0};
998 uint8 keyLenAsBytes
[4];
1000 SecKeyRef theirPubKey
= NULL
;
1001 CFStringRef keyType
= NULL
;
1002 CFDictionaryRef theirKeyAttrs
= NULL
, kekParams
= NULL
;
1003 CFMutableDictionaryRef cekParams
= NULL
;
1004 CFDataRef sharedInfoData
= NULL
, theirPubData
= NULL
, kekData
= NULL
, cekData
= NULL
;
1005 CFNumberRef kekLen
= NULL
, theirKeyLen
= NULL
;
1006 CFErrorRef error
= NULL
;
1008 CCOptions options
= 0;
1009 CCCryptorRef ciphercc
= NULL
;
1010 size_t theirKeySizeInBits
= 0;
1013 * Decode keyEncAlg.params to get KEK algorithm and IV
1015 pool
= PORT_NewArena(1024);
1019 memset(&keyAlgParam
, 0, sizeof(keyAlgParam
));
1020 if(SEC_ASN1DecodeItem(pool
, &keyAlgParam
, SECOID_AlgorithmIDTemplate
,
1021 &keyEncAlg
->parameters
)) {
1022 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1025 kekOid
= SECOID_FindOID(&keyAlgParam
.algorithm
);
1026 if(kekOid
== NULL
) {
1027 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1030 rv
= encrAlgInfo(kekOid
->offset
, &kekSizeBits
, &alg
, &options
);
1034 /* IV is OCTET STRING in the alg params */
1035 if(SEC_ASN1DecodeItem(pool
, &iv
, kSecAsn1OctetStringTemplate
,
1036 &keyAlgParam
.parameters
)) {
1038 * Not sure here - is it legal to have no IV? I haven't seen this
1039 * addressed in any spec. Maybe we should condition the behavior
1040 * here on the KEK algorithm.
1042 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1047 * Now in order to derive the KEK proper, we have to create a
1048 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1049 * encode that struct, the result of which is used as the
1050 * SharedInfo value in the KEK key derive.
1052 memset(&sharedInfo
, 0, sizeof(sharedInfo
));
1053 sharedInfo
.algId
.algorithm
= kekOid
->oid
;
1054 sharedInfo
.algId
.parameters
.Data
= nullData
;
1055 sharedInfo
.algId
.parameters
.Length
= 2;
1056 sharedInfo
.entityUInfo
= *ukm
;
1057 int32ToBytes(kekSizeBits
, keyLenAsBytes
);
1058 sharedInfo
.suppPubInfo
.Length
= 4;
1059 sharedInfo
.suppPubInfo
.Data
= keyLenAsBytes
;
1060 if (!SEC_ASN1EncodeItem(pool
, &sharedInfoEnc
,
1061 &sharedInfo
, ECC_CMS_SharedInfoTemplate
)) {
1062 rv
= internalComponentErr
;
1065 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc
);
1066 dumpBuf("receiver IV", &iv
);
1067 dumpBuf("receiver UKM", ukm
);
1068 dumpBuf("sender's public key", pubKey
);
1070 /* pubKey is bit string, convert here */
1071 theirKeySizeInBits
= pubKey
->Length
;
1072 pubKey
->Length
= (theirKeySizeInBits
+ 7) >> 3;
1073 theirPubData
= CFDataCreate(NULL
, pubKey
->Data
, pubKey
->Length
);
1074 theirKeyLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &theirKeySizeInBits
);
1075 const void *keys
[] = { kSecAttrKeyType
, kSecAttrKeyClass
, kSecAttrKeySizeInBits
};
1076 const void *values
[] = { kSecAttrKeyTypeECSECPrimeRandom
, kSecAttrKeyClassPublic
, theirKeyLen
};
1077 theirKeyAttrs
= CFDictionaryCreate(NULL
, keys
, values
, 3,
1078 &kCFTypeDictionaryKeyCallBacks
,
1079 &kCFTypeDictionaryValueCallBacks
);
1080 theirPubKey
= SecKeyCreateWithData(theirPubData
, theirKeyAttrs
, &error
);
1082 dprintf("SecKeyCreateWithData: failed\n");
1087 sharedInfoData
= CFDataCreate(NULL
, sharedInfoEnc
.Data
, sharedInfoEnc
.Length
);
1088 int32_t ecdh_key_key_len
= (kekSizeBits
+ 7) >> 3;
1089 kekLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ecdh_key_key_len
);
1090 const void *kekKeys
[] = { kSecKeyKeyExchangeParameterRequestedSize
, kSecKeyKeyExchangeParameterSharedInfo
};
1091 const void *kekValues
[] = { kekLen
, sharedInfoData
};
1092 kekParams
= CFDictionaryCreate(NULL
, kekKeys
, kekValues
, 2,
1093 &kCFTypeDictionaryKeyCallBacks
,
1094 &kCFTypeDictionaryValueCallBacks
);
1095 kekData
= SecKeyCopyKeyExchangeResult(privkey
, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
,
1096 theirPubKey
, kekParams
, &error
);
1098 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
1103 * Decrypt the raw CEK bits with the KEK we just derived
1105 CSSM_DATA cek
= { 0, NULL
};
1106 rv
= CCCryptorCreate(kCCDecrypt
, alg
, options
,
1107 CFDataGetBytePtr(kekData
), CFDataGetLength(kekData
), iv
.Data
, &ciphercc
);
1109 dprintf("CCCryptorCreate failed: %d\n", (int)rv
);
1112 size_t expectedKeyLength
= CCCryptorGetOutputLength(ciphercc
, encKey
->Length
, true);
1113 cek
.Data
= PORT_ArenaAlloc(pool
, expectedKeyLength
);
1114 size_t bytes_output
= 0;
1115 rv
= CCCryptorUpdate(ciphercc
, encKey
->Data
, encKey
->Length
, cek
.Data
, expectedKeyLength
, &bytes_output
);
1117 dprintf("CCCryptorUpdate failed: %d\n", (int)rv
);
1120 size_t final_bytes_output
= 0;
1121 rv
= CCCryptorFinal(ciphercc
, cek
.Data
+bytes_output
, expectedKeyLength
- bytes_output
, &final_bytes_output
);
1123 dprintf("CCCryptorFinal failed: %d\n", (int)rv
);
1126 cek
.Length
= bytes_output
+ final_bytes_output
;
1128 /* create the SecSymmetricKeyRef */
1129 cekData
= CFDataCreate(NULL
, cek
.Data
, cek
.Length
);
1130 keyType
= SECOID_CopyKeyTypeByTag(bulkalgtag
);
1134 cekParams
= CFDictionaryCreateMutable(NULL
, 1,
1135 &kCFTypeDictionaryKeyCallBacks
,
1136 &kCFTypeDictionaryValueCallBacks
);
1140 CFDictionaryAddValue(cekParams
, kSecAttrKeyType
, keyType
);
1141 outKey
= SecKeyCreateFromData(cekParams
, cekData
, NULL
);
1145 PORT_FreeArena(pool
, PR_FALSE
);
1147 if (theirPubData
) { CFRelease(theirPubData
); }
1148 if (theirKeyLen
) { CFRelease(theirKeyLen
); }
1149 if (theirPubKey
) { CFRelease(theirPubKey
); }
1150 if (theirKeyAttrs
) { CFRelease(theirKeyAttrs
); }
1151 if (sharedInfoData
) { CFRelease(sharedInfoData
); }
1152 if (kekLen
) { CFRelease(kekLen
); }
1153 if (kekParams
) { CFRelease(kekParams
); }
1154 if (kekData
) { CFRelease(kekData
); }
1155 if (error
) { CFRelease(error
); }
1156 if (ciphercc
) { CCCryptorRelease(ciphercc
); }
1157 if (cekData
) { CFRelease(cekData
); }
1158 if (keyType
) { CFRelease(keyType
); }
1159 if (cekParams
) { CFRelease(cekParams
); }
1160 if(outKey
== NULL
) {
1161 PORT_SetError(SEC_ERROR_NO_KEY
);