X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_apple_csp/lib/wrapKeyCms.cpp diff --git a/libsecurity_apple_csp/lib/wrapKeyCms.cpp b/libsecurity_apple_csp/lib/wrapKeyCms.cpp new file mode 100644 index 00000000..b35ac45e --- /dev/null +++ b/libsecurity_apple_csp/lib/wrapKeyCms.cpp @@ -0,0 +1,468 @@ +/* + * 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. + */ + + +// +// wrapKeyCms.cpp - wrap/unwrap key, CMS format +// + +#include "AppleCSPSession.h" +#include "AppleCSPUtils.h" +#include "AppleCSPKeys.h" +#include "cspdebugging.h" + +/* + * + * Here is the algorithm implemented in this module: + * + * Note that DEK is the wrapping key, + * + * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the + * following concatenation: + * + * 4-byte length of Descriptive Data, big-endian | + * Descriptive Data | + * rawBlob.Data bytes + * + * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with + * PKCS1 padding. Call the ciphertext TEMP1 + * + * 3. Let TEMP2 = IV || TEMP1. + * + * 4. Reverse the order of the octets in TEMP2 call the result TEMP3. + * + * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode + * with PKCS1 padding call the result TEMP4. + * + * TEMP4 is wrappedKey.KeyData. + */ + +/* true: cook up second CCHandle via a new HandleObject + * false - OK to reuse a CCHandle */ +#define USE_SECOND_CCHAND 0 + +/* false : make copy of incoming context before changing IV + * true : resuse OK */ +#define REUSE_CONTEXT 1 + +/* lots'o'printfs in lieu of a debugger which works */ +#define VERBOSE_DEBUG 0 + +static const uint8 magicCmsIv[] = + { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 }; + +#if VERBOSE_DEBUG +static void dumpBuf( + char *title, + const CSSM_DATA *d, + uint32 maxLen) +{ + unsigned i; + uint32 len; + + if(title) { + printf("%s: ", title); + } + if(d == NULL) { + printf("NO DATA\n"); + return; + } + printf("Total Length: %d\n ", d->Length); + len = maxLen; + if(d->Length < len) { + len = d->Length; + } + for(i=0; iData[i]); + if((i % 16) == 15) { + printf("\n "); + } + } + printf("\n"); +} +#else +#define dumpBuf(t, d, m) +#endif /* VERBOSE_DEBUG */ + + +/* serialize/deserialize uint32, big-endian. */ +static void serializeUint32(uint32 i, uint8 *buf) +{ + *buf++ = (uint8)(i >> 24); + *buf++ = (uint8)(i >> 16); + *buf++ = (uint8)(i >> 8); + *buf = (uint8)i; +} + +static uint32 deserializeUint32(const uint8 *buf) { + uint32 result; + + result = ((uint32)buf[0] << 24) | + ((uint32)buf[1] << 16) | + ((uint32)buf[2] << 8) | + (uint32)buf[3]; + return result; +} + +void AppleCSPSession::WrapKeyCms( + CSSM_CC_HANDLE CCHandle, + const Context &context, + const AccessCredentials &AccessCred, + const CssmKey &UnwrappedKey, + CssmData &rawBlob, + bool allocdRawBlob, // callee has to free rawBlob + const CssmData *DescriptiveData, + CssmKey &WrappedKey, + CSSM_PRIVILEGE Privilege) +{ + uint32 ddLen; + CssmData PRIVATE_KEY_BYTES; + #if !REUSE_CONTEXT + Context secondCtx(context.ContextType, context.AlgorithmType); + secondCtx.copyFrom(context, privAllocator); + #endif /* REUSE_CONTEXT */ + + /* + * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the + * following concatenation: + * + * 4-byte length of Descriptive Data, big-endian | + * Descriptive Data | + * rawBlob.Data bytes + */ + dumpBuf("wrap rawBlob", &rawBlob, 24); + dumpBuf("wrap DescriptiveData", DescriptiveData, 24); + + if(DescriptiveData == NULL) { + ddLen = 0; + } + else { + ddLen = DescriptiveData->Length; + } + uint32 pkbLen = 4 + ddLen + rawBlob.Length; + setUpCssmData(PRIVATE_KEY_BYTES, pkbLen, privAllocator); + uint8 *cp = PRIVATE_KEY_BYTES.Data; + serializeUint32(ddLen, cp); + cp += 4; + if(ddLen != 0) { + memcpy(cp, DescriptiveData->Data, ddLen); + cp += ddLen; + } + memcpy(cp, rawBlob.Data, rawBlob.Length); + dumpBuf("wrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES, 48); + + /* 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with + * PKCS1 padding. Call the ciphertext TEMP1 + * + * We'll just use the caller's context for this. Maybe we should + * validate mode, padding, IV? + */ + CssmData TEMP1; + CSSM_SIZE bytesEncrypted; + CssmData remData; + EncryptData(CCHandle, + context, + &PRIVATE_KEY_BYTES, // ClearBufs[] + 1, // ClearBufCount + &TEMP1, // CipherBufs[], + 1, // CipherBufCount, + bytesEncrypted, + remData, + Privilege); + + // I'm not 100% sure about this.... + assert(remData.Length == 0); + TEMP1.Length = bytesEncrypted; + dumpBuf("wrap TEMP1", &TEMP1, 48); + + /* + * 3. Let TEMP2 = IV || TEMP1. + */ + CssmData TEMP2; + CssmData &IV = context.get(CSSM_ATTRIBUTE_INIT_VECTOR, + CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); + setUpCssmData(TEMP2, IV.Length + TEMP1.Length, privAllocator); + memcpy(TEMP2.Data, IV.Data, IV.Length); + memcpy(TEMP2.Data + IV.Length, TEMP1.Data, TEMP1.Length); + dumpBuf("wrap TEMP2", &TEMP2, 56); + + + /* + * 4. Reverse the order of the octets in TEMP2 call the result + * TEMP3. + */ + CssmData TEMP3; + setUpCssmData(TEMP3, TEMP2.Length, privAllocator); + uint8 *cp2 = TEMP2.Data + TEMP2.Length - 1; + cp = TEMP3.Data; + for(uint32 i=0; i + * actually is in the context and not a copy! + */ + #if REUSE_CONTEXT + CssmData &IV2 = context.get(CSSM_ATTRIBUTE_INIT_VECTOR, + CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); + #else + CssmData &IV2 = secondCtx.get(CSSM_ATTRIBUTE_INIT_VECTOR, + CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); + #endif /* REUSE_CONTEXT */ + + uint8 *savedIV = IV2.Data; + uint32 savedIVLen = IV2.Length; + IV2.Data = (uint8 *)magicCmsIv; + IV2.Length = 8; + CssmData &outBlob = CssmData::overlay(WrappedKey.KeyData); + outBlob.Length = 0; + outBlob.Data = NULL; + try { + EncryptData(CCHandle, + #if REUSE_CONTEXT + context, + #else + secondCtx, + #endif /* REUSE_CONTEXT */ + + &TEMP3, // ClearBufs[] + 1, // ClearBufCount + &outBlob, // CipherBufs[], + 1, // CipherBufCount, + bytesEncrypted, + remData, + Privilege); + } + catch (...) { + IV2.Data = savedIV; + IV2.Length = savedIVLen; + throw; // and leak + } + IV2.Data = savedIV; + IV2.Length = savedIVLen; + + // I'm not 100% sure about this.... + assert(remData.Length == 0); + outBlob.Length = bytesEncrypted; + dumpBuf("wrap outBlob", &outBlob, 64); + + /* outgoing header */ + WrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED; + // OK to be zero or not present + WrappedKey.KeyHeader.WrapMode = context.getInt(CSSM_ATTRIBUTE_MODE); + WrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; + + /* free resources */ + freeCssmData(PRIVATE_KEY_BYTES, privAllocator); + freeCssmData(TEMP1, normAllocator); // alloc via encrypt + freeCssmData(TEMP2, privAllocator); + freeCssmData(TEMP3, privAllocator); + if(allocdRawBlob) { + /* our caller mallocd this when dereferencing a ref key */ + freeCssmData(rawBlob, privAllocator); + } +} + +/* note we expect an IV present in the context though we don't use it + * FIXME - we should figure out how to add this attribute at this level + */ + +/* safety trap - don't try to malloc anything bigger than this - we get + * sizes from the processed bit stream.... */ +#define MAX_MALLOC_SIZE 0x10000 + +void AppleCSPSession::UnwrapKeyCms( + CSSM_CC_HANDLE CCHandle, + const Context &Context, + const CssmKey &WrappedKey, + const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry, + CssmKey &UnwrappedKey, + CssmData &DescriptiveData, + CSSM_PRIVILEGE Privilege, + cspKeyStorage keyStorage) +{ + /* + * In reverse order, the steps from wrap... + * + * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode + * with PKCS1 padding call the result TEMP4. + * + * TEMP4 is wrappedKey.KeyData. + */ + const CssmData &wrappedBlob = CssmData::overlay(WrappedKey.KeyData); + dumpBuf("unwrap inBlob", &wrappedBlob, 64); + CssmData &IV1 = Context.get(CSSM_ATTRIBUTE_INIT_VECTOR, + CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); + uint8 *savedIV = IV1.Data; + uint32 savedIvLen = IV1.Length; + IV1.Data = (uint8 *)magicCmsIv; + IV1.Length = 8; + CssmData TEMP3; + CSSM_SIZE bytesDecrypted; + CssmData remData; + + try { + DecryptData(CCHandle, + Context, + &wrappedBlob, // CipherBufs[], + 1, // CipherBufCount, + &TEMP3, // ClearBufs[] + 1, // ClearBufCount + bytesDecrypted, + remData, + Privilege); + } + catch(...) { + IV1.Data = savedIV; + IV1.Length = savedIvLen; + throw; + } + IV1.Data = savedIV; + IV1.Length = savedIvLen; + // I'm not 100% sure about this.... + assert(remData.Length == 0); + TEMP3.Length = bytesDecrypted; + dumpBuf("unwrap TEMP3", &TEMP3, 64); + + /* + * 4. Reverse the order of the octets in TEMP2 call the result + * TEMP3. + * + * i.e., TEMP2 := reverse(TEMP3) + */ + CssmData TEMP2; + setUpCssmData(TEMP2, TEMP3.Length, privAllocator); + uint8 *src = TEMP3.Data + TEMP3.Length - 1; + uint8 *dst = TEMP2.Data; + for(uint32 i=0; i MAX_MALLOC_SIZE) { + dprintf0("UnwrapKeyCms: preposterous ddLen in PRIVATE_KEY_BYTES\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + setUpCssmData(DescriptiveData, ddLen, normAllocator); + memcpy(DescriptiveData.Data, cp1, ddLen); + cp1 += ddLen; + uint32 outBlobLen = PRIVATE_KEY_BYTES.Length - ddLen - 4; + if(ddLen > MAX_MALLOC_SIZE) { + dprintf0("UnwrapKeyCms: preposterous outBlobLen in PRIVATE_KEY_BYTES\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + CssmData &outBlob = CssmData::overlay(UnwrappedKey.KeyData); + setUpCssmData(outBlob, outBlobLen, normAllocator); + memcpy(outBlob.Data, cp1, outBlobLen); + + /* set up outgoing header */ + UnwrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + UnwrappedKey.KeyHeader.Format = inferFormat(UnwrappedKey); + + /* + * Cook up a BinaryKey if caller wants a reference key. + */ + if(keyStorage == CKS_Ref) { + BinaryKey *binKey = NULL; + CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey); + /* optional parameter-bearing key */ + CssmKey *paramKey = Context.get(CSSM_ATTRIBUTE_PARAM_KEY); + provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey); + addRefKey(*binKey, UnwrappedKey); + delete provider; + } + /* free resources */ + freeCssmData(PRIVATE_KEY_BYTES, normAllocator); // alloc via decrypt + freeCssmData(TEMP1, privAllocator); + freeCssmData(IV2, privAllocator); + freeCssmData(TEMP2, privAllocator); + freeCssmData(TEMP3, normAllocator); // via decrypt + +} +