/* * Copyright (c) 2000-2001,2011-2012,2014 Apple 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. */ /* * RSA_asymmetric.cpp - CSPContext for RSA asymmetric encryption */ #include "RSA_asymmetric.h" #include "RSA_DSA_utils.h" #include #include #define rsaCryptDebug(args...) secdebug("rsaCrypt", ## args) #define rbprintf(args...) secdebug("rsaBuf", ## args) static ModuleNexus gMutex; RSA_CryptContext::~RSA_CryptContext() { StLock _(gMutex()); if(mAllocdRsaKey) { assert(mRsaKey != NULL); RSA_free(mRsaKey); mRsaKey = NULL; mAllocdRsaKey = false; } } /* called by CSPFullPluginSession */ void RSA_CryptContext::init(const Context &context, bool encoding /*= true*/) { StLock _(gMutex()); if(mInitFlag && !opStarted()) { /* reusing - e.g. query followed by encrypt */ return; } /* optional mode to use alternate key class (e.g., decrypt with public key) */ CSSM_KEYCLASS keyClass; switch (context.getInt(CSSM_ATTRIBUTE_MODE)) { case CSSM_ALGMODE_PUBLIC_KEY: keyClass = CSSM_KEYCLASS_PUBLIC_KEY; break; case CSSM_ALGMODE_PRIVATE_KEY: keyClass = CSSM_KEYCLASS_PRIVATE_KEY; break; case CSSM_ALGMODE_NONE: /* default, not present in context: infer from op type */ keyClass = encoding ? CSSM_KEYCLASS_PUBLIC_KEY : CSSM_KEYCLASS_PRIVATE_KEY; break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE); } /* fetch key from context */ if(mRsaKey == NULL) { assert(!opStarted()); CSSM_DATA label = {0, NULL}; mRsaKey = contextToRsaKey(context, session(), keyClass, encoding ? CSSM_KEYUSE_ENCRYPT : CSSM_KEYUSE_DECRYPT, mAllocdRsaKey, label); if(label.Data) { mLabel.copy(label); mOaep = true; free(label.Data); } } else { assert(opStarted()); } unsigned cipherBlockSize = RSA_size(mRsaKey); unsigned plainBlockSize; /* padding - not present means value zero, CSSM_PADDING_NONE */ uint32 padding = context.getInt(CSSM_ATTRIBUTE_PADDING); switch(padding) { case CSSM_PADDING_NONE: mPadding = RSA_NO_PADDING; plainBlockSize = cipherBlockSize; break; case CSSM_PADDING_PKCS1: mPadding = RSA_PKCS1_PADDING; plainBlockSize = cipherBlockSize - 11; break; case CSSM_PADDING_APPLE_SSLv2: rsaCryptDebug("RSA_CryptContext::init using CSSM_PADDING_APPLE_SSLv2"); mPadding = RSA_SSLV23_PADDING; plainBlockSize = cipherBlockSize - 11; break; default: rsaCryptDebug("RSA_CryptContext::init bad padding (0x%x)", (unsigned)padding); CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); } /* optional blinding attribute */ uint32 blinding = context.getInt(CSSM_ATTRIBUTE_RSA_BLINDING); if(blinding) { if(RSA_blinding_on(mRsaKey, NULL) <= 0) { /* actually no legit failures */ CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); } } else { RSA_blinding_off(mRsaKey); } /* finally, have BlockCryptor set up its stuff. */ setup(encoding ? plainBlockSize : cipherBlockSize, // blockSizeIn encoding ? cipherBlockSize : plainBlockSize, // blockSizeOut false, // pkcs5Pad false, // needsFinal BCM_ECB, NULL); // IV mInitFlag = true; } /* called by BlockCryptor */ void RSA_CryptContext::encryptBlock( const void *plainText, // length implied (one block) size_t plainTextLen, void *cipherText, size_t &cipherTextLen, // in/out, throws on overflow bool final) { StLock _(gMutex()); int irtn; /* FIXME do OAEP encoding here */ if(mRsaKey->d == NULL) { irtn = RSA_public_encrypt((int)plainTextLen, (unsigned char *)plainText, (unsigned char *)cipherText, mRsaKey, mPadding); } else { irtn = RSA_private_encrypt((int)plainTextLen, (unsigned char *)plainText, (unsigned char *)cipherText, mRsaKey, mPadding); } if(irtn < 0) { throwRsaDsa("RSA_public_encrypt"); } else if((unsigned)irtn > cipherTextLen) { rsaCryptDebug("RSA_public_encrypt overflow"); CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } cipherTextLen = (size_t)irtn; } void RSA_CryptContext::decryptBlock( const void *cipherText, // length implied (one cipher block) size_t cipherTextLen, void *plainText, size_t &plainTextLen, // in/out, throws on overflow bool final) { StLock _(gMutex()); int irtn; rsaCryptDebug("decryptBlock padding %d", mPadding); /* FIXME do OAEP encoding here */ if(mRsaKey->d == NULL) { irtn = RSA_public_decrypt((int)inBlockSize(), (unsigned char *)cipherText, (unsigned char *)plainText, mRsaKey, mPadding); } else { irtn = RSA_private_decrypt((int)inBlockSize(), (unsigned char *)cipherText, (unsigned char *)plainText, mRsaKey, mPadding); } if(irtn < 0) { rsaCryptDebug("decryptBlock err"); throwRsaDsa("RSA_private_decrypt"); } else if((unsigned)irtn > plainTextLen) { rsaCryptDebug("RSA_private_decrypt overflow"); CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); } plainTextLen = (size_t)irtn; } size_t RSA_CryptContext::outputSize( bool final, // ignored size_t inSize /*= 0*/) // output for given input size { StLock _(gMutex()); size_t rawBytes = inSize + inBufSize(); size_t rawBlocks = (rawBytes + inBlockSize() - 1) / inBlockSize(); rbprintf("--- RSA_CryptContext::outputSize inSize 0x%lux outSize 0x%lux mInBufSize 0x%lux", (unsigned long)inSize, (unsigned long)(rawBlocks * outBlockSize()), (unsigned long)inBufSize()); return rawBlocks * outBlockSize(); }