2 * Copyright (c) 2000-2001 Apple Computer, 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
22 * Created March 8 2001 by dmitch.
25 #ifdef CRYPTKIT_CSP_ENABLE
27 #include "FEEAsymmetricContext.h"
28 #include "FEECSPUtils.h"
29 #include <security_cryptkit/falloc.h>
30 #include <CommonCrypto/CommonDigest.h>
32 /* validate context for FEED and FEEDExp - no unexpected attributes allowed */
33 static void validateFeedContext(
34 const Context
&context
)
36 /* Note we cannot distinguish between zero and "not there" */
37 uint32 blockSize
= context
.getInt(CSSM_ATTRIBUTE_BLOCK_SIZE
);
39 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_BLOCK_SIZE
);
41 CSSM_ENCRYPT_MODE cssmMode
= context
.getInt(CSSM_ATTRIBUTE_MODE
);
43 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
46 /* we allow this for CMS wrapping */
47 CssmData
*iv
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
);
49 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
52 CSSM_PADDING padding
= context
.getInt(CSSM_ATTRIBUTE_PADDING
);
54 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
59 *** FEED - 1:1 FEED - encrypt n bytes of plaintext, get (roughly) n bytes
60 *** of ciphertext. Ciphertext is smaller than with FEED, but this is slower.
62 CryptKit::FEEDContext::~FEEDContext()
65 feeFEEDFree(mFeeFeed
);
68 if(mPrivKey
&& mAllocdPrivKey
) {
69 feePubKeyFree(mPrivKey
);
71 if(mPubKey
&& mAllocdPubKey
) {
72 feePubKeyFree(mPubKey
);
79 // called by CSPFullPluginSession; reusable
80 void CryptKit::FEEDContext::init(
81 const Context
&context
,
84 if(mInitFlag
&& !opStarted()) {
85 /* reusing - e.g. query followed by encrypt */
90 * Fetch FEE keys from context. This is an unusual algorithm - it requires
91 * two keys, one public and one private. The public key MUST be stored in
92 * the context with attribute type CSSM_ATTRIBUTE_PUBLIC_KEY, and the private
93 * key with CSSM_ATTRIBUTE_KEY.
95 * For now, we require CSSM_KEYUSE_ANY for FEE keys used for this algorithm.
96 * Otherwise we'd have to allow both KEYUSE_ENCRYPT and KEYUSE_DECRYPT for
97 * both keys, and that would require some algorithm-specific hack in
98 * cspValidateKeyUsageBits() which I really don't want to do.
100 if(mPrivKey
== NULL
) {
101 assert(!opStarted());
102 mPrivKey
= contextToFeeKey(context
,
105 CSSM_KEYCLASS_PRIVATE_KEY
,
112 if(mPubKey
== NULL
) {
113 assert(!opStarted());
114 mPubKey
= contextToFeeKey(context
,
116 CSSM_ATTRIBUTE_PUBLIC_KEY
,
117 CSSM_KEYCLASS_PUBLIC_KEY
,
125 /* validate context - no other attributes allowed */
126 validateFeedContext(context
);
128 if(mFeeFeed
!= NULL
) {
131 feeFEEDFree(mFeeFeed
);
135 /* OK, looks good. Cook up a feeFEED object. */
136 mFeeFeed
= feeFEEDNewWithPubKey(mPrivKey
,
141 if(mFeeFeed
== NULL
) {
142 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
145 /* finally, have BlockCryptor set up its stuff. */
146 unsigned plainBlockSize
= feeFEEDPlainBlockSize(mFeeFeed
);
147 unsigned cipherBlockSize
= feeFEEDCipherBlockSize(mFeeFeed
);
148 setup(encoding
? plainBlockSize
: cipherBlockSize
, // blockSizeIn
149 encoding
? cipherBlockSize
: plainBlockSize
, // blockSizeOut
157 // called by BlockCryptor
158 void CryptKit::FEEDContext::encryptBlock(
159 const void *plainText
, // length implied (one block)
162 size_t &cipherTextLen
, // in/out, throws on overflow
168 assert(mFeeFeed
!= NULL
);
169 frtn
= feeFEEDEncryptBlock(mFeeFeed
,
170 (unsigned char *)plainText
,
171 (unsigned int)plainTextLen
,
172 (unsigned char *)cipherText
,
176 throwCryptKit(frtn
, "feeFEEDEncryptBlock");
178 if(actMoved
> cipherTextLen
) {
179 /* Overflow already occurred! */
180 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
182 cipherTextLen
= actMoved
;
185 void CryptKit::FEEDContext::decryptBlock(
186 const void *cipherText
, // length implied (one cipher block)
187 size_t cipherTextLen
,
189 size_t &plainTextLen
, // in/out, throws on overflow
195 assert(mFeeFeed
!= NULL
);
196 frtn
= feeFEEDDecryptBlock(mFeeFeed
,
197 (unsigned char *)cipherText
,
198 (unsigned int)inBlockSize(),
199 (unsigned char *)plainText
,
203 throwCryptKit(frtn
, "feeFEEDDecryptBlock");
205 if(actMoved
> plainTextLen
) {
206 /* Overflow already occurred! */
207 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
209 plainTextLen
= actMoved
;
213 * Additional query size support, necessary because we don't conform to
214 * BlockCryptor's standard one-to-one block scheme
217 #define BUFFER_DEBUG 0
219 #define bprintf(s) printf s
224 size_t CryptKit::FEEDContext::inputSize(
225 size_t outSize
) // input for given output size
228 * We've been assured that this is NOT called for the final() op...
232 inSize
= feeFEEDPlainTextSize(mFeeFeed
, (unsigned int)outSize
, 0);
235 inSize
= feeFEEDCipherTextSize(mFeeFeed
, (unsigned int)outSize
, 0);
238 /* account for possible pending buffered input */
239 if(inSize
>= inBufSize()) {
240 inSize
-= inBufSize();
243 /* round up to next block size, then lop off one...anything from
244 * blockSize*n to (blockSize*n)-1 has same effect */
245 unsigned inBlocks
= (unsigned int)((inSize
+ inBlockSize()) / inBlockSize());
246 inSize
= (unsigned int)(inBlocks
* inBlockSize()) - 1;
247 bprintf(("--- FEEDContext::inputSize inSize 0x%x outSize 0x%x\n",
252 size_t CryptKit::FEEDContext::outputSize(
254 size_t inSize
) // output for given input size
258 rtn
= feeFEEDCipherTextSize(mFeeFeed
, (unsigned int)(inSize
+ inBufSize()), final
? 1 : 0);
261 rtn
= feeFEEDPlainTextSize(mFeeFeed
, (unsigned int)(inSize
+ inBufSize()), final
? 1 : 0);
263 bprintf(("--- FEEDContext::outputSize inSize 0x%x outSize 0x%x final %d\n",
264 inSize
, rtn
, final
));
268 void CryptKit::FEEDContext::minimumProgress(
270 size_t &out
) // minimum progress chunks
274 * -- in := one block plaintext
275 * -- out := current cipher size for one block plaintext
278 out
= feeFEEDCipherBufSize(mFeeFeed
, 0);
282 * -- in := current cipher size for one block plaintext
283 * -- out := one block plaintext
285 in
= feeFEEDCipherBufSize(mFeeFeed
, 0);
286 out
= outBlockSize();
290 * Either case - input adjusted for pending. Note inBufSize can be up to one
291 * input block size, leaving the temp result zero here....
293 assert(in
>= inBufSize());
296 /* if it is zero, bump it up so caller can make something happen */
300 bprintf(("--- FEEDContext::minProgres inSize 0x%x outSize 0x%x\n",
305 *** FEEDExp - 2:1 FEED - encrypt n bytes of plaintext, get (roughly) 2n bytes
306 *** of ciphertext. Ciphertext is larger than with FEED, but this is faster.
308 CryptKit::FEEDExpContext::~FEEDExpContext()
311 feeFEEDExpFree(mFeeFeedExp
);
314 if(mFeeKey
&& mAllocdFeeKey
) {
315 feePubKeyFree(mFeeKey
);
321 // called by CSPFullPluginSession; reusable
322 void CryptKit::FEEDExpContext::init(
323 const Context
&context
,
326 if(mInitFlag
&& !opStarted()) {
327 /* reusing - e.g. query followed by encrypt */
331 /* fetch FEE key from context */
332 CSSM_KEYCLASS keyClass
;
336 /* encrypting to public key */
337 keyClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
338 keyUse
= CSSM_KEYUSE_ENCRYPT
;
341 /* decrypting with private key */
342 keyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
343 keyUse
= CSSM_KEYUSE_DECRYPT
;
345 if(mFeeKey
== NULL
) {
346 assert(!opStarted());
347 mFeeKey
= contextToFeeKey(context
,
358 /* validate context - no other attributes allowed */
359 validateFeedContext(context
);
361 /* OK, looks good. Cook up a feeFEEDExp object. */
362 if(mFeeFeedExp
!= NULL
) {
365 feeFEEDExpFree(mFeeFeedExp
);
368 mFeeFeedExp
= feeFEEDExpNewWithPubKey(mFeeKey
,
371 if(mFeeFeedExp
== NULL
) {
372 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
375 /* finally, have BlockCryptor set up its stuff. */
376 unsigned plainBlockSize
= feeFEEDExpPlainBlockSize(mFeeFeedExp
);
377 unsigned cipherBlockSize
= feeFEEDExpCipherBlockSize(mFeeFeedExp
);
378 setup(encoding
? plainBlockSize
: cipherBlockSize
, // blockSizeIn
379 encoding
? cipherBlockSize
: plainBlockSize
, // blockSizeOut
387 // called by BlockCryptor
388 void CryptKit::FEEDExpContext::encryptBlock(
389 const void *plainText
, // length implied (one block)
392 size_t &cipherTextLen
, // in/out, throws on overflow
398 assert(mFeeFeedExp
!= NULL
);
399 frtn
= feeFEEDExpEncryptBlock(mFeeFeedExp
,
400 (unsigned char *)plainText
,
401 (unsigned int)plainTextLen
,
402 (unsigned char *)cipherText
,
406 throwCryptKit(frtn
, "feeFEEDExpEncryptBlock");
408 if(actMoved
> cipherTextLen
) {
409 /* Overflow already occurred! */
410 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
412 cipherTextLen
= actMoved
;
415 void CryptKit::FEEDExpContext::decryptBlock(
416 const void *cipherText
, // length implied (one cipher block)
417 size_t cipherTextLen
,
419 size_t &plainTextLen
, // in/out, throws on overflow
425 assert(mFeeFeedExp
!= NULL
);
426 frtn
= feeFEEDExpDecryptBlock(mFeeFeedExp
,
427 (unsigned char *)cipherText
,
428 (unsigned int)inBlockSize(),
429 (unsigned char *)plainText
,
433 throwCryptKit(frtn
, "feeFEEDExpDecryptBlock");
435 if(actMoved
> plainTextLen
) {
436 /* Overflow already occurred! */
437 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
439 plainTextLen
= actMoved
;
442 /* convert uint32 to big-endian 4 bytes */
443 static void int32ToBytes(
447 for(int dex
=3; dex
>=0; dex
--) {
454 * X9.63 key derivation with optional SharedInfo passed as
455 * context attribute CSSM_ATTRIBUTE_SALT.
457 static feeReturn
ecdhKdf(
458 const Context
&context
,
459 const unsigned char *Z
, /* shared secret, i.e., output of ECDH */
461 CSSM_DATA
*K
) /* output RETURNED in K->Data, length K->Length bytes */
463 /* SharedInfo via salt, from context, optional */
464 const unsigned char *sharedInfo
= NULL
;
465 CSSM_SIZE sharedInfoLen
= 0;
467 CssmData
*salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
);
469 sharedInfo
= (const unsigned char *)salt
->Data
;
470 sharedInfoLen
= salt
->Length
;
473 unsigned char *outp
= K
->Data
;
474 CSSM_SIZE bytesToGo
= K
->Length
;
476 uint32_t counter
= 1;
477 uint8 counterBytes
[4];
478 unsigned char digOut
[CC_SHA1_DIGEST_LENGTH
];
481 /* K[i] = Hash(Z || Counter || SharedInfo) */
483 CC_SHA1_Update(&sha1
, Z
, ZLen
);
484 int32ToBytes(counter
, counterBytes
);
485 CC_SHA1_Update(&sha1
, counterBytes
, 4);
487 CC_SHA1_Update(&sha1
, sharedInfo
, (CC_LONG
)sharedInfoLen
);
489 CC_SHA1_Final(digOut
, &sha1
);
491 /* digest --> output */
492 unsigned toMove
= CC_SHA1_DIGEST_LENGTH
;
493 if(toMove
> bytesToGo
) {
494 toMove
= (unsigned int)bytesToGo
;
496 memmove(outp
, digOut
, toMove
);
508 * Elliptic curve Diffie-Hellman key exchange. The public key is
509 * specified in one of two ways - a raw X9.62 format public key
510 * string in Param, or a CSSM_KEY in the Context.
511 * Requested size, in keyData->Length, must be the same size as
512 * the keys' modulus. Data is returned in keyData->Data, which is
513 * allocated by the caller.
514 * Optionally performs X9.63 key derivation if algId ==
515 * CSSM_ALGID_ECDH_X963_KDF, with the optional SharedInfo passed
516 * as optional context attribute CSSM_ATTRIBUTE_SALT.
518 void CryptKit::DeriveKey_ECDH (
519 const Context
&context
,
520 CSSM_ALGORITHMS algId
,
521 const CssmData
&Param
, // other's public key. may be empty
522 CSSM_DATA
*keyData
, // mallocd by caller
523 // we fill in keyData->Length bytes
524 AppleCSPSession
&session
)
529 /* private ECDH key from context - required */
530 feePubKey privKey
= contextToFeeKey(context
, session
, CSSM_ATTRIBUTE_KEY
,
531 CSSM_KEYCLASS_PRIVATE_KEY
, CSSM_KEYUSE_DERIVE
, mallocdPrivKey
);
532 if(privKey
== NULL
) {
533 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
535 privSize
= (feePubKeyBitsize(privKey
) + 7) / 8;
536 if((algId
== CSSM_ALGID_ECDH
) & (privSize
!= keyData
->Length
)) {
537 /* exact match required here */
538 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR
);
542 * Public key ("their" key) can come from two places:
543 * -- in the context as a CSSM_ATTRIBUTE_PUBLIC_KEY. This is how
544 * public keys in X509 format must be used in this function.
545 * -- in the incoming Param, the raw unformatted (ANSI X9.62) form
547 bool mallocdPubKey
= false;
548 feePubKey pubKey
= NULL
;
549 if(Param
.Data
== NULL
) {
550 /* this throws if no key present */
551 pubKey
= contextToFeeKey(context
, session
, CSSM_ATTRIBUTE_PUBLIC_KEY
,
552 CSSM_KEYCLASS_PUBLIC_KEY
, CSSM_KEYUSE_DERIVE
, mallocdPubKey
);
554 if((pubKey
== NULL
) && (Param
.Data
== NULL
)) {
555 errorLog0("DeriveKey_ECDH: no pub_key\n");
556 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
558 unsigned char *output
= NULL
;
559 unsigned outputLen
= 0;
560 feeReturn frtn
= feePubKeyECDH(privKey
, pubKey
,
561 (const unsigned char *)Param
.Data
, (unsigned)Param
.Length
,
562 &output
, &outputLen
);
567 case CSSM_ALGID_ECDH
:
569 * Raw ECDH - requested length must match the generated size
570 * exactly. If so, return the result unmodified.
572 if(outputLen
!= keyData
->Length
) {
573 errorLog0("DeriveKey_ECDH: length mismatch\n");
577 memmove(keyData
->Data
, output
, outputLen
);
579 case CSSM_ALGID_ECDH_X963_KDF
:
580 /* Further processing... */
581 frtn
= ecdhKdf(context
, output
, outputLen
, keyData
);
584 /* shouldn't be here */
591 feePubKeyFree(privKey
);
594 feePubKeyFree(pubKey
);
600 throwCryptKit(frtn
, NULL
);
604 #endif /* CRYPTKIT_CSP_ENABLE */