#define ALLOW_PUB_KEY_WRAP 1
#include "AppleCSPSession.h"
#include "AppleCSPUtils.h"
#include "AppleCSPKeys.h"
#include "pkcs8.h"
#include "cspdebugging.h"
void AppleCSPSession::WrapKey(
CSSM_CC_HANDLE CCHandle,
const Context &Context,
const AccessCredentials &AccessCred,
const CssmKey &UnwrappedKey,
const CssmData *DescriptiveData,
CssmKey &WrappedKey,
CSSM_PRIVILEGE Privilege)
{
CssmKey::Header &wrappedHdr = WrappedKey.header();
bool isNullWrap = false;
CssmKey *wrappingKey = NULL;
CSSM_KEYBLOB_FORMAT wrapFormat;
switch(UnwrappedKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
case CSSM_KEYCLASS_SESSION_KEY:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
wrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
if(wrappingKey == NULL) {
if((Context.algorithm() == CSSM_ALGID_NONE) &&
(Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
isNullWrap = true;
}
else {
errorLog0("WrapKey: missing wrapping key\n");
CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
}
}
if(isNullWrap) {
wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_NONE;
}
else {
#if !ALLOW_PUB_KEY_WRAP
if(UnwrappedKey.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
#endif
cspValidateIntendedKeyUsage(&wrappingKey->KeyHeader, CSSM_KEYUSE_WRAP);
cspVerifyKeyTimes(wrappingKey->KeyHeader);
CSSM_CONTEXT_TYPE wrapType;
switch(wrappingKey->KeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
wrapType = CSSM_ALGCLASS_ASYMMETRIC;
break;
case CSSM_KEYCLASS_SESSION_KEY:
wrapType = CSSM_ALGCLASS_SYMMETRIC;
break;
default:
errorLog0("WrapKey: bad class of wrappingKey\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
}
if(wrapType != Context.type()) {
errorLog0("WrapKey: mismatch wrappingKey/contextType\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
}
if(Context.algorithm() == CSSM_ALGID_NONE) {
errorLog0("WrapKey: null wrap alg, non-null key\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
wrapFormat = Context.getInt(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT);
if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
switch(UnwrappedKey.keyClass()) {
case CSSM_KEYCLASS_SESSION_KEY:
wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
break;
case CSSM_KEYCLASS_PUBLIC_KEY:
wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
switch(UnwrappedKey.algorithm()) {
case CSSM_ALGID_FEE:
wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM;
break;
default:
wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
break;
}
break;
default:
break;
}
}
}
switch(wrapFormat) {
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
if(UnwrappedKey.algorithm() != CSSM_ALGID_RSA) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
if(UnwrappedKey.blobType() != CSSM_KEYBLOB_REFERENCE) {
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE:
if(isNullWrap) {
break;
}
default:
dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat);
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
}
CssmData rawBlob;
bool allocdRawBlob = false;
CSSM_KEYBLOB_FORMAT rawFormat;
const CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
CSSM_KEYATTR_FLAGS unwrappedKeyAttrFlags = unwrappedHdr.KeyAttr;
switch(UnwrappedKey.blobType()) {
case CSSM_KEYBLOB_RAW:
rawBlob = CssmData::overlay(UnwrappedKey.KeyData);
rawFormat = UnwrappedKey.blobFormat();
break;
case CSSM_KEYBLOB_REFERENCE:
{
BinaryKey &binKey = lookupRefKey(UnwrappedKey);
CSSM_KEYATTR_FLAGS keyAttr = binKey.mKeyHeader.KeyAttr;
if(!(keyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1) {
break;
}
if(isNullWrap && (keyAttr & CSSM_KEYATTR_SENSITIVE)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
rawFormat = requestedKeyFormat(Context, UnwrappedKey);
if(rawFormat == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
CSSM_ALGORITHMS keyAlg = binKey.mKeyHeader.AlgorithmId;
switch(wrapFormat) {
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
rawFormat = pkcs8RawKeyFormat(keyAlg);
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
rawFormat = opensslRawKeyFormat(keyAlg);
break;
default:
break;
}
}
if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) {
binKey.descData(*DescriptiveData);
}
CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
binKey.generateKeyBlob(privAllocator,
rawBlob,
rawFormat,
*this,
paramKey,
unwrappedKeyAttrFlags);
}
allocdRawBlob = true; break;
default:
errorLog0("WrapKey: bad unwrappedKey BlobType\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
setKeyHeader(wrappedHdr,
plugin.myGuid(),
unwrappedHdr.algorithm(), unwrappedHdr.keyClass(), unwrappedKeyAttrFlags,
unwrappedHdr.KeyUsage);
wrappedHdr.LogicalKeySizeInBits = unwrappedHdr.LogicalKeySizeInBits;
wrappedHdr.WrapAlgorithmId = Context.algorithm(); wrappedHdr.StartDate = unwrappedHdr.StartDate;
wrappedHdr.EndDate = unwrappedHdr.EndDate;
wrappedHdr.Format = wrapFormat;
if(isNullWrap) {
wrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
}
else {
wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
}
if(!isNullWrap) {
switch(wrapFormat) {
case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
try {
WrapKeyCms(CCHandle,
Context,
AccessCred,
UnwrappedKey,
rawBlob,
allocdRawBlob,
DescriptiveData,
WrappedKey,
Privilege);
}
catch(...) {
if(allocdRawBlob) {
freeCssmData(rawBlob, privAllocator);
}
throw;
}
if(allocdRawBlob) {
freeCssmData(rawBlob, privAllocator);
}
return;
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
{
BinaryKey &binKey = lookupRefKey(UnwrappedKey);
WrapKeyOpenSSH1(CCHandle,
Context,
AccessCred,
binKey,
rawBlob,
allocdRawBlob,
DescriptiveData,
WrappedKey,
Privilege);
return;
}
default:
break;
}
}
CssmData encryptedBlob;
CssmData remData;
WrappedKey.KeyData.Data = NULL; WrappedKey.KeyData.Length = 0;
try {
if(isNullWrap) {
copyCssmData(rawBlob,
CssmData::overlay(WrappedKey.KeyData),
normAllocator);
wrappedHdr.Format = rawFormat;
}
else {
CSSM_SIZE bytesEncrypted;
EncryptData(CCHandle,
Context,
&rawBlob, 1, &encryptedBlob, 1, bytesEncrypted,
remData,
Privilege);
assert(remData.Length == 0);
encryptedBlob.Length = bytesEncrypted;
WrappedKey.KeyData = encryptedBlob;
wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
wrappedHdr.WrapMode = Context.getInt(
CSSM_ATTRIBUTE_MODE);
}
}
catch (...) {
errorLog0("WrapKey: EncryptData() threw exception\n");
if(allocdRawBlob) {
freeCssmData(rawBlob, privAllocator);
}
freeCssmData(remData,normAllocator);
throw;
}
if(allocdRawBlob) {
freeCssmData(rawBlob, privAllocator);
}
freeCssmData(remData, normAllocator);
}
void AppleCSPSession::UnwrapKey(
CSSM_CC_HANDLE CCHandle,
const Context &Context,
const CssmKey *PublicKey,
const CssmKey &WrappedKey,
uint32 KeyUsage,
uint32 KeyAttr,
const CssmData *KeyLabel,
const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
CssmKey &UnwrappedKey,
CssmData &DescriptiveData,
CSSM_PRIVILEGE Privilege)
{
bool isNullUnwrap = false;
CssmKey *unwrappingKey = NULL;
cspKeyType keyType; CSSM_KEYBLOB_FORMAT wrapFormat = WrappedKey.blobFormat();
unwrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
if(unwrappingKey == NULL) {
if((Context.algorithm() == CSSM_ALGID_NONE) &&
(Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
isNullUnwrap = true;
}
else {
errorLog0("UnwrapKey: missing wrapping key\n");
CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
}
}
if(!isNullUnwrap) {
CSSM_CONTEXT_TYPE unwrapType;
switch(unwrappingKey->KeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
unwrapType = CSSM_ALGCLASS_ASYMMETRIC;
break;
case CSSM_KEYCLASS_SESSION_KEY:
unwrapType = CSSM_ALGCLASS_SYMMETRIC;
break;
default:
errorLog0("UnwrapKey: bad class of wrappingKey\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
}
if(unwrapType != Context.type()) {
errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
}
if(Context.algorithm() == CSSM_ALGID_NONE) {
errorLog0("UnwrapKey: null wrap alg, non-null key\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
cspValidateIntendedKeyUsage(&unwrappingKey->KeyHeader, CSSM_KEYUSE_UNWRAP);
cspVerifyKeyTimes(unwrappingKey->KeyHeader);
}
switch(WrappedKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
#if !ALLOW_PUB_KEY_WRAP
if(!isNullUnwrap) {
errorLog0("UnwrapKey: unwrap of public key illegal\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
#endif
keyType = CKT_Public;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
keyType = CKT_Private;
break;
case CSSM_KEYCLASS_SESSION_KEY:
keyType = CKT_Session;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
if(isNullUnwrap) {
if(WrappedKey.blobType() != CSSM_KEYBLOB_RAW) {
errorLog0("UnwrapKey: expected raw blobType\n");
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
}
else {
if(WrappedKey.blobType() != CSSM_KEYBLOB_WRAPPED) {
errorLog0("UnwrapKey: expected wrapped blobType\n");
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
}
cspKeyStorage keyStorage = cspParseKeyAttr(keyType, KeyAttr);
switch(keyStorage) {
case CKS_Ref:
case CKS_Data:
break; default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
}
cspValidateKeyUsageBits(keyType, KeyUsage);
CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
const CssmKey::Header &wrappedHdr = WrappedKey.header();
setKeyHeader(unwrappedHdr,
plugin.myGuid(),
wrappedHdr.algorithm(), wrappedHdr.keyClass(), KeyAttr & ~KEY_ATTR_RETURN_MASK,
KeyUsage);
unwrappedHdr.LogicalKeySizeInBits = wrappedHdr.LogicalKeySizeInBits;
unwrappedHdr.StartDate = wrappedHdr.StartDate;
unwrappedHdr.EndDate = wrappedHdr.EndDate;
UnwrappedKey.KeyData.Data = NULL; UnwrappedKey.KeyData.Length = 0;
if(!isNullUnwrap) {
switch(wrapFormat) {
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
if(WrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
UnwrapKeyCms(CCHandle,
Context,
WrappedKey,
CredAndAclEntry,
UnwrappedKey,
DescriptiveData,
Privilege,
keyStorage);
return;
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
if(WrappedKey.algorithm() != CSSM_ALGID_RSA) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
if(keyStorage != CKS_Ref) {
errorLog0("UNwrapKey: OPENSSH1 only wraps to reference key\n");
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
UnwrapKeyOpenSSH1(CCHandle,
Context,
WrappedKey,
CredAndAclEntry,
UnwrappedKey,
DescriptiveData,
Privilege,
keyStorage);
return;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
}
}
CssmData decodedBlob;
CssmData remData;
try {
if(isNullUnwrap) {
copyData(WrappedKey.KeyData,
UnwrappedKey.KeyData,
normAllocator);
unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
unwrappedHdr.Format = wrapFormat;
}
else {
decodedBlob = CssmData::overlay(WrappedKey.KeyData);
CSSM_SIZE bytesDecrypted;
CssmData *unwrapData =
CssmData::overlay(&UnwrappedKey.KeyData);
DecryptData(CCHandle,
Context,
&decodedBlob, 1, unwrapData, 1, bytesDecrypted,
remData,
Privilege);
assert(remData.Length == 0);
UnwrappedKey.KeyData.Length = bytesDecrypted;
unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
switch(wrapFormat) {
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
unwrappedHdr.Format =
CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
if(unwrappedHdr.LogicalKeySizeInBits == 0) {
unwrappedHdr.LogicalKeySizeInBits =
(unsigned)(bytesDecrypted * 8);
}
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
pkcs8InferKeyHeader(UnwrappedKey);
break;
case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
opensslInferKeyHeader(UnwrappedKey);
break;
}
}
}
catch (...) {
errorLog0("UnwrapKey: DecryptData() threw exception\n");
freeCssmData(remData, normAllocator);
throw;
}
freeCssmData(remData, normAllocator);
if(keyStorage == CKS_Ref) {
BinaryKey *binKey = NULL;
CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey);
CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey);
addRefKey(*binKey, UnwrappedKey);
delete provider;
}
}