SecFDERecoveryAsymmetricCrypto.cpp   [plain text]


/* 
 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include "SecFDERecoveryAsymmetricCrypto.h"
#include "SecBridge.h"

#include <security_cdsa_client/clclient.h>
#include <security_cdsa_client/wrapkey.h>
#include <security_utilities/cfutilities.h>

#include <Security/SecCertificate.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainSearch.h>
#include <Security/SecKey.h>

static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader);
static CFDataRef decodePrivateKeyHeader(SecKeychainRef keychainName, const FVPrivateKeyHeader &inHeader);
static void throwIfError(CSSM_RETURN rv);

#pragma mark ----- Public SPI -----

int SecFDERecoveryWrapCRSKWithPubKey(const uint8_t *crsk, size_t crskLen, 
	SecCertificateRef certificateRef, FVPrivateKeyHeader *outHeader)
{
	BEGIN_SECAPI

	CssmData inBlob((unsigned char *)crsk, (size_t)crskLen);
	Required(certificateRef);
	CFRef<CFDataRef> certificateData(SecCertificateCopyData(certificateRef));
	encodePrivateKeyHeader(inBlob, certificateData, Required(outHeader));

	END_SECAPI
}

CFDataRef SecFDERecoveryUnwrapCRSKWithPrivKey(SecKeychainRef keychain, const FVPrivateKeyHeader *inHeader)
{
	CFDataRef result = NULL;
	OSStatus __secapiresult;

	try
	{
		result = decodePrivateKeyHeader(keychain, Required(inHeader));
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=memFullErr; }
	catch (...) { __secapiresult=internalComponentErr; }
	secdebug("FDERecovery", "SecFDERecoveryUnwrapCRSKWithPrivKey: %ld", __secapiresult);
	return result;
}


#pragma mark ----- Asymmetric Crypto -----

static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader)
{
	CssmClient::CL cl(gGuidAppleX509CL);
	const CssmData cert(const_cast<UInt8 *>(CFDataGetBytePtr(certificate)), CFDataGetLength(certificate));
	CSSM_KEY_PTR key;
	if (CSSM_RETURN rv = CSSM_CL_CertGetKeyInfo(cl->handle(), &cert, &key))
		CssmError::throwMe(rv);
	
	Security::CssmClient::CSP fCSP(gGuidAppleCSP);

	// Set it up so the cl is used to free key and key->KeyData
	// CssmAutoData _keyData(cl.allocator());
	// _keyData.set(CssmData::overlay(key->KeyData));
	CssmAutoData _key(cl.allocator());
	_key.set(reinterpret_cast<uint8 *>(key), sizeof(*key));
	CssmClient::Key cKey(fCSP, *key);
	
	/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the 
		* associated key blob. 
		* Key is specified in CSSM_CSP_CreatePassThroughContext.
		* Hash is allocated by the CSP, in the App's memory, and returned
		* in *outData. */
	CssmClient::PassThrough passThrough(fCSP);
	passThrough.key(key);
	void *outData;
	passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
	CssmData *cssmData = reinterpret_cast<CssmData *>(outData);
	
	assert(cssmData->Length <= sizeof(outHeader.publicKeyHash));
	outHeader.publicKeyHashSize = cssmData->Length;
	memcpy(outHeader.publicKeyHash, cssmData->Data, cssmData->Length);
	fCSP.allocator().free(cssmData->Data);
	fCSP.allocator().free(cssmData);
	
	/* Now encrypt the blob with the public key. */
    CssmClient::Encrypt encrypt(fCSP, key->KeyHeader.AlgorithmId);
	encrypt.key(cKey);
	CssmData clearBuf(outHeader.encryptedBlob, sizeof(outHeader.encryptedBlob));
	CssmAutoData remData(fCSP.allocator());
	encrypt.padding(CSSM_PADDING_PKCS1);
	
	outHeader.encryptedBlobSize = encrypt.encrypt(inBlob, clearBuf, remData.get());
	if (outHeader.encryptedBlobSize > sizeof(outHeader.encryptedBlob))
		secdebug("FDERecovery", "encodePrivateKeyHeader: encrypted blob too big: %d", outHeader.encryptedBlobSize);
}

CFDataRef decodePrivateKeyHeader(SecKeychainRef keychain, const FVPrivateKeyHeader &inHeader)
{	
	// kSecKeyLabel is defined in libsecurity_keychain/lib/SecKey.h
	SecKeychainAttribute attrs[] =
	{
		{ 6 /* kSecKeyLabel */, inHeader.publicKeyHashSize, const_cast<uint8 *>(inHeader.publicKeyHash) }
	};
	SecKeychainAttributeList attrList =
	{
		sizeof(attrs) / sizeof(SecKeychainAttribute),
		attrs
	};
	CSSM_CSP_HANDLE cspHandle = 0;
	const CSSM_KEY *cssmKey = NULL;
    const CSSM_ACCESS_CREDENTIALS *accessCred = NULL;
    CSSM_CC_HANDLE cc = 0;
	
	SecKeychainSearchRef _searchRef;
	throwIfError(SecKeychainSearchCreateFromAttributes(keychain, CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &_searchRef));
	CFRef<SecKeychainSearchRef> searchRef(_searchRef);
	
	SecKeychainItemRef _item;
	if (SecKeychainSearchCopyNext(searchRef, &_item))
		return false;
	
	CFRef<SecKeyRef> keyItem(reinterpret_cast<SecKeyRef>(_item));
	throwIfError(SecKeyGetCSPHandle(keyItem, &cspHandle));
	throwIfError(SecKeyGetCSSMKey(keyItem, &cssmKey));
    throwIfError(SecKeyGetCredentials(keyItem, CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault, &accessCred));
    throwIfError(CSSM_CSP_CreateAsymmetricContext(cspHandle, cssmKey->KeyHeader.AlgorithmId, accessCred, cssmKey, CSSM_PADDING_PKCS1, &cc));
	CFDataRef result;
	
	try
	{		
		CssmMemoryFunctions memFuncs;
		throwIfError(CSSM_GetAPIMemoryFunctions(cspHandle, &memFuncs));
		CssmMemoryFunctionsAllocator allocator(memFuncs);
		
		const CssmData cipherBuf(const_cast<uint8 *>(inHeader.encryptedBlob), inHeader.encryptedBlobSize);
		CssmAutoData clearBuf(allocator);
		CssmAutoData remData(allocator);
		size_t bytesDecrypted;
		CSSM_RETURN crx = CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get());
		secdebug("FDERecovery", "decodePrivateKeyHeader: CSSM_DecryptData result: %d", crx);
		throwIfError(crx);
//		throwIfError(CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get()));
		clearBuf.length(bytesDecrypted);
//		rawKey.copy(clearBuf.get());
		result = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)clearBuf.get().data(), clearBuf.get().length());
//		result = parseKeyBlob(clearBuf.get());
	}
	catch(...)
	{
		CSSM_DeleteContext(cc);
		throw;
	}
	
	throwIfError(CSSM_DeleteContext(cc));
	
	return result;
}

static void throwIfError(CSSM_RETURN rv)
{
	if (rv)
		CssmError::throwMe(rv);
}