#ifdef BSAFE_CSP_ENABLE
#include "bsafecspi.h"
#include "bsafePKCS1.h"
#include <bkey.h>
#include <balg.h>
#include <algobj.h>
#include "cspdebugging.h"
#define DATA(cData) POINTER(cData.data()), cData.length()
A_SURRENDER_CTX * const BSafe::BSafeContext::bsSurrender = NULL;
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();
}
void BSafe::BSafeContext::destroyBsKey()
{
if(bsBinKey == NULL) {
B_DestroyKeyObject(&bsKey);
}
else {
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:
default:
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
}
}
void BSafe::BSafeContext::setAlgorithm(
B_INFO_TYPE bAlgType,
const void *info)
{
B_DestroyAlgorithmObject(&bsAlgorithm); check(B_CreateAlgorithmObject(&bsAlgorithm));
check(B_SetAlgorithmInfo(bsAlgorithm, bAlgType, POINTER(info)));
}
void BSafe::BSafeContext::createBsKey()
{
destroyBsKey();
check(B_CreateKeyObject(&bsKey));
}
void BSafe::BSafeContext::setKeyAtom(
B_INFO_TYPE bKeyInfo,
const void *info)
{
if((bKeyInfo == KI_RSAPublicBER) || (bKeyInfo == KI_RSAPublic)) {
printf("Aargh! Unhandled KI_RSAPublic!\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
assert(bKeyInfo != KI_RSAPublicBER); assert(bKeyInfo != KI_RSAPublic); createBsKey();
check(B_SetKeyInfo(bsKey, bKeyInfo, POINTER(info)), true);
}
void BSafe::BSafeContext::setRsaOutSize(
bool isPubKey)
{
assert(bsKey != NULL);
A_RSA_KEY *keyInfo;
if(isPubKey) {
keyInfo = getKey<A_RSA_KEY>(bsKey, KI_RSAPublic);
}
else {
keyInfo = getKey<A_RSA_KEY>(bsKey, KI_RSAPrivate);
}
mOutSize = (B_IntegerBits(keyInfo->modulus.data,
keyInfo->modulus.len) + 7) / 8;
}
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);
SymmetricBinaryKey *symBinKey =
dynamic_cast<SymmetricBinaryKey *>(&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; case CSSM_KEYCLASS_PRIVATE_KEY:
{
BinaryKey &binKey = session().lookupRefKey(key);
destroyBsKey();
bsBinKey = dynamic_cast<BSafeBinaryKey *>(&binKey);
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<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
switch(key.blobType()) {
case CSSM_KEYBLOB_REFERENCE:
setRefKey(key);
return;
case CSSM_KEYBLOB_RAW:
break; default:
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
bool isPubKey;
switch (key.keyClass()) {
case CSSM_KEYCLASS_SESSION_KEY:
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);
}
B_INFO_TYPE infoType;
CSSM_KEYBLOB_FORMAT expectedFormat;
if(!bsafeAlgToInfoType(key.algorithm(),
isPubKey,
infoType,
expectedFormat)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
if(expectedFormat != key.blobFormat()) {
errorLog1("setKeyFromContext: invalid blob format (%d)\n",
(int)key.blobFormat());
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
}
switch(expectedFormat) {
case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
createBsKey();
BS_setKeyPkcs1(CssmData::overlay(key.KeyData), bsKey);
break;
default:
setKeyFromCssmKey(infoType, key);
break;
}
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));
}
}
void BSafe::BSafeContext::init(const Context &, bool)
{
}
void BSafe::BSafeContext::update(const CssmData &data)
{
opStarted = true;
check(inUpdate(bsAlgorithm, POINTER(data.data()), data.length(), bsSurrender));
}
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));
outSize = length;
trackUpdate(inSize, outSize);
}
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;
}
void BSafe::BSafeContext::final(const CssmData &in)
{
int status;
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)) {
CssmError::throwMe(CSSMERR_CSP_VERIFY_FAILED);
}
check(status);
}
initialized = false;
}
size_t BSafe::BSafeContext::outputSize(bool final, size_t inSize)
{
return final ? mOutSize : 0;
}
void BSafe::BSafeContext::trackUpdate(size_t, size_t)
{ }
void BSafe::CipherContext::cipherInit()
{
if (encoding) {
inOutUpdate = B_EncryptUpdate;
outFinalR = B_EncryptFinal;
} else {
inOutUpdate = B_DecryptUpdate;
outFinalR = B_DecryptFinal;
}
outFinal = NULL;
check((encoding ? B_EncryptInit : B_DecryptInit)
(bsAlgorithm, bsKey, chooser(), bsSurrender));
pending = 0;
initialized = true;
opStarted = false;
}
#endif