// // 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 = Allocator::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(), fieldOffsetOf(&DbBlob::blobSignature)), CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length()) }; CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY); 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(const 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 = 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>(); // 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::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; // BSafe bug compatibility #endif VerifyMac verifier(Server::csp(), verifyAlgorithm); verifier.key(mSigningKey); verifier.verify(signChunk, 2, CssmData::wrap(blob->blobSignature)); // all checks out; start extracting fields if (privateAclBlob) { // extract private ACL blob as a separately allocated area uint32 blobLength = (uint32) decryptedBlob.length() - sizeof(DbBlob::PrivateBlob); *privateAclBlob = Allocator::standard().malloc(blobLength); memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength); } // secrets have been established mBlobVersion = blob->version(); mIsValid = true; Allocator::standard().free(privateBlob); }
// // Decode a key blob // void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl) const { // 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()); bool inTheClear = blob->isClearText(); if(!inTheClear) { // verify signature (check against corruption) assert(isValid()); // need our database secrets 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; // BSafe bug compatibility #endif VerifyMac verifier(Server::csp(), verifyAlgorithm); verifier.key(mSigningKey); CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); verifier.verify(signChunk, 2, signature); } /* else signature indicates cleartext */ // extract and hold some header bits the CSP does not want to see uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes; CssmData privAclData; if(inTheClear) { /* NULL unwrap */ UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?) unwrap(wrappedKey, KeySpec(n2h(blob->header.usage()), (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes), key, &privAclData); } else { // 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)); 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; if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) { /* Spoof - cleartext KeyBlob passed off as private key */ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); } // got a valid key: return the pieces pubAcl = blob->publicAclBlob(); // points into blob (shared) privAcl = privAclData; // was allocated by CSP decrypt, else NULL for // cleatext keys // key was set by unwrap operation }
// // Encode a key blob // 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)) { /* can't store private ACL component in the clear */ CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS); } // extract and hold some header bits the CSP does not want to see uint32 heldAttributes = key.attributes() & managedAttributes; key.clearAttribute(managedAttributes); key.setAttribute(forcedAttributes); if(inTheClear) { /* NULL wrap of public key */ WrapKey wrap(Server::csp(), CSSM_ALGID_NONE); wrap(key, wrappedKey, NULL); } else { assert(isValid()); // need our database secrets // create new IV Server::active().random(iv); // 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)); 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 = Allocator::standard().malloc<KeyBlob>(length); // assemble the KeyBlob memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps blob->initialize(); if(!inTheClear) { 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(); if(inTheClear) { /* indicate that this is cleartext for decoding */ blob->setClearTextSignature(); } else { // sign the blob CssmData signChunk[] = { CssmData(blob->data(), fieldOffsetOf(&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; }