/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* * FEEAsymmetricContext.cpp - CSPContexts for FEE asymmetric encryption * * Created March 8 2001 by dmitch. */ #ifdef CRYPTKIT_CSP_ENABLE #include "FEEAsymmetricContext.h" #include "FEECSPUtils.h" #include /* validate context for FEED and FEEDExp - no unexpected attributes allowed */ static void validateFeedContext( const Context &context) { /* Note we cannot distinguish between zero and "not there" */ uint32 blockSize = context.getInt(CSSM_ATTRIBUTE_BLOCK_SIZE); if(blockSize != 0) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_BLOCK_SIZE); } CSSM_ENCRYPT_MODE cssmMode = context.getInt(CSSM_ATTRIBUTE_MODE); if(cssmMode != 0) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE); } #if 0 /* we allow this for CMS wrapping */ CssmData *iv = context.get(CSSM_ATTRIBUTE_INIT_VECTOR); if(iv != NULL) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR); } #endif CSSM_PADDING padding = context.getInt(CSSM_ATTRIBUTE_PADDING); if(padding != 0) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); } } /*** *** FEED - 1:1 FEED - encrypt n bytes of plaintext, get (roughly) n bytes *** of ciphertext. Ciphertext is smaller than with FEED, but this is slower. ***/ CryptKit::FEEDContext::~FEEDContext() { if(mFeeFeed) { feeFEEDFree(mFeeFeed); mFeeFeed = NULL; } if(mPrivKey && mAllocdPrivKey) { feePubKeyFree(mPrivKey); } if(mPubKey && mAllocdPubKey) { feePubKeyFree(mPubKey); } mPrivKey = NULL; mPubKey = NULL; mInitFlag = false; } // called by CSPFullPluginSession; reusable void CryptKit::FEEDContext::init( const Context &context, bool encoding) { if(mInitFlag && !opStarted()) { /* reusing - e.g. query followed by encrypt */ return; } /* * Fetch FEE keys from context. This is an unusual algorithm - it requires * two keys, one public and one private. The public key MUST be stored in * the context with attribute type CSSM_ATTRIBUTE_PUBLIC_KEY, and the private * key with CSSM_ATTRIBUTE_KEY. * * For now, we require CSSM_KEYUSE_ANY for FEE keys used for this algorithm. * Otherwise we'd have to allow both KEYUSE_ENCRYPT and KEYUSE_DECRYPT for * both keys, and that would require some algorithm-specific hack in * cspValidateKeyUsageBits() which I really don't want to do. */ if(mPrivKey == NULL) { assert(!opStarted()); mPrivKey = contextToFeeKey(context, session(), CSSM_ATTRIBUTE_KEY, CSSM_KEYCLASS_PRIVATE_KEY, CSSM_KEYUSE_ANY, mAllocdPrivKey); } else { assert(opStarted()); } if(mPubKey == NULL) { assert(!opStarted()); mPubKey = contextToFeeKey(context, session(), CSSM_ATTRIBUTE_PUBLIC_KEY, CSSM_KEYCLASS_PUBLIC_KEY, CSSM_KEYUSE_ANY, mAllocdPubKey); } else { assert(opStarted()); } /* validate context - no other attributes allowed */ validateFeedContext(context); if(mFeeFeed != NULL) { /* not reusable */ assert(opStarted()); feeFEEDFree(mFeeFeed); mFeeFeed = NULL; } /* OK, looks good. Cook up a feeFEED object. */ mFeeFeed = feeFEEDNewWithPubKey(mPrivKey, mPubKey, encoding ? 1 : 0, feeRandCallback, &session()); if(mFeeFeed == NULL) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); } /* finally, have BlockCryptor set up its stuff. */ unsigned plainBlockSize = feeFEEDPlainBlockSize(mFeeFeed); unsigned cipherBlockSize = feeFEEDCipherBlockSize(mFeeFeed); setup(encoding ? plainBlockSize : cipherBlockSize, // blockSizeIn encoding ? cipherBlockSize : plainBlockSize, // blockSizeOut false, // pkcsPad true, // needsFinal BCM_ECB, NULL); // IV mInitFlag = true; } // called by BlockCryptor void CryptKit::FEEDContext::encryptBlock( const void *plainText, // length implied (one block) size_t plainTextLen, void *cipherText, size_t &cipherTextLen, // in/out, throws on overflow bool final) { feeReturn frtn; unsigned actMoved; assert(mFeeFeed != NULL); frtn = feeFEEDEncryptBlock(mFeeFeed, (unsigned char *)plainText, plainTextLen, (unsigned char *)cipherText, &actMoved, final ? 1 : 0); if(frtn) { throwCryptKit(frtn, "feeFEEDEncryptBlock"); } if(actMoved > cipherTextLen) { /* Overflow already occurred! */ CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } cipherTextLen = actMoved; } void CryptKit::FEEDContext::decryptBlock( const void *cipherText, // length implied (one cipher block) void *plainText, size_t &plainTextLen, // in/out, throws on overflow bool final) { feeReturn frtn; unsigned actMoved; assert(mFeeFeed != NULL); frtn = feeFEEDDecryptBlock(mFeeFeed, (unsigned char *)cipherText, inBlockSize(), (unsigned char *)plainText, &actMoved, final ? 1 : 0); if(frtn) { throwCryptKit(frtn, "feeFEEDDecryptBlock"); } if(actMoved > plainTextLen) { /* Overflow already occurred! */ CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } plainTextLen = actMoved; } /* * Additional query size support, necessary because we don't conform to * BlockCryptor's standard one-to-one block scheme */ #define BUFFER_DEBUG 0 #if BUFFER_DEBUG #define bprintf(s) printf s #else #define bprintf(s) #endif size_t CryptKit::FEEDContext::inputSize( size_t outSize) // input for given output size { /* * We've been assured that this is NOT called for the final() op... */ unsigned inSize; if(encoding()) { inSize = feeFEEDPlainTextSize(mFeeFeed, outSize, 0); } else { inSize = feeFEEDCipherTextSize(mFeeFeed, outSize, 0); } /* account for possible pending buffered input */ if(inSize >= inBufSize()) { inSize -= inBufSize(); } /* round up to next block size, then lop off one...anything from * blockSize*n to (blockSize*n)-1 has same effect */ unsigned inBlocks = ((inSize + inBlockSize()) / inBlockSize()); inSize = (inBlocks * inBlockSize()) - 1; bprintf(("--- FEEDContext::inputSize inSize 0x%x outSize 0x%x\n", inSize, outSize)); return inSize; } size_t CryptKit::FEEDContext::outputSize( bool final, size_t inSize) // output for given input size { size_t rtn; if(encoding()) { rtn = feeFEEDCipherTextSize(mFeeFeed, inSize + inBufSize(), final ? 1 : 0); } else { rtn = feeFEEDPlainTextSize(mFeeFeed, inSize + inBufSize(), final ? 1 : 0); } bprintf(("--- FEEDContext::outputSize inSize 0x%x outSize 0x%x final %d\n", inSize, rtn, final)); return rtn; } void CryptKit::FEEDContext::minimumProgress( size_t &in, size_t &out) // minimum progress chunks { if(encoding()) { /* * -- in := one block plaintext * -- out := current cipher size for one block plaintext */ in = inBlockSize(); out = feeFEEDCipherBufSize(mFeeFeed, 0); } else { /* * -- in := current cipher size for one block plaintext * -- out := one block plaintext */ in = feeFEEDCipherBufSize(mFeeFeed, 0); out = outBlockSize(); } /* * Either case - input adjusted for pending. Note inBufSize can be up to one * input block size, leaving the temp result zero here.... */ assert(in >= inBufSize()); in -= inBufSize(); /* if it is zero, bump it up so caller can make something happen */ if(in == 0) { in++; } bprintf(("--- FEEDContext::minProgres inSize 0x%x outSize 0x%x\n", in, out)); } /*** *** FEEDExp - 2:1 FEED - encrypt n bytes of plaintext, get (roughly) 2n bytes *** of ciphertext. Ciphertext is larger than with FEED, but this is faster. ***/ CryptKit::FEEDExpContext::~FEEDExpContext() { if(mFeeFeedExp) { feeFEEDExpFree(mFeeFeedExp); mFeeFeedExp = NULL; } if(mFeeKey && mAllocdFeeKey) { feePubKeyFree(mFeeKey); } mFeeKey = NULL; mInitFlag = false; } // called by CSPFullPluginSession; reusable void CryptKit::FEEDExpContext::init( const Context &context, bool encoding) { if(mInitFlag && !opStarted()) { /* reusing - e.g. query followed by encrypt */ return; } /* fetch FEE key from context */ CSSM_KEYCLASS keyClass; CSSM_KEYUSE keyUse; if(encoding) { /* encrypting to public key */ keyClass = CSSM_KEYCLASS_PUBLIC_KEY; keyUse = CSSM_KEYUSE_ENCRYPT; } else { /* decrypting with private key */ keyClass = CSSM_KEYCLASS_PRIVATE_KEY; keyUse = CSSM_KEYUSE_DECRYPT; } if(mFeeKey == NULL) { assert(!opStarted()); mFeeKey = contextToFeeKey(context, session(), CSSM_ATTRIBUTE_KEY, keyClass, keyUse, mAllocdFeeKey); } else { assert(opStarted()); } /* validate context - no other attributes allowed */ validateFeedContext(context); /* OK, looks good. Cook up a feeFEEDExp object. */ if(mFeeFeedExp != NULL) { /* not reusable */ assert(opStarted()); feeFEEDExpFree(mFeeFeedExp); mFeeFeedExp = NULL; } mFeeFeedExp = feeFEEDExpNewWithPubKey(mFeeKey, feeRandCallback, &session()); if(mFeeFeedExp == NULL) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); } /* finally, have BlockCryptor set up its stuff. */ unsigned plainBlockSize = feeFEEDExpPlainBlockSize(mFeeFeedExp); unsigned cipherBlockSize = feeFEEDExpCipherBlockSize(mFeeFeedExp); setup(encoding ? plainBlockSize : cipherBlockSize, // blockSizeIn encoding ? cipherBlockSize : plainBlockSize, // blockSizeOut false, // pkcs5Pad true, // needsFinal BCM_ECB, NULL); // IV mInitFlag = true; } // called by BlockCryptor void CryptKit::FEEDExpContext::encryptBlock( const void *plainText, // length implied (one block) size_t plainTextLen, void *cipherText, size_t &cipherTextLen, // in/out, throws on overflow bool final) { feeReturn frtn; unsigned actMoved; assert(mFeeFeedExp != NULL); frtn = feeFEEDExpEncryptBlock(mFeeFeedExp, (unsigned char *)plainText, plainTextLen, (unsigned char *)cipherText, &actMoved, final ? 1 : 0); if(frtn) { throwCryptKit(frtn, "feeFEEDExpEncryptBlock"); } if(actMoved > cipherTextLen) { /* Overflow already occurred! */ CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } cipherTextLen = actMoved; } void CryptKit::FEEDExpContext::decryptBlock( const void *cipherText, // length implied (one cipher block) void *plainText, size_t &plainTextLen, // in/out, throws on overflow bool final) { feeReturn frtn; unsigned actMoved; assert(mFeeFeedExp != NULL); frtn = feeFEEDExpDecryptBlock(mFeeFeedExp, (unsigned char *)cipherText, inBlockSize(), (unsigned char *)plainText, &actMoved, final ? 1 : 0); if(frtn) { throwCryptKit(frtn, "feeFEEDExpDecryptBlock"); } if(actMoved > plainTextLen) { /* Overflow already occurred! */ CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } plainTextLen = actMoved; } #endif /* CRYPTKIT_CSP_ENABLE */