X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_apple_csp/lib/bsafeContext.cpp diff --git a/libsecurity_apple_csp/lib/bsafeContext.cpp b/libsecurity_apple_csp/lib/bsafeContext.cpp new file mode 100644 index 00000000..a58f2afd --- /dev/null +++ b/libsecurity_apple_csp/lib/bsafeContext.cpp @@ -0,0 +1,448 @@ +/* + * 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. + */ + +#ifdef BSAFE_CSP_ENABLE + + +// +// bsafeContext.cpp - implementation of class BSafe::BSafeContext +// and some of its subclasses +// + +#include "bsafecspi.h" +#include "bsafePKCS1.h" +#include +#include +#include +#include "cspdebugging.h" + +#define DATA(cData) POINTER(cData.data()), cData.length() + +A_SURRENDER_CTX * const BSafe::BSafeContext::bsSurrender = NULL; + + +// +// Construct an algorithm object +// +BSafe::BSafeContext::BSafeContext(AppleCSPSession &session) + : AppleCSPContext(session) +{ + bsAlgorithm = NULL; + bsKey = NULL; + bsBinKey = NULL; + bsRandom = NULL; + initialized = false; + opStarted = false; +#ifdef SAFER + inUpdate = NULL; + inOutUpdate = NULL; + inFinal = NULL; + outFinal = NULL; + outFinalR = NULL; +#endif //SAFER +} + +BSafe::BSafeContext::~BSafeContext() +{ + reset(); +} + +void BSafe::BSafeContext::reset() +{ + B_DestroyAlgorithmObject(&bsAlgorithm); + B_DestroyAlgorithmObject(&bsRandom); + destroyBsKey(); +} + +/* + * Clear key state. We only destroy bsKey if we don't have a + * BinaryKey. + */ +void BSafe::BSafeContext::destroyBsKey() +{ + if(bsBinKey == NULL) { + B_DestroyKeyObject(&bsKey); + } + else { + // bsKey gets destroyed when bsBinKey gets deleted + bsBinKey = NULL; + bsKey = NULL; + } +} + +void BSafe::check(int status, bool isKeyOp) +{ + if(status == 0) { + return; + } + dprintf1("BSAFE Error %d\n", status); + switch (status) { + case BE_ALLOC: + throw std::bad_alloc(); + case BE_SIGNATURE: + CssmError::throwMe(CSSMERR_CSP_VERIFY_FAILED); + case BE_OUTPUT_LEN: + CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); + case BE_INPUT_LEN: + CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); + case BE_EXPONENT_EVEN: + case BE_EXPONENT_LEN: + case BE_EXPONENT_ONE: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + case BE_DATA: + case BE_INPUT_DATA: + if(isKeyOp) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + else { + CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); + } + case BE_MODULUS_LEN: + case BE_OVER_32K: + case BE_INPUT_COUNT: + case BE_CANCEL: + //@@@ later... + default: + //@@@ translate BSafe errors intelligently + CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); + } +} + + +void BSafe::BSafeContext::setAlgorithm( + B_INFO_TYPE bAlgType, + const void *info) +{ + B_DestroyAlgorithmObject(&bsAlgorithm); // clear any old BSafe algorithm + check(B_CreateAlgorithmObject(&bsAlgorithm)); + check(B_SetAlgorithmInfo(bsAlgorithm, bAlgType, POINTER(info))); +} + +/* safely create bsKey */ +void BSafe::BSafeContext::createBsKey() +{ + /* reset to initial key state - some keys can't be reused */ + destroyBsKey(); + check(B_CreateKeyObject(&bsKey)); +} + +/* form of *info varies per bKeyInfo */ +void BSafe::BSafeContext::setKeyAtom( + B_INFO_TYPE bKeyInfo, + const void *info) +{ + /* debug only */ + if((bKeyInfo == KI_RSAPublicBER) || (bKeyInfo == KI_RSAPublic)) { + printf("Aargh! Unhandled KI_RSAPublic!\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + assert(bKeyInfo != KI_RSAPublicBER); // handled elsewhere for now + assert(bKeyInfo != KI_RSAPublic); // handled elsewhere for now + createBsKey(); + check(B_SetKeyInfo(bsKey, bKeyInfo, POINTER(info)), true); +} + +// +// Set outSize for RSA keys. +// +void BSafe::BSafeContext::setRsaOutSize( + bool isPubKey) +{ + assert(bsKey != NULL); + + A_RSA_KEY *keyInfo; + if(isPubKey) { + keyInfo = getKey(bsKey, KI_RSAPublic); + } + else { + keyInfo = getKey(bsKey, KI_RSAPrivate); + } + mOutSize = (B_IntegerBits(keyInfo->modulus.data, + keyInfo->modulus.len) + 7) / 8; +} + +// +// Handle various forms of reference key. Symmetric +// keys are stored as SymmetricBinaryKey, with raw key bytes +// in keyData. Our asymmetric keys are stored as BSafeBinaryKeys, +// with an embedded ready-to-use B_KEY_OBJ. +// +void BSafe::BSafeContext::setRefKey(CssmKey &key) +{ + bool isPubKey = false; + + switch(key.keyClass()) { + case CSSM_KEYCLASS_SESSION_KEY: + { + assert(key.blobFormat() == + CSSM_KEYBLOB_REF_FORMAT_INTEGER); + + BinaryKey &binKey = session().lookupRefKey(key); + // fails if this is not a SymmetricBinaryKey + SymmetricBinaryKey *symBinKey = + dynamic_cast(&binKey); + if(symBinKey == NULL) { + errorLog0("BSafe::setRefKey(1): wrong BinaryKey subclass\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + setKeyFromCssmData(KI_Item, symBinKey->mKeyData); + return; + } + case CSSM_KEYCLASS_PUBLIC_KEY: + isPubKey = true; // and fall thru + case CSSM_KEYCLASS_PRIVATE_KEY: + { + BinaryKey &binKey = session().lookupRefKey(key); + destroyBsKey(); + bsBinKey = dynamic_cast(&binKey); + /* this cast failing means that this is some other + * kind of binary key */ + if(bsBinKey == NULL) { + errorLog0("BSafe::setRefKey(2): wrong BinaryKey subclass\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + assert(bsBinKey->bsKey() != NULL); + bsKey = bsBinKey->bsKey(); + if(key.algorithm() == CSSM_ALGID_RSA) { + setRsaOutSize(isPubKey); + } + return; + } + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } +} + +void BSafe::BSafeContext::setKeyFromContext( + const Context &context, + bool required) +{ + CssmKey &key = + context.get(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY); + + switch(key.blobType()) { + case CSSM_KEYBLOB_REFERENCE: + setRefKey(key); + return; + case CSSM_KEYBLOB_RAW: + break; // to main routine + default: + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + + bool isPubKey; + switch (key.keyClass()) { + case CSSM_KEYCLASS_SESSION_KEY: + /* symmetric, one format supported for all algs */ + switch (key.blobFormat()) { + case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING: + setKeyFromCssmKey(KI_Item, key); + return; + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); + } + case CSSM_KEYCLASS_PUBLIC_KEY: + isPubKey = true; + break; + case CSSM_KEYCLASS_PRIVATE_KEY: + isPubKey = false; + break; + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + + /* We know it's an asymmetric key; get some info */ + B_INFO_TYPE infoType; + CSSM_KEYBLOB_FORMAT expectedFormat; + + if(!bsafeAlgToInfoType(key.algorithm(), + isPubKey, + infoType, + expectedFormat)) { + /* unknown alg! */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + + /* + * Correct format? + * NOTE: if we end up supporting multiple incoming key formats, they'll + * have to be handled here. + */ + if(expectedFormat != key.blobFormat()) { + errorLog1("setKeyFromContext: invalid blob format (%d)\n", + (int)key.blobFormat()); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); + } + + /* + * Most formats can be handled directly by BSAFE. Handle the special cases + * requiring additional processing here. + */ + switch(expectedFormat) { + case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: + /* RSA public keys */ + createBsKey(); + BS_setKeyPkcs1(CssmData::overlay(key.KeyData), bsKey); + break; + default: + setKeyFromCssmKey(infoType, key); + break; + } + + /* + * One more thing - set mOutSize for RSA keys + */ + if(key.algorithm() == CSSM_ALGID_RSA) { + setRsaOutSize(isPubKey); + } +} + +#define BSAFE_RANDSIZE 32 + +void BSafe::BSafeContext::setRandom() +{ + if (bsRandom == NULL) { + check(B_CreateAlgorithmObject(&bsRandom)); + check(B_SetAlgorithmInfo(bsRandom, AI_X962Random_V0, NULL_PTR)); + check(B_RandomInit(bsRandom, chooser(), bsSurrender)); + uint8 seed[BSAFE_RANDSIZE]; + session().getRandomBytes(BSAFE_RANDSIZE, seed); + check(B_RandomUpdate(bsRandom, seed, sizeof(seed), bsSurrender)); + } +} + + +// +// Operational methods of BSafeContext +// +void BSafe::BSafeContext::init(const Context &, bool) +{ + // some algorithms don't need init(), because all is done in the context constructor +} + +// update for input-only block/stream algorithms +void BSafe::BSafeContext::update(const CssmData &data) +{ + opStarted = true; + check(inUpdate(bsAlgorithm, POINTER(data.data()), data.length(), bsSurrender)); +} + +// update for input/output block/stream algorithms +void BSafe::BSafeContext::update(void *inp, size_t &inSize, void *outp, size_t &outSize) +{ + unsigned int length; + opStarted = true; + check(inOutUpdate(bsAlgorithm, POINTER(outp), &length, outSize, + POINTER(inp), inSize, bsRandom, bsSurrender)); + // always eat all input (inSize unchanged) + outSize = length; + + // let the algorithm manager track I/O sizes, if needed + trackUpdate(inSize, outSize); +} + +// output-generating final call +void BSafe::BSafeContext::final(CssmData &out) +{ + unsigned int length; + if (outFinal) { + check(outFinal(bsAlgorithm, + POINTER(out.data()), + &length, + out.length(), + bsSurrender)); + } + else { + check(outFinalR(bsAlgorithm, + POINTER(out.data()), + &length, + out.length(), + bsRandom, + bsSurrender)); + } + out.length(length); + initialized = false; +} + +// verifying final call (takes additional input) +void BSafe::BSafeContext::final(const CssmData &in) +{ + int status; + + /* note sig verify errors can show up as lots of BSAFE statuses; + * munge them all into the appropriate error */ + if (inFinal) { + status = inFinal(bsAlgorithm, + POINTER(in.data()), + in.length(), + bsSurrender); + } + else { + status = inFinalR(bsAlgorithm, + POINTER(in.data()), + in.length(), + bsRandom, + bsSurrender); + } + if(status != 0) { + if((mType == CSSM_ALGCLASS_SIGNATURE) && (mDirection == false)) { + /* yep, sig verify error */ + CssmError::throwMe(CSSMERR_CSP_VERIFY_FAILED); + } + /* other error, use standard trap */ + check(status); + } + initialized = false; +} + +size_t BSafe::BSafeContext::outputSize(bool final, size_t inSize) +{ + // this default implementation only makes sense for single-output end-loaded algorithms + return final ? mOutSize : 0; +} + +void BSafe::BSafeContext::trackUpdate(size_t, size_t) +{ /* do nothing */ } + +// +// Common features of CipherContexts. +// +void BSafe::CipherContext::cipherInit() +{ + // set handlers + if (encoding) { + inOutUpdate = B_EncryptUpdate; + outFinalR = B_EncryptFinal; + } else { + inOutUpdate = B_DecryptUpdate; + outFinalR = B_DecryptFinal; + } + outFinal = NULL; + + // init the algorithm + check((encoding ? B_EncryptInit : B_DecryptInit) + (bsAlgorithm, bsKey, chooser(), bsSurrender)); + + // buffers start empty + pending = 0; + + // state is now valid + initialized = true; + opStarted = false; +} +#endif /* BSAFE_CSP_ENABLE */ +