#include "dbcrypto.h"
#include <security_utilities/casts.h>
#include <securityd_client/ssblob.h>
#include "server.h" // just for Server::csp()
#include <security_cdsa_client/genkey.h>
#include <security_cdsa_client/cryptoclient.h>
#include <security_cdsa_client/keyclient.h>
#include <security_cdsa_client/macclient.h>
#include <security_cdsa_client/wrapkey.h>
#include <security_cdsa_utilities/cssmendian.h>
using namespace CssmClient;
using LowLevelMemoryUtilities::fieldOffsetOf;
DatabaseCryptoCore::DatabaseCryptoCore(uint32 requestedVersion) : mBlobVersion(CommonBlob::version_MacOS_10_0), mHaveMaster(false), mIsValid(false)
{
if(requestedVersion == CommonBlob::version_none) {
mBlobVersion = CommonBlob::getCurrentVersion();
} else {
mBlobVersion = requestedVersion;
}
}
DatabaseCryptoCore::~DatabaseCryptoCore()
{
}
void DatabaseCryptoCore::invalidate()
{
mMasterKey.release();
mHaveMaster = false;
mEncryptionKey.release();
mSigningKey.release();
mIsValid = false;
}
void DatabaseCryptoCore::initializeFrom(DatabaseCryptoCore& core, uint32 requestedVersion) {
if(core.hasMaster()) {
mMasterKey = core.mMasterKey;
memcpy(mSalt, core.mSalt, sizeof(mSalt));
mHaveMaster = core.mHaveMaster;
} else {
mHaveMaster = false;
}
if(core.isValid()) {
importSecrets(core);
} else {
mIsValid = false;
}
if(requestedVersion == CommonBlob::version_none) {
mBlobVersion = core.mBlobVersion;
} else {
mBlobVersion = requestedVersion;
}
}
void DatabaseCryptoCore::generateNewSecrets()
{
GenerateKey desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
mEncryptionKey = desGenerator(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
GenerateKey signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC,
sizeof(DbBlob::PrivateBlob::SigningKey) * 8);
mSigningKey = signGenerator(KeySpec(CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
mIsValid = true;
}
CssmClient::Key DatabaseCryptoCore::masterKey()
{
assert(mHaveMaster);
return mMasterKey;
}
void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase, bool copyVersion )
{
if (blob) {
if(copyVersion) {
mBlobVersion = blob->version();
}
memcpy(mSalt, blob->salt, sizeof(mSalt));
} else {
Server::active().random(mSalt);
}
mMasterKey = deriveDbMasterKey(passphrase);
mHaveMaster = true;
}
void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master, bool copyVersion )
{
CssmKey::Header header = master.header();
if (header.keyClass() != CSSM_KEYCLASS_SESSION_KEY)
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
if (header.algorithm() != CSSM_ALGID_3DES_3KEY_EDE)
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
if (blob) {
if(copyVersion) {
mBlobVersion = blob->version();
}
memcpy(mSalt, blob->salt, sizeof(mSalt));
} else {
Server::active().random(mSalt);
}
mMasterKey = master;
mHaveMaster = true;
}
bool DatabaseCryptoCore::get_encryption_key(CssmOwnedData &data)
{
bool result = false;
if (isValid()) {
data = mEncryptionKey->keyData();
result = true;
}
return result;
}
bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase)
{
CssmClient::Key master = deriveDbMasterKey(passphrase);
return validateKey(master);
}
bool DatabaseCryptoCore::validateKey(const CssmClient::Key& master) {
assert(hasMaster());
StringData probe
("Now is the time for all good processes to come to the aid of their kernel.");
CssmData noRemainder((void *)1, 0); Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
cryptor.padding(CSSM_PADDING_PKCS1);
uint8 iv[8]; CssmData ivData = CssmData::wrap(iv);
cryptor.initVector(ivData);
cryptor.key(master);
CssmAutoData cipher1(Server::csp().allocator());
cryptor.encrypt(probe, cipher1.get(), noRemainder);
cryptor.key(mMasterKey);
CssmAutoData cipher2(Server::csp().allocator());
cryptor.encrypt(probe, cipher2.get(), noRemainder);
return cipher1 == cipher2;
}
DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
const CssmData &publicAcl, const CssmData &privateAcl) const
{
assert(isValid());
uint8 iv[8];
Server::active().random(iv);
CssmData &encryptionBits = *mEncryptionKey;
CssmData &signingBits = *mSigningKey;
CssmData incrypt[3];
incrypt[0] = encryptionBits;
incrypt[1] = signingBits;
incrypt[2] = privateAcl;
CssmData cryptoBlob, remData;
Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
cryptor.padding(CSSM_PADDING_PKCS1);
cryptor.key(mMasterKey);
CssmData ivd(iv, sizeof(iv)); cryptor.initVector(ivd);
cryptor.encrypt(incrypt, 3, &cryptoBlob, 1, remData);
size_t length = sizeof(DbBlob) + publicAcl.length() + cryptoBlob.length();
DbBlob *blob = Allocator::standard().malloc<DbBlob>(length);
memset(blob, 0x7d, sizeof(DbBlob)); blob->initialize(mBlobVersion);
blob->randomSignature = blobTemplate.randomSignature;
blob->sequence = blobTemplate.sequence;
blob->params = blobTemplate.params;
memcpy(blob->salt, mSalt, sizeof(blob->salt));
memcpy(blob->iv, iv, sizeof(iv));
memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
blob->startCryptoBlob = sizeof(DbBlob) + int_cast<size_t, uint32_t>(publicAcl.length());
memcpy(blob->cryptoBlob(), cryptoBlob, cryptoBlob.length());
blob->totalLength = blob->startCryptoBlob + int_cast<size_t, uint32_t>(cryptoBlob.length());
CssmData signChunk[] = {
CssmData(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
};
CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
CSSM_ALGORITHMS signingAlgorithm = CSSM_ALGID_SHA1HMAC;
#if defined(COMPAT_OSX_10_0)
if (blob->version() == blob->version_MacOS_10_0)
signingAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; #endif
GenerateMac signer(Server::csp(), signingAlgorithm);
signer.key(mSigningKey);
signer.sign(signChunk, 2, signature);
assert(signature.length() == sizeof(blob->blobSignature));
Server::csp()->allocator().free(cryptoBlob);
return blob;
}
void DatabaseCryptoCore::decodeCore(const DbBlob *blob, void **privateAclBlob)
{
assert(mHaveMaster);
Decrypt decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
decryptor.mode(CSSM_ALGMODE_CBCPadIV8);
decryptor.padding(CSSM_PADDING_PKCS1);
decryptor.key(mMasterKey);
CssmData ivd = CssmData::wrap(blob->iv); decryptor.initVector(ivd);
CssmData cryptoBlob = CssmData::wrap(blob->cryptoBlob(), blob->cryptoBlobLength());
CssmData decryptedBlob, remData;
decryptor.decrypt(cryptoBlob, decryptedBlob, remData);
DbBlob::PrivateBlob *privateBlob = decryptedBlob.interpretedAs<DbBlob::PrivateBlob>();
mEncryptionKey = makeRawKey(privateBlob->encryptionKey,
sizeof(privateBlob->encryptionKey), CSSM_ALGID_3DES_3KEY_EDE,
CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP);
mSigningKey = makeRawKey(privateBlob->signingKey,
sizeof(privateBlob->signingKey), CSSM_ALGID_SHA1HMAC,
CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY);
CssmData signChunk[] = {
CssmData::wrap(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
CssmData::wrap(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
};
CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
#if defined(COMPAT_OSX_10_0)
if (blob->version() == blob->version_MacOS_10_0)
verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; #endif
VerifyMac verifier(Server::csp(), verifyAlgorithm);
verifier.key(mSigningKey);
verifier.verify(signChunk, 2, CssmData::wrap(blob->blobSignature));
if (privateAclBlob) {
uint32 blobLength = (uint32) decryptedBlob.length() - sizeof(DbBlob::PrivateBlob);
*privateAclBlob = Allocator::standard().malloc(blobLength);
memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength);
}
mBlobVersion = blob->version();
mIsValid = true;
Allocator::standard().free(privateBlob);
}
void DatabaseCryptoCore::importSecrets(const DatabaseCryptoCore &src)
{
assert(src.isValid()); assert(hasMaster());
mEncryptionKey = src.mEncryptionKey;
mSigningKey = src.mSigningKey;
mBlobVersion = src.mBlobVersion; mIsValid = true;
}
KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
const CssmData &publicAcl, const CssmData &privateAcl,
bool inTheClear) const
{
CssmKey key = inKey;
uint8 iv[8];
CssmKey wrappedKey;
if(inTheClear && (privateAcl.Length != 0)) {
CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS);
}
uint32 heldAttributes = key.attributes() & managedAttributes;
key.clearAttribute(managedAttributes);
key.setAttribute(forcedAttributes);
if(inTheClear) {
WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
wrap(key, wrappedKey, NULL);
}
else {
assert(isValid());
Server::active().random(iv);
WrapKey wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
wrap.key(mEncryptionKey);
wrap.mode(CSSM_ALGMODE_CBCPadIV8);
wrap.padding(CSSM_PADDING_PKCS1);
CssmData ivd(iv, sizeof(iv)); wrap.initVector(ivd);
wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
wrap(key, wrappedKey, &privateAcl);
}
key.clearAttribute(forcedAttributes);
key.setAttribute(heldAttributes);
size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length();
KeyBlob *blob = Allocator::standard().malloc<KeyBlob>(length);
memset(blob, 0, sizeof(KeyBlob)); blob->initialize(mBlobVersion);
if(!inTheClear) {
memcpy(blob->iv, iv, sizeof(iv));
}
blob->header = key.header();
h2ni(blob->header); blob->wrappedHeader.blobType = wrappedKey.blobType();
blob->wrappedHeader.blobFormat = wrappedKey.blobFormat();
blob->wrappedHeader.wrapAlgorithm = wrappedKey.wrapAlgorithm();
blob->wrappedHeader.wrapMode = wrappedKey.wrapMode();
memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
blob->startCryptoBlob = sizeof(KeyBlob) + int_cast<size_t, uint32_t>(publicAcl.length());
memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length());
blob->totalLength = blob->startCryptoBlob + int_cast<size_t, uint32_t>(wrappedKey.length());
if(inTheClear) {
blob->setClearTextSignature();
}
else {
CssmData signChunk[] = {
CssmData(blob->data(), fieldOffsetOf(&KeyBlob::blobSignature)),
CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
};
CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
CSSM_ALGORITHMS signingAlgorithm = CSSM_ALGID_SHA1HMAC;
#if defined(COMPAT_OSX_10_0)
if (blob->version() == blob->version_MacOS_10_0)
signingAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; #endif
GenerateMac signer(Server::csp(), signingAlgorithm);
signer.key(mSigningKey);
signer.sign(signChunk, 2, signature);
assert(signature.length() == sizeof(blob->blobSignature));
}
Server::csp()->allocator().free(wrappedKey);
return blob;
}
void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
CssmKey &key, void * &pubAcl, void * &privAcl) const
{
CssmKey wrappedKey;
wrappedKey.KeyHeader = blob->header;
h2ni(wrappedKey.KeyHeader);
wrappedKey.blobType(blob->wrappedHeader.blobType);
wrappedKey.blobFormat(blob->wrappedHeader.blobFormat);
wrappedKey.wrapAlgorithm(blob->wrappedHeader.wrapAlgorithm);
wrappedKey.wrapMode(blob->wrappedHeader.wrapMode);
wrappedKey.KeyData = CssmData(blob->cryptoBlob(), blob->cryptoBlobLength());
bool inTheClear = blob->isClearText();
if(!inTheClear) {
assert(isValid()); CssmData signChunk[] = {
CssmData::wrap(blob, fieldOffsetOf(&KeyBlob::blobSignature)),
CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
};
CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
#if defined(COMPAT_OSX_10_0)
if (blob->version() == blob->version_MacOS_10_0)
verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; #endif
VerifyMac verifier(Server::csp(), verifyAlgorithm);
verifier.key(mSigningKey);
CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
verifier.verify(signChunk, 2, signature);
}
uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes;
CssmData privAclData;
if(inTheClear) {
UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
wrappedKey.clearAttribute(managedAttributes); unwrap(wrappedKey,
KeySpec(n2h(blob->header.usage()),
(n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
key, &privAclData);
}
else {
UnwrapKey unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
unwrap.key(mEncryptionKey);
unwrap.mode(CSSM_ALGMODE_CBCPadIV8);
unwrap.padding(CSSM_PADDING_PKCS1);
CssmData ivd(blob->iv, sizeof(blob->iv)); unwrap.initVector(ivd);
unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
wrappedKey.clearAttribute(managedAttributes); unwrap(wrappedKey,
KeySpec(n2h(blob->header.usage()),
(n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
key, &privAclData);
}
CssmKey::Header &real = key.header();
CssmKey::Header &incoming = blob->header;
n2hi(incoming);
if (real.HeaderVersion != incoming.HeaderVersion ||
real.cspGuid() != incoming.cspGuid())
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
if (real.algorithm() != incoming.algorithm())
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
key.header().KeyAttr |= heldAttributes;
if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
pubAcl = blob->publicAclBlob(); privAcl = privAclData; }
CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const
{
CssmClient::DeriveKey makeKey(Server::csp(),
CSSM_ALGID_PKCS5_PBKDF2, CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
makeKey.iterationCount(1000);
CssmData salt = CssmData::wrap(mSalt);
makeKey.salt(salt);
CSSM_PKCS5_PBKDF2_PARAMS params;
params.Passphrase = passphrase;
params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
CssmData paramData = CssmData::wrap(params);
return makeKey(¶mData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
}
CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
{
CssmKey key;
key.header().BlobType = CSSM_KEYBLOB_RAW;
key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
key.header().AlgorithmId = algid;
key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
key.header().KeyUsage = usage;
key.header().KeyAttr = 0;
key.KeyData = CssmData(data, length);
UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
CssmKey unwrappedKey;
CssmData descriptiveData;
unwrap(key,
KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE),
unwrappedKey, &descriptiveData, NULL);
return CssmClient::Key(Server::csp(), unwrappedKey);
}