2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 * FEEAsymmetricContext.cpp - CSPContexts for FEE asymmetric encryption
24 #ifdef CRYPTKIT_CSP_ENABLE
26 #include "FEEAsymmetricContext.h"
27 #include "FEECSPUtils.h"
28 #include <security_cryptkit/falloc.h>
29 #include <CommonCrypto/CommonDigest.h>
31 /* validate context for FEED and FEEDExp - no unexpected attributes allowed */
32 static void validateFeedContext(
33 const Context
&context
)
35 /* Note we cannot distinguish between zero and "not there" */
36 uint32 blockSize
= context
.getInt(CSSM_ATTRIBUTE_BLOCK_SIZE
);
38 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_BLOCK_SIZE
);
40 CSSM_ENCRYPT_MODE cssmMode
= context
.getInt(CSSM_ATTRIBUTE_MODE
);
42 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
45 /* we allow this for CMS wrapping */
46 CssmData
*iv
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
);
48 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
51 CSSM_PADDING padding
= context
.getInt(CSSM_ATTRIBUTE_PADDING
);
53 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
58 *** FEED - 1:1 FEED - encrypt n bytes of plaintext, get (roughly) n bytes
59 *** of ciphertext. Ciphertext is smaller than with FEED, but this is slower.
61 CryptKit::FEEDContext::~FEEDContext()
64 feeFEEDFree(mFeeFeed
);
67 if(mPrivKey
&& mAllocdPrivKey
) {
68 feePubKeyFree(mPrivKey
);
70 if(mPubKey
&& mAllocdPubKey
) {
71 feePubKeyFree(mPubKey
);
78 // called by CSPFullPluginSession; reusable
79 void CryptKit::FEEDContext::init(
80 const Context
&context
,
83 if(mInitFlag
&& !opStarted()) {
84 /* reusing - e.g. query followed by encrypt */
89 * Fetch FEE keys from context. This is an unusual algorithm - it requires
90 * two keys, one public and one private. The public key MUST be stored in
91 * the context with attribute type CSSM_ATTRIBUTE_PUBLIC_KEY, and the private
92 * key with CSSM_ATTRIBUTE_KEY.
94 * For now, we require CSSM_KEYUSE_ANY for FEE keys used for this algorithm.
95 * Otherwise we'd have to allow both KEYUSE_ENCRYPT and KEYUSE_DECRYPT for
96 * both keys, and that would require some algorithm-specific hack in
97 * cspValidateKeyUsageBits() which I really don't want to do.
99 if(mPrivKey
== NULL
) {
100 assert(!opStarted());
101 mPrivKey
= contextToFeeKey(context
,
104 CSSM_KEYCLASS_PRIVATE_KEY
,
111 if(mPubKey
== NULL
) {
112 assert(!opStarted());
113 mPubKey
= contextToFeeKey(context
,
115 CSSM_ATTRIBUTE_PUBLIC_KEY
,
116 CSSM_KEYCLASS_PUBLIC_KEY
,
124 /* validate context - no other attributes allowed */
125 validateFeedContext(context
);
127 if(mFeeFeed
!= NULL
) {
130 feeFEEDFree(mFeeFeed
);
134 /* OK, looks good. Cook up a feeFEED object. */
135 mFeeFeed
= feeFEEDNewWithPubKey(mPrivKey
,
140 if(mFeeFeed
== NULL
) {
141 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
144 /* finally, have BlockCryptor set up its stuff. */
145 unsigned plainBlockSize
= feeFEEDPlainBlockSize(mFeeFeed
);
146 unsigned cipherBlockSize
= feeFEEDCipherBlockSize(mFeeFeed
);
147 setup(encoding
? plainBlockSize
: cipherBlockSize
, // blockSizeIn
148 encoding
? cipherBlockSize
: plainBlockSize
, // blockSizeOut
156 // called by BlockCryptor
157 void CryptKit::FEEDContext::encryptBlock(
158 const void *plainText
, // length implied (one block)
161 size_t &cipherTextLen
, // in/out, throws on overflow
167 assert(mFeeFeed
!= NULL
);
168 frtn
= feeFEEDEncryptBlock(mFeeFeed
,
169 (unsigned char *)plainText
,
170 (unsigned int)plainTextLen
,
171 (unsigned char *)cipherText
,
175 throwCryptKit(frtn
, "feeFEEDEncryptBlock");
177 if(actMoved
> cipherTextLen
) {
178 /* Overflow already occurred! */
179 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
181 cipherTextLen
= actMoved
;
184 void CryptKit::FEEDContext::decryptBlock(
185 const void *cipherText
, // length implied (one cipher block)
186 size_t cipherTextLen
,
188 size_t &plainTextLen
, // in/out, throws on overflow
194 assert(mFeeFeed
!= NULL
);
195 frtn
= feeFEEDDecryptBlock(mFeeFeed
,
196 (unsigned char *)cipherText
,
197 (unsigned int)inBlockSize(),
198 (unsigned char *)plainText
,
202 throwCryptKit(frtn
, "feeFEEDDecryptBlock");
204 if(actMoved
> plainTextLen
) {
205 /* Overflow already occurred! */
206 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
208 plainTextLen
= actMoved
;
212 * Additional query size support, necessary because we don't conform to
213 * BlockCryptor's standard one-to-one block scheme
216 #define BUFFER_DEBUG 0
218 #define bprintf(s) printf s
223 size_t CryptKit::FEEDContext::inputSize(
224 size_t outSize
) // input for given output size
227 * We've been assured that this is NOT called for the final() op...
231 inSize
= feeFEEDPlainTextSize(mFeeFeed
, (unsigned int)outSize
, 0);
234 inSize
= feeFEEDCipherTextSize(mFeeFeed
, (unsigned int)outSize
, 0);
237 /* account for possible pending buffered input */
238 if(inSize
>= inBufSize()) {
239 inSize
-= inBufSize();
242 /* round up to next block size, then lop off one...anything from
243 * blockSize*n to (blockSize*n)-1 has same effect */
244 unsigned inBlocks
= (unsigned int)((inSize
+ inBlockSize()) / inBlockSize());
245 inSize
= (unsigned int)(inBlocks
* inBlockSize()) - 1;
246 bprintf(("--- FEEDContext::inputSize inSize 0x%x outSize 0x%x\n",
251 size_t CryptKit::FEEDContext::outputSize(
253 size_t inSize
) // output for given input size
257 rtn
= feeFEEDCipherTextSize(mFeeFeed
, (unsigned int)(inSize
+ inBufSize()), final
? 1 : 0);
260 rtn
= feeFEEDPlainTextSize(mFeeFeed
, (unsigned int)(inSize
+ inBufSize()), final
? 1 : 0);
262 bprintf(("--- FEEDContext::outputSize inSize 0x%x outSize 0x%x final %d\n",
263 inSize
, rtn
, final
));
267 void CryptKit::FEEDContext::minimumProgress(
269 size_t &out
) // minimum progress chunks
273 * -- in := one block plaintext
274 * -- out := current cipher size for one block plaintext
277 out
= feeFEEDCipherBufSize(mFeeFeed
, 0);
281 * -- in := current cipher size for one block plaintext
282 * -- out := one block plaintext
284 in
= feeFEEDCipherBufSize(mFeeFeed
, 0);
285 out
= outBlockSize();
289 * Either case - input adjusted for pending. Note inBufSize can be up to one
290 * input block size, leaving the temp result zero here....
292 assert(in
>= inBufSize());
295 /* if it is zero, bump it up so caller can make something happen */
299 bprintf(("--- FEEDContext::minProgres inSize 0x%x outSize 0x%x\n",
304 *** FEEDExp - 2:1 FEED - encrypt n bytes of plaintext, get (roughly) 2n bytes
305 *** of ciphertext. Ciphertext is larger than with FEED, but this is faster.
307 CryptKit::FEEDExpContext::~FEEDExpContext()
310 feeFEEDExpFree(mFeeFeedExp
);
313 if(mFeeKey
&& mAllocdFeeKey
) {
314 feePubKeyFree(mFeeKey
);
320 // called by CSPFullPluginSession; reusable
321 void CryptKit::FEEDExpContext::init(
322 const Context
&context
,
325 if(mInitFlag
&& !opStarted()) {
326 /* reusing - e.g. query followed by encrypt */
330 /* fetch FEE key from context */
331 CSSM_KEYCLASS keyClass
;
335 /* encrypting to public key */
336 keyClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
337 keyUse
= CSSM_KEYUSE_ENCRYPT
;
340 /* decrypting with private key */
341 keyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
342 keyUse
= CSSM_KEYUSE_DECRYPT
;
344 if(mFeeKey
== NULL
) {
345 assert(!opStarted());
346 mFeeKey
= contextToFeeKey(context
,
357 /* validate context - no other attributes allowed */
358 validateFeedContext(context
);
360 /* OK, looks good. Cook up a feeFEEDExp object. */
361 if(mFeeFeedExp
!= NULL
) {
364 feeFEEDExpFree(mFeeFeedExp
);
367 mFeeFeedExp
= feeFEEDExpNewWithPubKey(mFeeKey
,
370 if(mFeeFeedExp
== NULL
) {
371 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
374 /* finally, have BlockCryptor set up its stuff. */
375 unsigned plainBlockSize
= feeFEEDExpPlainBlockSize(mFeeFeedExp
);
376 unsigned cipherBlockSize
= feeFEEDExpCipherBlockSize(mFeeFeedExp
);
377 setup(encoding
? plainBlockSize
: cipherBlockSize
, // blockSizeIn
378 encoding
? cipherBlockSize
: plainBlockSize
, // blockSizeOut
386 // called by BlockCryptor
387 void CryptKit::FEEDExpContext::encryptBlock(
388 const void *plainText
, // length implied (one block)
391 size_t &cipherTextLen
, // in/out, throws on overflow
397 assert(mFeeFeedExp
!= NULL
);
398 frtn
= feeFEEDExpEncryptBlock(mFeeFeedExp
,
399 (unsigned char *)plainText
,
400 (unsigned int)plainTextLen
,
401 (unsigned char *)cipherText
,
405 throwCryptKit(frtn
, "feeFEEDExpEncryptBlock");
407 if(actMoved
> cipherTextLen
) {
408 /* Overflow already occurred! */
409 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
411 cipherTextLen
= actMoved
;
414 void CryptKit::FEEDExpContext::decryptBlock(
415 const void *cipherText
, // length implied (one cipher block)
416 size_t cipherTextLen
,
418 size_t &plainTextLen
, // in/out, throws on overflow
424 assert(mFeeFeedExp
!= NULL
);
425 frtn
= feeFEEDExpDecryptBlock(mFeeFeedExp
,
426 (unsigned char *)cipherText
,
427 (unsigned int)inBlockSize(),
428 (unsigned char *)plainText
,
432 throwCryptKit(frtn
, "feeFEEDExpDecryptBlock");
434 if(actMoved
> plainTextLen
) {
435 /* Overflow already occurred! */
436 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
438 plainTextLen
= actMoved
;
441 /* convert uint32 to big-endian 4 bytes */
442 static void int32ToBytes(
446 for(int dex
=3; dex
>=0; dex
--) {
453 * X9.63 key derivation with optional SharedInfo passed as
454 * context attribute CSSM_ATTRIBUTE_SALT.
456 static feeReturn
ecdhKdf(
457 const Context
&context
,
458 const unsigned char *Z
, /* shared secret, i.e., output of ECDH */
460 CSSM_DATA
*K
) /* output RETURNED in K->Data, length K->Length bytes */
462 /* SharedInfo via salt, from context, optional */
463 const unsigned char *sharedInfo
= NULL
;
464 CSSM_SIZE sharedInfoLen
= 0;
466 CssmData
*salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
);
468 sharedInfo
= (const unsigned char *)salt
->Data
;
469 sharedInfoLen
= salt
->Length
;
472 unsigned char *outp
= K
->Data
;
473 CSSM_SIZE bytesToGo
= K
->Length
;
475 uint32_t counter
= 1;
476 uint8 counterBytes
[4];
477 unsigned char digOut
[CC_SHA1_DIGEST_LENGTH
];
480 /* K[i] = Hash(Z || Counter || SharedInfo) */
482 CC_SHA1_Update(&sha1
, Z
, ZLen
);
483 int32ToBytes(counter
, counterBytes
);
484 CC_SHA1_Update(&sha1
, counterBytes
, 4);
486 CC_SHA1_Update(&sha1
, sharedInfo
, (CC_LONG
)sharedInfoLen
);
488 CC_SHA1_Final(digOut
, &sha1
);
490 /* digest --> output */
491 unsigned toMove
= CC_SHA1_DIGEST_LENGTH
;
492 if(toMove
> bytesToGo
) {
493 toMove
= (unsigned int)bytesToGo
;
495 memmove(outp
, digOut
, toMove
);
507 * Elliptic curve Diffie-Hellman key exchange. The public key is
508 * specified in one of two ways - a raw X9.62 format public key
509 * string in Param, or a CSSM_KEY in the Context.
510 * Requested size, in keyData->Length, must be the same size as
511 * the keys' modulus. Data is returned in keyData->Data, which is
512 * allocated by the caller.
513 * Optionally performs X9.63 key derivation if algId ==
514 * CSSM_ALGID_ECDH_X963_KDF, with the optional SharedInfo passed
515 * as optional context attribute CSSM_ATTRIBUTE_SALT.
517 void CryptKit::DeriveKey_ECDH (
518 const Context
&context
,
519 CSSM_ALGORITHMS algId
,
520 const CssmData
&Param
, // other's public key. may be empty
521 CSSM_DATA
*keyData
, // mallocd by caller
522 // we fill in keyData->Length bytes
523 AppleCSPSession
&session
)
528 /* private ECDH key from context - required */
529 feePubKey privKey
= contextToFeeKey(context
, session
, CSSM_ATTRIBUTE_KEY
,
530 CSSM_KEYCLASS_PRIVATE_KEY
, CSSM_KEYUSE_DERIVE
, mallocdPrivKey
);
531 if(privKey
== NULL
) {
532 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
534 privSize
= (feePubKeyBitsize(privKey
) + 7) / 8;
535 if((algId
== CSSM_ALGID_ECDH
) & (privSize
!= keyData
->Length
)) {
536 /* exact match required here */
537 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
541 * Public key ("their" key) can come from two places:
542 * -- in the context as a CSSM_ATTRIBUTE_PUBLIC_KEY. This is how
543 * public keys in X509 format must be used in this function.
544 * -- in the incoming Param, the raw unformatted (ANSI X9.62) form
546 bool mallocdPubKey
= false;
547 feePubKey pubKey
= NULL
;
548 if(Param
.Data
== NULL
) {
549 /* this throws if no key present */
550 pubKey
= contextToFeeKey(context
, session
, CSSM_ATTRIBUTE_PUBLIC_KEY
,
551 CSSM_KEYCLASS_PUBLIC_KEY
, CSSM_KEYUSE_DERIVE
, mallocdPubKey
);
553 if((pubKey
== NULL
) && (Param
.Data
== NULL
)) {
554 errorLog0("DeriveKey_ECDH: no pub_key\n");
555 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
557 unsigned char *output
= NULL
;
558 unsigned outputLen
= 0;
559 feeReturn frtn
= feePubKeyECDH(privKey
, pubKey
,
560 (const unsigned char *)Param
.Data
, (unsigned)Param
.Length
,
561 &output
, &outputLen
);
566 case CSSM_ALGID_ECDH
:
568 * Raw ECDH - requested length must match the generated size
569 * exactly. If so, return the result unmodified.
571 if(outputLen
!= keyData
->Length
) {
572 errorLog0("DeriveKey_ECDH: length mismatch\n");
576 memmove(keyData
->Data
, output
, outputLen
);
578 case CSSM_ALGID_ECDH_X963_KDF
:
579 /* Further processing... */
580 frtn
= ecdhKdf(context
, output
, outputLen
, keyData
);
583 /* shouldn't be here */
590 feePubKeyFree(privKey
);
593 feePubKeyFree(pubKey
);
599 throwCryptKit(frtn
, NULL
);
603 #endif /* CRYPTKIT_CSP_ENABLE */