+++ /dev/null
-/*
- * 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.
- */
-
-
-//
-// dbcrypto - cryptographic core for database and key blob cryptography
-//
-#include "dbcrypto.h"
-#include "ssblob.h"
-#include "server.h" // just for Server::csp()
-#include <Security/genkey.h>
-#include <Security/cryptoclient.h>
-#include <Security/keyclient.h>
-#include <Security/macclient.h>
-#include <Security/wrapkey.h>
-
-
-using namespace CssmClient;
-
-
-DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
-{
-}
-
-
-DatabaseCryptoCore::~DatabaseCryptoCore()
-{
- // key objects take care of themselves
-}
-
-
-//
-// Forget the secrets
-//
-void DatabaseCryptoCore::invalidate()
-{
- mMasterKey.release();
- mHaveMaster = false;
-
- mEncryptionKey.release();
- mSigningKey.release();
- mIsValid = false;
-}
-
-
-//
-// Generate new secrets for this crypto core.
-//
-void DatabaseCryptoCore::generateNewSecrets()
-{
- // create a random DES3 key
- 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));
-
- // create a random 20 byte HMAC/SHA1 signing "key"
- 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));
-
- // secrets established
- mIsValid = true;
-}
-
-
-CssmClient::Key DatabaseCryptoCore::masterKey()
-{
- assert(mHaveMaster);
- return mMasterKey;
-}
-
-
-//
-// Establish the master secret as derived from a passphrase passed in.
-// If a DbBlob is passed, take the salt from it and remember it.
-// If a NULL DbBlob is passed, generate a new (random) salt.
-// Note that the passphrase is NOT remembered; only the master key.
-//
-void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase)
-{
- if (blob)
- memcpy(mSalt, blob->salt, sizeof(mSalt));
- else
- Server::active().random(mSalt);
- mMasterKey = deriveDbMasterKey(passphrase);
- mHaveMaster = true;
-}
-
-
-//
-// Establish the master secret directly from a master key passed in.
-// We will copy the KeyData (caller still owns its copy).
-// Blob/salt handling as above.
-//
-void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master)
-{
- // pre-screen the key
- 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);
-
- // accept it
- if (blob)
- memcpy(mSalt, blob->salt, sizeof(mSalt));
- else
- Server::active().random(mSalt);
- mMasterKey = master;
- mHaveMaster = true;
-}
-
-
-//
-// Given a putative passphrase, determine whether that passphrase
-// properly generates the database's master secret.
-// Return a boolean accordingly. Do not change our state.
-// The database must have a master secret (to compare with).
-// Note that any errors thrown by the cryptography here will actually
-// throw out of validatePassphrase, since they "should not happen" and
-// thus indicate a problem *beyond* (just) a bad passphrase.
-//
-bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase)
-{
- assert(hasMaster());
- CssmClient::Key master = deriveDbMasterKey(passphrase);
-
- // to compare master with mMaster, see if they encrypt alike
- StringData probe
- ("Now is the time for all good processes to come to the aid of their kernel.");
- CssmData noRemainder((void *)1, 0); // no cipher overflow
- Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
- cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
- cryptor.padding(CSSM_PADDING_PKCS1);
- uint8 iv[8]; // leave uninitialized; pseudo-random is cool
- cryptor.initVector(CssmData::wrap(iv));
-
- 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;
-}
-
-
-//
-// Encode a database blob from the core.
-//
-DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
- const CssmData &publicAcl, const CssmData &privateAcl) const
-{
- assert(isValid()); // must have secrets to work from
-
- // make a new IV
- uint8 iv[8];
- Server::active().random(iv);
-
- // build the encrypted section blob
- 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);
-
- // allocate the final DbBlob, uh, blob
- size_t length = sizeof(DbBlob) + publicAcl.length() + cryptoBlob.length();
- DbBlob *blob = CssmAllocator::standard().malloc<DbBlob>(length);
-
- // assemble the DbBlob
- memset(blob, 0x7d, sizeof(DbBlob)); // deterministically fill any alignment gaps
- blob->initialize();
- 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) + publicAcl.length();
- memcpy(blob->cryptoBlob(), cryptoBlob, cryptoBlob.length());
- blob->totalLength = blob->startCryptoBlob + cryptoBlob.length();
-
- // sign the blob
- CssmData signChunk[] = {
- CssmData(blob->data(), offsetof(DbBlob, blobSignature)),
- CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
- };
- CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
- GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY); //@@@!!! CRUD
- signer.key(mSigningKey);
- signer.sign(signChunk, 2, signature);
- assert(signature.length() == sizeof(blob->blobSignature));
-
- // all done. Clean up
- Server::csp()->allocator().free(cryptoBlob);
- return blob;
-}
-
-
-//
-// Decode a database blob into the core.
-// Throws exceptions if decoding fails.
-// Memory returned in privateAclBlob is allocated and becomes owned by caller.
-//
-void DatabaseCryptoCore::decodeCore(DbBlob *blob, void **privateAclBlob)
-{
- assert(mHaveMaster); // must have master key installed
-
- // try to decrypt the cryptoblob section
- Decrypt decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
- decryptor.mode(CSSM_ALGMODE_CBCPadIV8);
- decryptor.padding(CSSM_PADDING_PKCS1);
- decryptor.key(mMasterKey);
- CssmData ivd(blob->iv, sizeof(blob->iv)); decryptor.initVector(ivd);
- CssmData cryptoBlob(blob->cryptoBlob(), blob->cryptoBlobLength());
- CssmData decryptedBlob, remData;
- decryptor.decrypt(cryptoBlob, decryptedBlob, remData);
- DbBlob::PrivateBlob *privateBlob = decryptedBlob.interpretedAs<DbBlob::PrivateBlob>();
-
- // tentatively establish keys
- 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);
-
- // verify signature on the whole blob
- CssmData signChunk[] = {
- CssmData(blob->data(), offsetof(DbBlob, 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; // BSafe bug compatibility
-#endif
- VerifyMac verifier(Server::csp(), verifyAlgorithm);
- verifier.key(mSigningKey);
- verifier.verify(signChunk, 2, CssmData(blob->blobSignature, sizeof(blob->blobSignature)));
-
- // all checks out; start extracting fields
- this->mEncryptionKey = mEncryptionKey;
- this->mSigningKey = mSigningKey;
- if (privateAclBlob) {
- // extract private ACL blob as a separately allocated area
- uint32 blobLength = decryptedBlob.length() - sizeof(DbBlob::PrivateBlob);
- *privateAclBlob = CssmAllocator::standard().malloc(blobLength);
- memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength);
- }
-
- // secrets have been established
- mIsValid = true;
- CssmAllocator::standard().free(privateBlob);
-}
-
-
-//
-// Encode a key blob
-//
-KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
- const CssmData &publicAcl, const CssmData &privateAcl) const
-{
- assert(isValid()); // need our database secrets
-
- // create new IV
- uint8 iv[8];
- Server::active().random(iv);
-
- // extract and hold some header bits the CSP does not want to see
- CssmKey key = inKey;
- uint32 heldAttributes = key.attributes() & managedAttributes;
- key.clearAttribute(managedAttributes);
- key.setAttribute(forcedAttributes);
-
- // use a CMS wrap to encrypt the key
- 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));
- CssmKey wrappedKey;
- wrap(key, wrappedKey, &privateAcl);
-
- // stick the held attribute bits back in
- key.clearAttribute(forcedAttributes);
- key.setAttribute(heldAttributes);
-
- // allocate the final KeyBlob, uh, blob
- size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length();
- KeyBlob *blob = CssmAllocator::standard().malloc<KeyBlob>(length);
-
- // assemble the KeyBlob
- memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps
- blob->initialize();
- memcpy(blob->iv, iv, sizeof(iv));
- blob->header = key.header();
- h2ni(blob->header); // endian-correct the 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) + publicAcl.length();
- memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length());
- blob->totalLength = blob->startCryptoBlob + wrappedKey.length();
-
- // sign the blob
- CssmData signChunk[] = {
- CssmData(blob->data(), offsetof(KeyBlob, blobSignature)),
- CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
- };
- CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
- GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY); //@@@!!! CRUD
- signer.key(mSigningKey);
- signer.sign(signChunk, 2, signature);
- assert(signature.length() == sizeof(blob->blobSignature));
-
- // all done. Clean up
- Server::csp()->allocator().free(wrappedKey);
- return blob;
-}
-
-
-//
-// Decode a key blob
-//
-void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
- CssmKey &key, void * &pubAcl, void * &privAcl) const
-{
- assert(isValid()); // need our database secrets
-
- // Assemble the encrypted blob as a CSSM "wrapped key"
- 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());
-
- // verify signature (check against corruption)
- CssmData signChunk[] = {
- CssmData::wrap(blob, offsetof(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; // BSafe bug compatibility
-#endif
- VerifyMac verifier(Server::csp(), verifyAlgorithm);
- verifier.key(mSigningKey);
- CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
- verifier.verify(signChunk, 2, signature);
-
- // extract and hold some header bits the CSP does not want to see
- uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes;
-
- // decrypt the key using an unwrapping operation
- 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));
- CssmData privAclData;
- wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?)
- unwrap(wrappedKey,
- KeySpec(n2h(blob->header.usage()),
- (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
- key, &privAclData);
-
- // compare retrieved key headers with blob headers (sanity check)
- // @@@ this should probably be checked over carefully
- 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);
-
- // re-insert held bits
- key.header().KeyAttr |= heldAttributes;
-
- // got a valid key: return the pieces
- pubAcl = blob->publicAclBlob(); // points into blob (shared)
- privAcl = privAclData; // was allocated by CSP decrypt
- // key was set by unwrap operation
-}
-
-
-//
-// Derive the blob-specific database blob encryption key from the passphrase and the salt.
-//
-CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const
-{
- // derive an encryption key and IV from passphrase and salt
- CssmClient::DeriveKey makeKey(Server::csp(),
- CSSM_ALGID_PKCS5_PBKDF2, CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
- makeKey.iterationCount(1000);
- makeKey.salt(CssmData::wrap(mSalt));
- 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));
-}
-
-
-//
-// Turn raw keybits into a symmetric key in the CSP
-//
-CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
- CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
-{
- // build a fake key
- 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);
-
- // unwrap it into the CSP (but keep it raw)
- 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);
-}