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
40 #include "SecAsn1Item.h"
44 #include <security_asn1/secasn1.h>
45 #include <security_asn1/secerr.h>
46 #include <security_asn1/secport.h>
48 #include <Security/Security.h>
49 #include <Security/SecCertificateInternal.h>
50 #include <Security/SecKeyPriv.h>
52 #include <CommonCrypto/CommonCryptor.h>
53 #include <CommonCrypto/CommonRandomSPI.h>
54 #include <CommonCrypto/CommonRandom.h>
56 /* ====== RSA ======================================================================= */
59 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
61 * this function takes a symmetric key and encrypts it using an RSA public key
62 * according to PKCS#1 and RFC2633 (S/MIME)
65 SecCmsUtilEncryptSymKeyRSA(PLArenaPool
*poolp
, SecCertificateRef cert
,
66 SecSymmetricKeyRef bulkkey
,
70 SecPublicKeyRef publickey
;
71 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
72 rv
= SecCertificateCopyPublicKey(cert
,&publickey
);
74 publickey
= SecCertificateCopyPublicKey(cert
);
76 if (publickey
== NULL
)
79 rv
= SecCmsUtilEncryptSymKeyRSAPubKey(poolp
, publickey
, bulkkey
, encKey
);
85 SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool
*poolp
,
86 SecPublicKeyRef publickey
,
87 SecSymmetricKeyRef bulkkey
, SecAsn1Item
* encKey
)
94 mark
= PORT_ArenaMark(poolp
);
100 keyType
= SECKEY_GetPublicKeyType(publickey
);
101 PORT_Assert(keyType
== rsaKey
);
102 if (keyType
!= rsaKey
) {
106 /* allocate memory for the encrypted key */
107 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
108 rv
= SecKeyGetStrengthInBits(publickey
, NULL
, &data_len
);
111 // Convert length to bytes;
112 data_len
= data_len
/ 8;
114 data_len
= SecKeyGetSize(publickey
, kSecKeyEncryptedDataSize
);
117 encKey
->Data
= (unsigned char*)PORT_ArenaAlloc(poolp
, data_len
);
118 encKey
->Length
= data_len
;
119 if (encKey
->Data
== NULL
)
122 /* encrypt the key now */
123 rv
= WRAP_PubWrapSymKey(publickey
, bulkkey
, encKey
);
124 if (rv
!= SECSuccess
)
127 PORT_ArenaUnmark(poolp
, mark
);
132 PORT_ArenaRelease(poolp
, mark
);
138 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
140 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
141 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
142 * a hardware token...
145 SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey
, SecAsn1Item
* encKey
, SECOidTag bulkalgtag
)
148 return WRAP_PubUnwrapSymKey(privkey
, encKey
, bulkalgtag
);
152 // @@@ Implement Fortezza and Diffie hellman support
154 /* ====== MISSI (Fortezza) ========================================================== */
156 extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams
[];
159 SecCmsUtilEncryptSymKeyMISSI(PLArenaPool
*poolp
, SecCertificateRef cert
, SecSymmetricKeyRef bulkkey
,
160 SECOidTag symalgtag
, SecAsn1Item
* encKey
, SecAsn1Item
* *pparams
, void *pwfn_arg
)
162 SECOidTag certalgtag
; /* the certificate's encryption algorithm */
163 SECOidTag encalgtag
; /* the algorithm used for key exchange/agreement */
164 OSStatus rv
= SECFailure
;
165 SecAsn1Item
* params
= NULL
;
167 SecSymmetricKeyRef tek
;
168 SecCertificateRef ourCert
;
169 SecPublicKeyRef ourPubKey
, *publickey
= NULL
;
170 SecPrivateKeyRef ourPrivKey
= NULL
;
171 SecCmsKEATemplateSelector whichKEA
= SecCmsKEAInvalid
;
172 SecCmsSMIMEKEAParameters keaParams
;
173 PLArenaPool
*arena
= NULL
;
174 const SECAlgorithmID
*algid
;
176 /* Clear keaParams, since cleanup code checks the lengths */
177 (void) memset(&keaParams
, 0, sizeof(keaParams
));
180 SecCertificateGetAlgorithmID(cert
,&algid
);
183 certalgtag
= SECOID_GetAlgorithmTag(algid
);
184 PORT_Assert(certalgtag
== SEC_OID_MISSI_KEA_DSS_OLD
||
185 certalgtag
== SEC_OID_MISSI_KEA_DSS
||
186 certalgtag
== SEC_OID_MISSI_KEA
);
188 #define SMIME_FORTEZZA_RA_LENGTH 128
189 #define SMIME_FORTEZZA_IV_LENGTH 24
190 #define SMIME_FORTEZZA_MAX_KEY_SIZE 256
192 /* We really want to show our KEA tag as the key exchange algorithm tag. */
193 encalgtag
= SEC_OID_NETSCAPE_SMIME_KEA
;
195 /* Get the public key of the recipient. */
196 publickey
= CERT_ExtractPublicKey(cert
);
197 if (publickey
== NULL
) goto loser
;
199 /* Find our own cert, and extract its keys. */
200 ourCert
= PK11_FindBestKEAMatch(cert
, pwfn_arg
);
201 if (ourCert
== NULL
) goto loser
;
203 arena
= PORT_NewArena(1024);
207 ourPubKey
= CERT_ExtractPublicKey(ourCert
);
208 if (ourPubKey
== NULL
) {
209 CERT_DestroyCertificate(ourCert
);
213 /* While we're here, copy the public key into the outgoing
215 SECITEM_CopyItem(arena
, &(keaParams
.originatorKEAKey
), &(ourPubKey
->u
.fortezza
.KEAKey
));
216 SECKEY_DestroyPublicKey(ourPubKey
);
219 /* Extract our private key in order to derive the KEA key. */
220 ourPrivKey
= PK11_FindKeyByAnyCert(ourCert
, pwfn_arg
);
221 CERT_DestroyCertificate(ourCert
); /* we're done with this */
225 /* Prepare raItem with 128 bytes (filled with zeros). */
226 keaParams
.originatorRA
.Data
= (unsigned char *)PORT_ArenaAlloc(arena
,SMIME_FORTEZZA_RA_LENGTH
);
227 keaParams
.originatorRA
.Length
= SMIME_FORTEZZA_RA_LENGTH
;
229 /* Generate the TEK (token exchange key) which we use
230 * to wrap the bulk encryption key. (keaparams.originatorRA) will be
231 * filled with a random seed which we need to send to
232 * the recipient. (user keying material in RFC2630/DSA speak) */
233 tek
= PK11_PubDerive(ourPrivKey
, publickey
, PR_TRUE
,
234 &keaParams
.originatorRA
, NULL
,
235 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
236 CKA_WRAP
, 0, pwfn_arg
);
238 SECKEY_DestroyPublicKey(publickey
);
239 SECKEY_DestroyPrivateKey(ourPrivKey
);
246 /* allocate space for the wrapped key data */
247 encKey
->Data
= (unsigned char *)PORT_ArenaAlloc(poolp
, SMIME_FORTEZZA_MAX_KEY_SIZE
);
248 encKey
->Length
= SMIME_FORTEZZA_MAX_KEY_SIZE
;
250 if (encKey
->Data
== NULL
) {
255 /* Wrap the bulk key. What we do with the resulting data
256 depends on whether we're using Skipjack to wrap the key. */
257 switch (PK11_AlgtagToMechanism(symalgtag
)) {
258 case CKM_SKIPJACK_CBC64
:
259 case CKM_SKIPJACK_ECB64
:
260 case CKM_SKIPJACK_OFB64
:
261 case CKM_SKIPJACK_CFB64
:
262 case CKM_SKIPJACK_CFB32
:
263 case CKM_SKIPJACK_CFB16
:
264 case CKM_SKIPJACK_CFB8
:
265 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */
266 err
= PK11_WrapSymKey(CKM_SKIPJACK_WRAP
, NULL
, tek
, bulkkey
, encKey
);
267 whichKEA
= SecCmsKEAUsesSkipjack
;
270 /* Not SKIPJACK, we encrypt the raw key data */
271 keaParams
.nonSkipjackIV
.Data
=
272 (unsigned char *)PORT_ArenaAlloc(arena
, SMIME_FORTEZZA_IV_LENGTH
);
273 keaParams
.nonSkipjackIV
.Length
= SMIME_FORTEZZA_IV_LENGTH
;
274 err
= PK11_WrapSymKey(CKM_SKIPJACK_CBC64
, &keaParams
.nonSkipjackIV
, tek
, bulkkey
, encKey
);
275 if (err
!= SECSuccess
)
278 if (encKey
->Length
!= PK11_GetKeyLength(bulkkey
)) {
279 /* The size of the encrypted key is not the same as
280 that of the original bulk key, presumably due to
281 padding. Encode and store the real size of the
283 if (SEC_ASN1EncodeInteger(arena
, &keaParams
.bulkKeySize
, PK11_GetKeyLength(bulkkey
)) == NULL
)
284 err
= (OSStatus
)PORT_GetError();
286 /* use full template for encoding */
287 whichKEA
= SecCmsKEAUsesNonSkipjackWithPaddedEncKey
;
290 /* enc key length == bulk key length */
291 whichKEA
= SecCmsKEAUsesNonSkipjack
;
297 if (err
!= SECSuccess
)
300 PORT_Assert(whichKEA
!= SecCmsKEAInvalid
);
302 /* Encode the KEA parameters into the recipient info. */
303 params
= SEC_ASN1EncodeItem(poolp
, NULL
, &keaParams
, nss_cms_get_kea_template(whichKEA
));
307 /* pass back the algorithm params */
314 PORT_FreeArena(arena
, PR_FALSE
);
316 SECKEY_DestroyPublicKey(publickey
);
318 SECKEY_DestroyPrivateKey(ourPrivKey
);
323 SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey
, SecAsn1Item
* encKey
, SECAlgorithmID
*keyEncAlg
, SECOidTag bulkalgtag
, void *pwfn_arg
)
325 /* fortezza: do a key exchange */
327 CK_MECHANISM_TYPE bulkType
;
328 SecSymmetricKeyRef tek
;
329 SecPublicKeyRef originatorPubKey
;
330 SecCmsSMIMEKEAParameters keaParams
;
331 SecSymmetricKeyRef bulkkey
;
334 (void) memset(&keaParams
, 0, sizeof(keaParams
));
336 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
337 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
339 /* Decode the KEA algorithm parameters. */
340 err
= SEC_ASN1DecodeItem(NULL
, &keaParams
, NSS_SMIMEKEAParamTemplateAllParams
,
341 &(keyEncAlg
->parameters
));
342 if (err
!= SECSuccess
)
345 /* get originator's public key */
346 originatorPubKey
= PK11_MakeKEAPubKey(keaParams
.originatorKEAKey
.Data
,
347 keaParams
.originatorKEAKey
.Length
);
348 if (originatorPubKey
== NULL
)
351 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
352 The Derive function generates a shared secret and combines it with the originatorRA
353 data to come up with an unique session key */
354 tek
= PK11_PubDerive(privkey
, originatorPubKey
, PR_FALSE
,
355 &keaParams
.originatorRA
, NULL
,
356 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
357 CKA_WRAP
, 0, pwfn_arg
);
358 SECKEY_DestroyPublicKey(originatorPubKey
); /* not needed anymore */
362 /* Now that we have the TEK, unwrap the bulk key
363 with which to decrypt the message. We have to
364 do one of two different things depending on
365 whether Skipjack was used for *bulk* encryption
367 bulkType
= PK11_AlgtagToMechanism(bulkalgtag
);
369 case CKM_SKIPJACK_CBC64
:
370 case CKM_SKIPJACK_ECB64
:
371 case CKM_SKIPJACK_OFB64
:
372 case CKM_SKIPJACK_CFB64
:
373 case CKM_SKIPJACK_CFB32
:
374 case CKM_SKIPJACK_CFB16
:
375 case CKM_SKIPJACK_CFB8
:
376 /* Skipjack is being used as the bulk encryption algorithm.*/
377 /* Unwrap the bulk key. */
378 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_WRAP
, NULL
,
379 encKey
, CKM_SKIPJACK_CBC64
, CKA_DECRYPT
, 0);
382 /* Skipjack was not used for bulk encryption of this
383 message. Use Skipjack CBC64, with the nonSkipjackIV
384 part of the KEA key parameters, to decrypt
385 the bulk key. If the optional parameter bulkKeySize is present,
386 bulk key size is different than the encrypted key size */
387 if (keaParams
.bulkKeySize
.Length
> 0) {
388 err
= SEC_ASN1DecodeItem(NULL
, &bulkLength
,
389 SEC_ASN1_GET(SEC_IntegerTemplate
),
390 &keaParams
.bulkKeySize
);
391 if (err
!= SECSuccess
)
395 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_CBC64
, &keaParams
.nonSkipjackIV
,
396 encKey
, bulkType
, CKA_DECRYPT
, bulkLength
);
404 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
407 SecCmsUtilEncryptSymKeyESDH(PLArenaPool
*poolp
, SecCertificateRef cert
, SecSymmetricKeyRef key
,
408 SecAsn1Item
* encKey
, SecAsn1Item
* *ukm
, SECAlgorithmID
*keyEncAlg
,
409 SecAsn1Item
* pubKey
)
411 #if 0 /* not yet done */
412 SECOidTag certalgtag
; /* the certificate's encryption algorithm */
413 SECOidTag encalgtag
; /* the algorithm used for key exchange/agreement */
415 SecAsn1Item
* params
= NULL
;
418 SecSymmetricKeyRef tek
;
419 SecCertificateRef ourCert
;
420 SecPublicKeyRef ourPubKey
;
421 SecCmsKEATemplateSelector whichKEA
= SecCmsKEAInvalid
;
423 certalgtag
= SECOID_GetAlgorithmTag(&(cert
->subjectPublicKeyInfo
.algorithm
));
424 PORT_Assert(certalgtag
== SEC_OID_X942_DIFFIE_HELMAN_KEY
);
426 /* We really want to show our KEA tag as the key exchange algorithm tag. */
427 encalgtag
= SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN
;
429 /* Get the public key of the recipient. */
430 publickey
= CERT_ExtractPublicKey(cert
);
431 if (publickey
== NULL
) goto loser
;
433 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
434 /* XXXX */ourCert
= PK11_FindBestKEAMatch(cert
, wincx
);
435 if (ourCert
== NULL
) goto loser
;
437 arena
= PORT_NewArena(1024);
438 if (arena
== NULL
) goto loser
;
440 /* While we're here, extract the key pair's public key data and copy it into */
441 /* the outgoing parameters. */
442 /* XXXX */ourPubKey
= CERT_ExtractPublicKey(ourCert
);
443 if (ourPubKey
== NULL
)
447 SECITEM_CopyItem(arena
, pubKey
, /* XXX */&(ourPubKey
->u
.fortezza
.KEAKey
));
448 SECKEY_DestroyPublicKey(ourPubKey
); /* we only need the private key from now on */
451 /* Extract our private key in order to derive the KEA key. */
452 ourPrivKey
= PK11_FindKeyByAnyCert(ourCert
,wincx
);
453 CERT_DestroyCertificate(ourCert
); /* we're done with this */
454 if (!ourPrivKey
) goto loser
;
456 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
458 ukm
->Data
= (unsigned char*)PORT_ArenaZAlloc(arena
,/* XXXX */);
459 ukm
->Length
= /* XXXX */;
462 /* Generate the KEK (key exchange key) according to RFC2631 which we use
463 * to wrap the bulk encryption key. */
464 kek
= PK11_PubDerive(ourPrivKey
, publickey
, PR_TRUE
,
466 /* XXXX */CKM_KEA_KEY_DERIVE
, /* XXXX */CKM_SKIPJACK_WRAP
,
469 SECKEY_DestroyPublicKey(publickey
);
470 SECKEY_DestroyPrivateKey(ourPrivKey
);
477 /* allocate space for the encrypted CEK (bulk key) */
478 encKey
->Data
= (unsigned char*)PORT_ArenaAlloc(poolp
, SMIME_FORTEZZA_MAX_KEY_SIZE
);
479 encKey
->Length
= SMIME_FORTEZZA_MAX_KEY_SIZE
;
481 if (encKey
->Data
== NULL
)
488 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
489 /* bulk encryption algorithm */
490 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo
->encalg
))
492 case /* XXXX */CKM_SKIPJACK_CFB8
:
493 err
= PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP
, NULL
, kek
, bulkkey
, encKey
);
494 whichKEA
= SecCmsKEAUsesSkipjack
;
496 case /* XXXX */CKM_SKIPJACK_CFB8
:
497 err
= PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP
, NULL
, kek
, bulkkey
, encKey
);
498 whichKEA
= SecCmsKEAUsesSkipjack
;
501 /* XXXX what do we do here? Neither RC2 nor 3DES... */
507 CFRelease(kek
); /* we do not need the KEK anymore */
508 if (err
!= SECSuccess
)
511 PORT_Assert(whichKEA
!= SecCmsKEAInvalid
);
513 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
514 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
515 params
= SEC_ASN1EncodeItem(arena
, NULL
, &keaParams
, sec_pkcs7_get_kea_template(whichKEA
));
519 /* now set keyEncAlg */
520 rv
= SECOID_SetAlgorithmID(poolp
, keyEncAlg
, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN
, params
);
521 if (rv
!= SECSuccess
)
524 /* XXXXXXX this is not right yet */
527 PORT_FreeArena(arena
, PR_FALSE
);
530 SECKEY_DestroyPublicKey(publickey
);
533 SECKEY_DestroyPrivateKey(ourPrivKey
);
540 SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey
, SecAsn1Item
* encKey
, SECAlgorithmID
*keyEncAlg
, SECOidTag bulkalgtag
, void *pwfn_arg
)
542 #if 0 /* not yet done */
544 CK_MECHANISM_TYPE bulkType
;
545 SecSymmetricKeyRef tek
;
546 SecPublicKeyRef originatorPubKey
;
547 SecCmsSMIMEKEAParameters keaParams
;
549 /* XXXX get originator's public key */
550 originatorPubKey
= PK11_MakeKEAPubKey(keaParams
.originatorKEAKey
.Data
,
551 keaParams
.originatorKEAKey
.Length
);
552 if (originatorPubKey
== NULL
)
555 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
556 The Derive function generates a shared secret and combines it with the originatorRA
557 data to come up with an unique session key */
558 tek
= PK11_PubDerive(privkey
, originatorPubKey
, PR_FALSE
,
559 &keaParams
.originatorRA
, NULL
,
560 CKM_KEA_KEY_DERIVE
, CKM_SKIPJACK_WRAP
,
561 CKA_WRAP
, 0, pwfn_arg
);
562 SECKEY_DestroyPublicKey(originatorPubKey
); /* not needed anymore */
566 /* Now that we have the TEK, unwrap the bulk key
567 with which to decrypt the message. */
568 /* Skipjack is being used as the bulk encryption algorithm.*/
569 /* Unwrap the bulk key. */
570 bulkkey
= PK11_UnwrapSymKey(tek
, CKM_SKIPJACK_WRAP
, NULL
,
571 encKey
, CKM_SKIPJACK_CBC64
, CKA_DECRYPT
, 0);
582 /* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
584 #pragma mark ---- ECDH support functions ----
587 #define CSSM_PERROR(f, r)
588 #define dprintf(args...)
590 #define CSSM_PERROR(f, r) cssmPerror(f, r)
591 #define dprintf(args...) fprintf(stderr, args)
594 /* Length of KeyAgreeRecipientInfo.ukm we create */
597 /* KEK algorithm info we generate */
598 #define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
599 #define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
600 #define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
601 #define ECDH_KEK_KEY_LEN_BYTES 24
602 #define ECDH_KEK_IV_LEN_BYTES 8
604 #define CMS_DUMP_BUFS 0
613 printf("%s:\n ", label
);
614 for(dex
=0; dex
<cd
->Length
; dex
++) {
615 printf("%02X ", cd
->Data
[dex
]);
616 if(((dex
% 16) == 15) && (dex
!= (cd
->Length
- 1))) {
624 #define dumpBuf(l, d)
625 #endif /* CMS_DUMP_BUFS */
628 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
629 * template for DER encoding and decoding it.
632 SECAlgorithmID algId
; /* KEK alg, NULL params */
633 SecAsn1Item entityUInfo
; /* optional, ukm */
634 SecAsn1Item suppPubInfo
; /* length of KEK in bits as 4-byte integer */
635 } ECC_CMS_SharedInfo
;
637 static const SecAsn1Template ECC_CMS_SharedInfoTemplate
[] = {
638 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(ECC_CMS_SharedInfo
) },
639 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
640 offsetof(ECC_CMS_SharedInfo
,entityUInfo
),
641 kSecAsn1OctetStringTemplate
},
642 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| SEC_ASN1_CONTEXT_SPECIFIC
| 2,
643 offsetof(ECC_CMS_SharedInfo
,suppPubInfo
),
644 kSecAsn1OctetStringTemplate
},
649 * Given a context specified via a CSSM_CC_HANDLE, add a new
650 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
651 * AttributeLength, and an untyped pointer.
653 /* specify either 32-bit integer or a pointer as an added attribute value */
659 /* convert uint32 to big-endian 4 bytes */
660 static void int32ToBytes(
665 for(dex
=3; dex
>=0; dex
--) {
672 * Given an OID tag, return key size and mode.
673 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
674 * be used here because the message does not contain a key size
677 static OSStatus
encrAlgInfo(
679 uint32_t *keySizeBits
, /* RETURNED */
680 CCAlgorithm
*algorithm
, /* RETURNED */
681 CCOptions
*options
) /* RETURNED */
683 *keySizeBits
= 64; /* default */
684 *options
= kCCOptionPKCS7Padding
; /* default */
687 case SEC_OID_RC2_CBC
:
689 case SEC_OID_RC5_CBC_PAD
:
690 dprintf("encrAlgInfo: key size unknowable\n");
691 return errSecNotAvailable
;
692 case SEC_OID_DES_EDE
:
693 /* Not sure about this; SecCmsCipherContextStart() treats this
694 * like SEC_OID_DES_EDE3_CBC... */
695 *options
= kCCOptionECBMode
;
697 case SEC_OID_DES_EDE3_CBC
:
699 *algorithm
= kCCAlgorithm3DES
;
701 case SEC_OID_DES_ECB
:
702 *options
= kCCOptionECBMode
;
704 case SEC_OID_DES_CBC
:
705 *algorithm
= kCCAlgorithmDES
;
707 case SEC_OID_AES_128_CBC
:
709 *algorithm
= kCCAlgorithmAES
;
711 case SEC_OID_AES_192_CBC
:
713 *algorithm
= kCCAlgorithmAES
;
715 case SEC_OID_AES_256_CBC
:
717 *algorithm
= kCCAlgorithmAES
;
719 case SEC_OID_AES_128_ECB
:
721 *algorithm
= kCCAlgorithmAES
;
722 *options
= kCCOptionECBMode
;
724 case SEC_OID_AES_192_ECB
:
726 *algorithm
= kCCAlgorithmAES
;
727 *options
= kCCOptionECBMode
;
729 case SEC_OID_AES_256_ECB
:
731 *algorithm
= kCCAlgorithmAES
;
732 *options
= kCCOptionECBMode
;
735 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag
);
736 return errSecNotAvailable
;
741 #pragma mark ---- ECDH CEK key wrap ----
744 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
747 SecCmsUtilEncryptSymKeyECDH(
749 SecCertificateRef cert
, /* recipient's cert */
750 SecSymmetricKeyRef key
, /* bulk key */
751 /* remaining fields RETURNED */
752 SecAsn1Item
*encKey
, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
753 SecAsn1Item
*ukm
, /* random UKM --> KeyAgreeRecipientInfo.ukm */
754 SECAlgorithmID
*keyEncAlg
, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
755 * params := another encoded AlgId, with the KEK alg and IV */
756 SecAsn1Item
*pubKey
) /* our pub key as ECPoint -->
757 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
760 SecKeyRef theirPubKey
= NULL
, ourPubKey
= NULL
, ourPrivKey
= NULL
;
761 CFDictionaryRef theirKeyAttrs
= NULL
, ourKeyParams
= NULL
, kekParams
= NULL
;
762 uint8_t iv
[ECDH_KEK_IV_LEN_BYTES
];
763 SecAsn1Item ivData
= { ECDH_KEK_IV_LEN_BYTES
, iv
};
764 SECAlgorithmID kekAlgId
;
766 ECC_CMS_SharedInfo sharedInfo
;
767 SecAsn1Item sharedInfoEnc
= {0, NULL
};
768 uint8_t nullData
[2] = {SEC_ASN1_NULL
, 0};
769 uint8_t keyLenAsBytes
[4];
770 CFDataRef sharedInfoData
= NULL
, kekData
= NULL
, ourPubData
= NULL
;
771 CFNumberRef kekLen
= NULL
;
772 CFErrorRef error
= NULL
;
773 CCCryptorRef ciphercc
= NULL
;
778 /* Copy the recipient's static public ECDH key */
780 theirPubKey
= SecCertificateCopyPublicKey(cert
);
782 rv
= SecCertificateCopyPublicKey(cert
, &theirPubKey
);
784 if (rv
|| !theirPubKey
) {
785 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get public key from cert, %d\n", (int)rv
);
789 theirKeyAttrs
= SecKeyCopyAttributes(theirPubKey
);
790 if (!theirKeyAttrs
) {
791 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get key attributes\n");
795 CFStringRef keyType
= NULL
;
796 CFNumberRef keySizeNum
= NULL
;
797 keyType
= CFDictionaryGetValue(theirKeyAttrs
, kSecAttrKeyType
);
798 keySizeNum
= CFDictionaryGetValue(theirKeyAttrs
, kSecAttrKeySizeInBits
);
800 if (!CFEqual(kSecAttrKeyTypeECSECPrimeRandom
, keyType
)) {
801 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported key type\n");
802 rv
= SEC_ERROR_INVALID_KEY
;
806 /* Generate ephemeral ECDH key */
807 const void *keys
[] = { kSecAttrKeyType
, kSecAttrKeySizeInBits
};
808 const void *values
[] = { keyType
, keySizeNum
};
809 ourKeyParams
= CFDictionaryCreate(NULL
, keys
, values
, 2,
810 &kCFTypeDictionaryKeyCallBacks
,
811 &kCFTypeDictionaryValueCallBacks
);
812 rv
= SecKeyGeneratePair(ourKeyParams
, &ourPubKey
, &ourPrivKey
);
813 if (rv
|| !ourPubKey
|| !ourPrivKey
) {
814 dprintf("SecKeyGeneratePair: unable to generate ECDH key pair, %d\n", (int)rv
);
819 ukm
->Data
= PORT_Alloc(UKM_LENGTH
);
820 ukm
->Length
= UKM_LENGTH
;
821 rv
= CCRandomCopyBytes(kCCRandomDefault
, ukm
->Data
, UKM_LENGTH
);
822 if (rv
|| !ukm
->Data
) {
823 dprintf("CCRandomGenerateBytes failed, %d", (int)rv
);
826 ukm
->Length
= UKM_LENGTH
;
829 * OK, we have to set up a weird SECAlgorithmID.
830 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
831 * params = an encoded SECAlgorithmID representing the KEK algorithm, with
832 * algorithm = whatever we pick
833 * parameters = IV as octet string (though I haven't seen that specified
834 * anywhere; it's how the CEK IV is encoded)
837 /* Generate 8-byte IV */
838 rv
= CCRandomCopyBytes(kCCRandomDefault
, iv
, ECDH_KEK_IV_LEN_BYTES
);
840 dprintf("CCRandomGenerateBytes failed, %d", (int)rv
);
843 dumpBuf("sender IV", &ivData
);
845 memset(&kekAlgId
, 0, sizeof(kekAlgId
));
846 if (!SEC_ASN1EncodeItem(poolp
, &kekAlgId
.parameters
,
847 &ivData
, kSecAsn1OctetStringTemplate
)) {
848 rv
= errSecInternalComponent
;
852 /* Drop in the KEK OID and encode the whole thing */
853 kekOid
= SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG
);
855 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
856 rv
= errSecInternalComponent
;
859 kekAlgId
.algorithm
= kekOid
->oid
;
860 memset(keyEncAlg
, 0, sizeof(*keyEncAlg
));
861 if (!SEC_ASN1EncodeItem(poolp
, &keyEncAlg
->parameters
,
862 &kekAlgId
, SECOID_AlgorithmIDTemplate
)) {
863 rv
= errSecInternalComponent
;
866 kekOid
= SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF
);
868 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
869 rv
= errSecInternalComponent
;
872 keyEncAlg
->algorithm
= kekOid
->oid
;
875 * Now in order to derive the KEK proper, we have to create a
876 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
877 * encode that struct, the result of which is used as the
878 * SharedInfo value in the KEK key derive.
880 memset(&sharedInfo
, 0, sizeof(sharedInfo
));
881 kekOid
= SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG
);
882 sharedInfo
.algId
.algorithm
= kekOid
->oid
;
883 sharedInfo
.algId
.parameters
.Data
= nullData
;
884 sharedInfo
.algId
.parameters
.Length
= 2;
885 sharedInfo
.entityUInfo
= *ukm
;
886 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES
<< 3, keyLenAsBytes
);
887 sharedInfo
.suppPubInfo
.Length
= 4;
888 sharedInfo
.suppPubInfo
.Data
= keyLenAsBytes
;
889 if (!SEC_ASN1EncodeItem(poolp
, &sharedInfoEnc
,
890 &sharedInfo
, ECC_CMS_SharedInfoTemplate
)) {
891 rv
= errSecInternalComponent
;
894 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc
);
897 sharedInfoData
= CFDataCreate(NULL
, sharedInfoEnc
.Data
, sharedInfoEnc
.Length
);
898 int32_t ecdh_key_key_len
= ECDH_KEK_KEY_LEN_BYTES
;
899 kekLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ecdh_key_key_len
);
900 const void *kekKeys
[] = { kSecKeyKeyExchangeParameterRequestedSize
, kSecKeyKeyExchangeParameterSharedInfo
};
901 const void *kekValues
[] = { kekLen
, sharedInfoData
};
902 kekParams
= CFDictionaryCreate(NULL
, kekKeys
, kekValues
, 2,
903 &kCFTypeDictionaryKeyCallBacks
,
904 &kCFTypeDictionaryValueCallBacks
);
905 kekData
= SecKeyCopyKeyExchangeResult(ourPrivKey
, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
,
906 theirPubKey
, kekParams
, &error
);
908 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
913 * Encrypt the raw CEK bits with the KEK we just derived
915 rv
= CCCryptorCreate(kCCEncrypt
, kCCAlgorithm3DES
, kCCOptionPKCS7Padding
,
916 CFDataGetBytePtr(kekData
), CFDataGetLength(kekData
), iv
, &ciphercc
);
918 dprintf("CCCryptorCreate failed: %d\n", (int)rv
);
922 size_t expectedEncKeyLength
= CCCryptorGetOutputLength(ciphercc
, CFDataGetLength(key
), true);
923 encKey
->Data
= PORT_ArenaAlloc(poolp
, expectedEncKeyLength
);
924 size_t bytes_output
= 0;
925 rv
= CCCryptorUpdate(ciphercc
, CFDataGetBytePtr(key
), CFDataGetLength(key
), encKey
->Data
, expectedEncKeyLength
, &bytes_output
);
927 dprintf("CCCryptorUpdate failed: %d\n", (int)rv
);
930 size_t final_bytes_output
= 0;
931 rv
= CCCryptorFinal(ciphercc
, encKey
->Data
+bytes_output
, expectedEncKeyLength
- bytes_output
, &final_bytes_output
);
933 dprintf("CCCryptorFinal failed: %d\n", (int)rv
);
936 encKey
->Length
= bytes_output
+ final_bytes_output
;
938 /* Provide our ephemeral public key to the caller */
939 ourPubData
= SecKeyCopyExternalRepresentation(ourPubKey
, &error
);
941 dprintf("SecKeyCopyExternalRepresentation failed\n");
944 pubKey
->Length
= CFDataGetLength(ourPubData
);
945 pubKey
->Data
= malloc(pubKey
->Length
);
947 memcpy(pubKey
->Data
, CFDataGetBytePtr(ourPubData
), pubKey
->Length
);
951 /* pubKey is bit string, convert here */
952 pubKey
->Length
<<= 3;
955 if (theirPubKey
) { CFRelease(theirPubKey
); }
956 if (theirKeyAttrs
) { CFRelease(theirKeyAttrs
); }
957 if (ourKeyParams
) { CFRelease(ourKeyParams
); }
958 if (ourPubKey
) { CFRelease(ourPubKey
); }
959 if (ourPrivKey
) { CFRelease(ourPrivKey
); }
960 if (sharedInfoData
) { CFRelease(sharedInfoData
); }
961 if (kekLen
) { CFRelease(kekLen
); }
962 if (kekParams
) { CFRelease(kekParams
); }
963 if (kekData
) { CFRelease(kekData
); }
964 if (error
) { CFRelease(error
); }
965 if (ciphercc
) { CCCryptorRelease(ciphercc
); }
966 if (ourPubData
) { CFRelease(ourPubData
); }
967 if (rv
&& encKey
->Data
) {
968 PORT_Free(encKey
->Data
);
972 if (rv
&& ukm
->Data
) {
973 PORT_Free(ukm
->Data
);
981 #pragma mark ---- ECDH CEK key unwrap ----
984 SecCmsUtilDecryptSymKeyECDH(
985 SecPrivateKeyRef privkey
, /* our private key */
986 SecAsn1Item
*encKey
, /* encrypted CEK */
987 SecAsn1Item
*ukm
, /* random UKM from KeyAgreeRecipientInfo.ukm */
988 SECAlgorithmID
*keyEncAlg
, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
989 * params := another encoded AlgId, with the KEK alg and IV */
990 SECOidTag bulkalgtag
, /* algorithm of returned key */
991 SecAsn1Item
*pubKey
) /* sender's pub key as ECPoint from
992 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
994 SecSymmetricKeyRef outKey
= NULL
;
996 PLArenaPool
*pool
= NULL
;
997 SECAlgorithmID keyAlgParam
;
998 SECOidData
*kekOid
= NULL
;
999 SecAsn1Item iv
= {0, NULL
};
1000 ECC_CMS_SharedInfo sharedInfo
;
1001 SecAsn1Item sharedInfoEnc
= {0, NULL
};
1002 uint8_t nullData
[2] = {SEC_ASN1_NULL
, 0};
1003 uint8_t keyLenAsBytes
[4];
1004 uint32_t kekSizeBits
;
1005 SecKeyRef theirPubKey
= NULL
;
1006 CFDictionaryRef theirKeyAttrs
= NULL
, kekParams
= NULL
;
1007 CFDataRef sharedInfoData
= NULL
, theirPubData
= NULL
, kekData
= NULL
;
1008 CFNumberRef kekLen
= NULL
, theirKeyLen
= NULL
;
1009 CFErrorRef error
= NULL
;
1011 CCOptions options
= 0;
1012 CCCryptorRef ciphercc
= NULL
;
1013 size_t theirKeySizeInBits
= 0;
1016 * Decode keyEncAlg.params to get KEK algorithm and IV
1018 pool
= PORT_NewArena(1024);
1022 memset(&keyAlgParam
, 0, sizeof(keyAlgParam
));
1023 if(SEC_ASN1DecodeItem(pool
, &keyAlgParam
, SECOID_AlgorithmIDTemplate
,
1024 &keyEncAlg
->parameters
)) {
1025 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1028 kekOid
= SECOID_FindOID(&keyAlgParam
.algorithm
);
1029 if(kekOid
== NULL
) {
1030 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1033 rv
= encrAlgInfo(kekOid
->offset
, &kekSizeBits
, &alg
, &options
);
1037 /* IV is OCTET STRING in the alg params */
1038 if(SEC_ASN1DecodeItem(pool
, &iv
, kSecAsn1OctetStringTemplate
,
1039 &keyAlgParam
.parameters
)) {
1041 * Not sure here - is it legal to have no IV? I haven't seen this
1042 * addressed in any spec. Maybe we should condition the behavior
1043 * here on the KEK algorithm.
1045 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1050 * Now in order to derive the KEK proper, we have to create a
1051 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1052 * encode that struct, the result of which is used as the
1053 * SharedInfo value in the KEK key derive.
1055 memset(&sharedInfo
, 0, sizeof(sharedInfo
));
1056 sharedInfo
.algId
.algorithm
= kekOid
->oid
;
1057 sharedInfo
.algId
.parameters
.Data
= nullData
;
1058 sharedInfo
.algId
.parameters
.Length
= 2;
1059 sharedInfo
.entityUInfo
= *ukm
;
1060 int32ToBytes(kekSizeBits
, keyLenAsBytes
);
1061 sharedInfo
.suppPubInfo
.Length
= 4;
1062 sharedInfo
.suppPubInfo
.Data
= keyLenAsBytes
;
1063 if (!SEC_ASN1EncodeItem(pool
, &sharedInfoEnc
,
1064 &sharedInfo
, ECC_CMS_SharedInfoTemplate
)) {
1065 rv
= errSecInternalComponent
;
1068 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc
);
1069 dumpBuf("receiver IV", &iv
);
1070 dumpBuf("receiver UKM", ukm
);
1071 dumpBuf("sender's public key", pubKey
);
1073 /* pubKey is bit string, convert here */
1074 theirKeySizeInBits
= pubKey
->Length
;
1075 pubKey
->Length
= (theirKeySizeInBits
+ 7) >> 3;
1076 theirPubData
= CFDataCreate(NULL
, pubKey
->Data
, pubKey
->Length
);
1077 theirKeyLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &theirKeySizeInBits
);
1078 const void *keys
[] = { kSecAttrKeyType
, kSecAttrKeyClass
, kSecAttrKeySizeInBits
};
1079 const void *values
[] = { kSecAttrKeyTypeECSECPrimeRandom
, kSecAttrKeyClassPublic
, theirKeyLen
};
1080 theirKeyAttrs
= CFDictionaryCreate(NULL
, keys
, values
, 3,
1081 &kCFTypeDictionaryKeyCallBacks
,
1082 &kCFTypeDictionaryValueCallBacks
);
1083 theirPubKey
= SecKeyCreateWithData(theirPubData
, theirKeyAttrs
, &error
);
1085 dprintf("SecKeyCreateWithData: failed\n");
1090 sharedInfoData
= CFDataCreate(NULL
, sharedInfoEnc
.Data
, sharedInfoEnc
.Length
);
1091 int32_t ecdh_key_key_len
= (kekSizeBits
+ 7) >> 3;
1092 kekLen
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ecdh_key_key_len
);
1093 const void *kekKeys
[] = { kSecKeyKeyExchangeParameterRequestedSize
, kSecKeyKeyExchangeParameterSharedInfo
};
1094 const void *kekValues
[] = { kekLen
, sharedInfoData
};
1095 kekParams
= CFDictionaryCreate(NULL
, kekKeys
, kekValues
, 2,
1096 &kCFTypeDictionaryKeyCallBacks
,
1097 &kCFTypeDictionaryValueCallBacks
);
1098 kekData
= SecKeyCopyKeyExchangeResult(privkey
, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
,
1099 theirPubKey
, kekParams
, &error
);
1101 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
1106 * Decrypt the raw CEK bits with the KEK we just derived
1108 SecAsn1Item cek
= { 0, NULL
};
1109 rv
= CCCryptorCreate(kCCDecrypt
, alg
, options
,
1110 CFDataGetBytePtr(kekData
), CFDataGetLength(kekData
), iv
.Data
, &ciphercc
);
1112 dprintf("CCCryptorCreate failed: %d\n", (int)rv
);
1115 size_t expectedKeyLength
= CCCryptorGetOutputLength(ciphercc
, encKey
->Length
, true);
1116 cek
.Data
= PORT_ArenaAlloc(pool
, expectedKeyLength
);
1117 size_t bytes_output
= 0;
1118 rv
= CCCryptorUpdate(ciphercc
, encKey
->Data
, encKey
->Length
, cek
.Data
, expectedKeyLength
, &bytes_output
);
1120 dprintf("CCCryptorUpdate failed: %d\n", (int)rv
);
1123 size_t final_bytes_output
= 0;
1124 rv
= CCCryptorFinal(ciphercc
, cek
.Data
+bytes_output
, expectedKeyLength
- bytes_output
, &final_bytes_output
);
1126 dprintf("CCCryptorFinal failed: %d\n", (int)rv
);
1129 cek
.Length
= bytes_output
+ final_bytes_output
;
1131 /* create the SecSymmetricKeyRef */
1132 outKey
= (SecSymmetricKeyRef
)CFDataCreate(NULL
, cek
.Data
, cek
.Length
);
1136 PORT_FreeArena(pool
, PR_FALSE
);
1138 if (theirPubData
) { CFRelease(theirPubData
); }
1139 if (theirKeyLen
) { CFRelease(theirKeyLen
); }
1140 if (theirPubKey
) { CFRelease(theirPubKey
); }
1141 if (theirKeyAttrs
) { CFRelease(theirKeyAttrs
); }
1142 if (sharedInfoData
) { CFRelease(sharedInfoData
); }
1143 if (kekLen
) { CFRelease(kekLen
); }
1144 if (kekParams
) { CFRelease(kekParams
); }
1145 if (kekData
) { CFRelease(kekData
); }
1146 if (error
) { CFRelease(error
); }
1147 if (ciphercc
) { CCCryptorRelease(ciphercc
); }
1148 if(outKey
== NULL
) {
1149 PORT_SetError(SEC_ERROR_NO_KEY
);