#include "AppleCSPUtils.h"
#include <Security/cssmerr.h>
#include <security_utilities/alloc.h>
#include <security_cdsa_utilities/cssmdates.h>
#include <string.h>
#include <FEECSPUtils.h>
#include <SHA1_MD5_Object.h>
#include "RSA_DSA_keys.h"
#include <syslog.h>
void cspValidateKeyAttr(
cspKeyType keyType,
uint32 keyAttr)
{
uint32 sensitiveBit = (keyAttr & CSSM_KEYATTR_SENSITIVE) ? 1 : 0;
uint32 extractBit = (keyAttr & CSSM_KEYATTR_EXTRACTABLE) ? 1 : 0;
if(keyAttr & KEY_ATTR_RETURN_MASK) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
if(keyAttr & CSSM_KEYATTR_PERMANENT) {
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK);
}
if(keyAttr & CSSM_KEYATTR_PRIVATE) {
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK);
}
switch(keyType) {
case CKT_Session:
break;
case CKT_Public:
if(sensitiveBit || !extractBit) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
break;
case CKT_Private:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
return;
}
cspKeyStorage cspParseKeyAttr(
cspKeyType keyType,
uint32 keyAttr)
{
uint32 sensitiveBit = (keyAttr & CSSM_KEYATTR_SENSITIVE) ? 1 : 0;
uint32 rtnDataBit = (keyAttr & CSSM_KEYATTR_RETURN_DATA) ? 1 : 0;
uint32 rtnRefBit = (keyAttr & CSSM_KEYATTR_RETURN_REF) ? 1 : 0;
uint32 extractBit = (keyAttr & CSSM_KEYATTR_EXTRACTABLE) ? 1 : 0;
cspKeyStorage rtn;
if(keyAttr & (CSSM_KEYATTR_ALWAYS_SENSITIVE |
CSSM_KEYATTR_NEVER_EXTRACTABLE)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
switch(keyAttr & KEY_ATTR_RETURN_MASK) {
case CSSM_KEYATTR_RETURN_DATA:
rtn = CKS_Data;
break;
case CSSM_KEYATTR_RETURN_REF:
rtn = CKS_Ref;
break;
case CSSM_KEYATTR_RETURN_NONE:
rtn = CKS_None;
break;
case CSSM_KEYATTR_RETURN_DEFAULT:
rtnRefBit = 1;
rtn = CKS_Ref;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
if(keyType != CKT_Session) {
if(keyAttr & CSSM_KEYATTR_MODIFIABLE) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
}
if(rtnDataBit) {
if(!extractBit) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
if(sensitiveBit) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
}
#if 0
switch(keyType) {
case CKT_Session:
break;
case MKT_Public:
break;
case MKT_Private:
if(rtnDataBit) {
errorLog0("Private keys must be generated by ref\n");
goto errorOut;
}
break;
default:
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
#endif
cspValidateKeyAttr(keyType, (keyAttr & ~KEY_ATTR_RETURN_MASK));
return rtn;
}
#define IGNORE_KEYUSE_EXCLUSIVITY 1
#if IGNORE_KEYUSE_EXCLUSIVITY
#define checkExclusiveUsage(ku, cb, ob, em)
#else
static void checkExclusiveUsage(
uint32 keyUsage, uint32 checkBits, uint32 otherBits, const char *errMsg)
{
if(keyUsage & checkBits) {
if(keyUsage & ~otherBits) {
errorLog0((char *)errMsg);
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
}
}
#endif
void cspValidateKeyUsageBits (
cspKeyType keyType,
uint32 keyUsage)
{
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_ANY,
CSSM_KEYUSE_ANY,
"CSSM_KEYUSE_ANY overload");
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_DERIVE,
CSSM_KEYUSE_DERIVE,
"CSSM_KEYUSE_DERIVE overload\n");
switch(keyType) {
case CKT_Session:
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
"session key usage: encrypt/decrypt overload\n");
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY |
CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER,
CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY |
CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER,
"session key usage: sign/verify overload\n");
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
"session key usage: wrap/unwrap overload\n");
break;
case CKT_Public:
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_ENCRYPT,
CSSM_KEYUSE_ENCRYPT,
"public key usage: encrypt overload\n");
if(keyUsage & CSSM_KEYUSE_DECRYPT) {
errorLog0("public key usage: DECRYPT illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
if(keyUsage & (CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER)) {
errorLog0("public key usage: SIGN illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER,
CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER,
"public key usage: verify overload\n");
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_WRAP,
CSSM_KEYUSE_WRAP,
"public key usage: wrap overload\n");
if(keyUsage & CSSM_KEYUSE_UNWRAP) {
errorLog0("public key usage: UNWRAP illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
break;
case CKT_Private:
if(keyUsage & CSSM_KEYUSE_ENCRYPT) {
errorLog0("private key usage: ENCRYPT illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_DECRYPT,
CSSM_KEYUSE_DECRYPT,
"private key usage: decrypt overload\n");
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER,
CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER,
"private key usage: sign overload\n");
if(keyUsage & (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER)) {
errorLog0("private key usage: VERIFY illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
if(keyUsage & CSSM_KEYUSE_WRAP) {
errorLog0("private key usage: WRAP illegal\n");
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
}
checkExclusiveUsage(keyUsage,
CSSM_KEYUSE_UNWRAP,
CSSM_KEYUSE_UNWRAP,
"private key usage: unwrap overload\n");
break;
default:
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
}
#define RELAXED_WRAP_USAGE 1
void cspValidateIntendedKeyUsage(
const CSSM_KEYHEADER *hdr,
CSSM_KEYUSE intendedUsage)
{
uint32 keyUsage = hdr->KeyUsage;
cspKeyType keyType;
if(keyUsage & CSSM_KEYUSE_ANY) {
return;
}
if(!(keyUsage & intendedUsage)) {
#if RELAXED_WRAP_USAGE
if(! ( ( (keyUsage & CSSM_KEYUSE_WRAP) &&
(intendedUsage == CSSM_KEYUSE_ENCRYPT)
) ||
( (keyUsage & CSSM_KEYUSE_UNWRAP) &&
(intendedUsage == CSSM_KEYUSE_DECRYPT)
)
) )
#endif
CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
}
switch(hdr->KeyClass) {
case CSSM_KEYCLASS_SESSION_KEY:
keyType = CKT_Session;
break;
case CSSM_KEYCLASS_PUBLIC_KEY:
keyType = CKT_Public;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
keyType = CKT_Private;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
try {
cspValidateKeyUsageBits(keyType, keyUsage);
}
catch (...) {
CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
}
}
void setKeyHeader(
CSSM_KEYHEADER &hdr,
const Guid &myGuid,
CSSM_ALGORITHMS alg,
CSSM_KEYCLASS keyClass,
CSSM_KEYATTR_FLAGS attrs,
CSSM_KEYUSE use)
{
memset(&hdr, 0, sizeof(CSSM_KEYHEADER));
hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr.CspId = myGuid;
hdr.AlgorithmId = alg;
hdr.KeyClass = keyClass;
hdr.KeyUsage = use;
hdr.KeyAttr = attrs;
hdr.WrapAlgorithmId = CSSM_ALGID_NONE;
}
void setUpCssmData(
CssmData &data,
size_t length,
Allocator &allocator)
{
if(data.Length == 0) {
data.Data = (uint8 *)allocator.malloc(length);
}
else if(data.Length < length) {
CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
}
data.Length = length;
}
void setUpData(
CSSM_DATA &data,
size_t length,
Allocator &allocator)
{
setUpCssmData(CssmData::overlay(data), length, allocator);
}
void freeCssmData(
CssmData &data,
Allocator &allocator)
{
if(data.Data) {
allocator.free(data.Data);
data.Data = NULL;
}
data.Length = 0;
}
void freeData(
CSSM_DATA *data,
Allocator &allocator,
bool freeStruct) {
if(data == NULL) {
return;
}
if(data->Data) {
allocator.free(data->Data);
data->Data = NULL;
}
data->Length = 0;
if(freeStruct) {
allocator.free(data);
}
}
void copyCssmData(
const CssmData &src,
CssmData &dst,
Allocator &allocator)
{
setUpCssmData(dst, src.Length, allocator);
memmove(dst.Data, src.Data, src.Length);
}
void copyData(
const CSSM_DATA &src,
CSSM_DATA &dst,
Allocator &allocator)
{
copyCssmData(CssmData::overlay(src),
CssmData::overlay(dst),
allocator);
}
CSSM_BOOL cspCompareCssmData(
const CSSM_DATA *data1,
const CSSM_DATA *data2)
{
if((data1 == NULL) || (data1->Data == NULL) ||
(data2 == NULL) || (data2->Data == NULL) ||
(data1->Length != data2->Length)) {
return CSSM_FALSE;
}
if(data1->Length != data2->Length) {
return CSSM_FALSE;
}
if(memcmp(data1->Data, data2->Data, data1->Length) == 0) {
return CSSM_TRUE;
}
else {
return CSSM_FALSE;
}
}
void copyCssmHeader(
const CssmKey::Header &src,
CssmKey::Header &dst,
Allocator &allocator)
{
dst = src;
}
CSSM_KEYBLOB_FORMAT inferFormat(
const CssmKey &wrappedKey)
{
switch(wrappedKey.keyClass()) {
case CSSM_KEYCLASS_SESSION_KEY:
return CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
case CSSM_KEYCLASS_PUBLIC_KEY:
switch(wrappedKey.algorithm()) {
case CSSM_ALGID_RSA:
return RSA_PUB_KEY_FORMAT;
case CSSM_ALGID_DSA:
return DSA_PUB_KEY_FORMAT;
#ifdef CRYPTKIT_CSP_ENABLE
case CSSM_ALGID_FEE:
return FEE_KEYBLOB_DEFAULT_FORMAT;
case CSSM_ALGID_ECDSA:
return CSSM_KEYBLOB_RAW_FORMAT_X509;
#endif
case CSSM_ALGID_DH:
return CSSM_KEYBLOB_RAW_FORMAT_PKCS3;
default:
return CSSM_KEYBLOB_RAW_FORMAT_NONE;
}
case CSSM_KEYCLASS_PRIVATE_KEY:
switch(wrappedKey.algorithm()) {
case CSSM_ALGID_RSA:
return RSA_PRIV_KEY_FORMAT;
case CSSM_ALGID_DSA:
return DSA_PRIV_KEY_FORMAT;
#ifdef CRYPTKIT_CSP_ENABLE
case CSSM_ALGID_FEE:
return FEE_KEYBLOB_DEFAULT_FORMAT;
case CSSM_ALGID_ECDSA:
return CSSM_KEYBLOB_RAW_FORMAT_OPENSSL;
#endif
case CSSM_ALGID_DH:
return CSSM_KEYBLOB_RAW_FORMAT_PKCS3;
default:
return CSSM_KEYBLOB_RAW_FORMAT_NONE;
}
default:
return CSSM_KEYBLOB_RAW_FORMAT_NONE;
}
}
CSSM_KEYBLOB_FORMAT requestedKeyFormat(
const Context &context,
const CssmKey &key)
{
CSSM_ATTRIBUTE_TYPE attrType;
switch(key.keyClass()) {
case CSSM_KEYCLASS_SESSION_KEY:
attrType = CSSM_ATTRIBUTE_SYMMETRIC_KEY_FORMAT;
break;
case CSSM_KEYCLASS_PUBLIC_KEY:
attrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
attrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
break;
default:
return CSSM_KEYBLOB_RAW_FORMAT_NONE;
}
return context.getInt(attrType);
}
void cspGenSha1Hash(
const void *inData,
size_t inDataLen,
void *out) {
SHA1Object sha1;
sha1.digestInit();
sha1.digestUpdate(inData, inDataLen);
sha1.digestFinal(out);
}
static CssmUniformDate *cspGetUniformDate(
const CSSM_DATE &cdate)
{
bool isZero = true;
unsigned char *cp = (unsigned char *)&cdate;
for(unsigned i=0; i<sizeof(cdate); i++) {
if(*cp++ != 0) {
isZero = false;
break;
}
}
if(isZero) {
return NULL;
}
else {
return new CssmUniformDate(CssmDate::overlay(cdate));
}
}
static CssmUniformDate *cspNow()
{
CFAbsoluteTime cfTime = CFAbsoluteTimeGetCurrent();
return new CssmUniformDate(cfTime);
}
#define keyDateDebug(args...) secinfo("keyDate", ## args)
void cspVerifyKeyTimes(
const CSSM_KEYHEADER &hdr)
{
CSSM_RETURN err = CSSM_OK;
CssmUniformDate *now = NULL; CssmUniformDate *end = NULL; CssmUniformDate *start = cspGetUniformDate(hdr.StartDate);
if(start) {
now = cspNow();
if(*now < *start) {
keyDateDebug("Invalid start date");
err = CSSMERR_CSP_APPLE_INVALID_KEY_START_DATE;
}
else {
keyDateDebug("Valid start date");
}
}
else {
keyDateDebug("Empty start date");
}
if(!err) {
end = cspGetUniformDate(hdr.EndDate);
if(end) {
if(now == NULL) {
now = cspNow();
}
if(*now > *end) {
keyDateDebug("Invalid end date");
err = CSSMERR_CSP_APPLE_INVALID_KEY_END_DATE;
}
else {
keyDateDebug("Valid end date");
}
}
else {
keyDateDebug("Empty end date");
}
}
if(now) {
delete now;
}
if(end) {
delete end;
}
if(start) {
delete start;
}
if(err) {
CssmError::throwMe(err);
}
}