/ OSX / libsecurity_keychain / lib / SecFDERecoveryAsymmetricCrypto.cpp
SecFDERecoveryAsymmetricCrypto.cpp
  1  /* 
  2   * Copyright (c) 2010-2013 Apple Inc. All Rights Reserved.
  3   * 
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  #include "SecFDERecoveryAsymmetricCrypto.h"
 25  #include "SecBridge.h"
 26  
 27  #include <security_cdsa_client/clclient.h>
 28  #include <security_cdsa_client/wrapkey.h>
 29  #include <security_utilities/cfutilities.h>
 30  
 31  #include <Security/SecCertificate.h>
 32  #include <Security/SecKeychain.h>
 33  #include <Security/SecKeychainSearch.h>
 34  #include <Security/SecKey.h>
 35  
 36  static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader);
 37  static CFDataRef CF_RETURNS_RETAINED decodePrivateKeyHeader(SecKeychainRef keychainName, const FVPrivateKeyHeader &inHeader);
 38  static void throwIfError(CSSM_RETURN rv);
 39  
 40  #pragma mark ----- Public SPI -----
 41  
 42  int SecFDERecoveryWrapCRSKWithPubKey(const uint8_t *crsk, size_t crskLen, 
 43  	SecCertificateRef certificateRef, FVPrivateKeyHeader *outHeader)
 44  {
 45  	BEGIN_SECAPI
 46  
 47  	CssmData inBlob((unsigned char *)crsk, (size_t)crskLen);
 48  	Required(certificateRef);
 49  	CFRef<CFDataRef> certificateData(SecCertificateCopyData(certificateRef));
 50  	encodePrivateKeyHeader(inBlob, certificateData, Required(outHeader));
 51  
 52  	END_SECAPI
 53  }
 54  
 55  CFDataRef SecFDERecoveryUnwrapCRSKWithPrivKey(SecKeychainRef keychain, const FVPrivateKeyHeader *inHeader)
 56  {
 57  	CFDataRef result = NULL;
 58  	OSStatus __secapiresult = 0;
 59  
 60  	try
 61  	{
 62  		result = decodePrivateKeyHeader(keychain, Required(inHeader));
 63  	}
 64  	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
 65  	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
 66  	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
 67  	catch (...) { __secapiresult=errSecInternalComponent; }
 68  	secinfo("FDERecovery", "SecFDERecoveryUnwrapCRSKWithPrivKey: %d", (int)__secapiresult);
 69  	return result;
 70  }
 71  
 72  
 73  #pragma mark ----- Asymmetric Crypto -----
 74  
 75  static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader)
 76  {
 77  	CssmClient::CL cl(gGuidAppleX509CL);
 78  	const CssmData cert(const_cast<UInt8 *>(CFDataGetBytePtr(certificate)), CFDataGetLength(certificate));
 79  	CSSM_KEY_PTR key;
 80  	if (CSSM_RETURN rv = CSSM_CL_CertGetKeyInfo(cl->handle(), &cert, &key))
 81  		CssmError::throwMe(rv);
 82  	
 83  	Security::CssmClient::CSP fCSP(gGuidAppleCSP);
 84  
 85  	// Set it up so the cl is used to free key and key->KeyData
 86  	// CssmAutoData _keyData(cl.allocator());
 87  	// _keyData.set(CssmData::overlay(key->KeyData));
 88  	CssmAutoData _key(cl.allocator());
 89  	_key.set(reinterpret_cast<uint8 *>(key), sizeof(*key));
 90  	CssmClient::Key cKey(fCSP, *key);
 91  	
 92  	/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the 
 93  		* associated key blob. 
 94  		* Key is specified in CSSM_CSP_CreatePassThroughContext.
 95  		* Hash is allocated by the CSP, in the App's memory, and returned
 96  		* in *outData. */
 97  	CssmClient::PassThrough passThrough(fCSP);
 98  	passThrough.key(key);
 99  	void *outData;
100  	passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
101  	CssmData *cssmData = reinterpret_cast<CssmData *>(outData);
102  	
103  	outHeader.publicKeyHashSize = (uint32_t)cssmData->Length;
104  	if (outHeader.publicKeyHashSize > sizeof(outHeader.publicKeyHash)) {
105  		secinfo("FDERecovery", "encodePrivateKeyHeader: publicKeyHash too big: %d", outHeader.publicKeyHashSize);
106  		outHeader.publicKeyHashSize = 0; /* failed to copy hash value */
107  	} else {
108  		memcpy(outHeader.publicKeyHash, cssmData->Data, outHeader.publicKeyHashSize);
109  	}
110  	fCSP.allocator().free(cssmData->Data);
111  	fCSP.allocator().free(cssmData);
112  	
113  	/* Now encrypt the blob with the public key. */
114      CssmClient::Encrypt encrypt(fCSP, key->KeyHeader.AlgorithmId);
115  	encrypt.key(cKey);
116  	CssmData clearBuf(outHeader.encryptedBlob, sizeof(outHeader.encryptedBlob));
117  	CssmAutoData remData(fCSP.allocator());
118  	encrypt.padding(CSSM_PADDING_PKCS1);
119  	
120  	outHeader.encryptedBlobSize = (uint32_t)encrypt.encrypt(inBlob, clearBuf, remData.get());
121  	if (outHeader.encryptedBlobSize > sizeof(outHeader.encryptedBlob))
122  		secinfo("FDERecovery", "encodePrivateKeyHeader: encrypted blob too big: %d", outHeader.encryptedBlobSize);
123  }
124  
125  CFDataRef decodePrivateKeyHeader(SecKeychainRef keychain, const FVPrivateKeyHeader &inHeader)
126  {	
127  	// kSecKeyLabel is defined in libsecurity_keychain/lib/SecKey.h
128  	SecKeychainAttribute attrs[] =
129  	{
130  		{ 6 /* kSecKeyLabel */, inHeader.publicKeyHashSize, const_cast<uint8 *>(inHeader.publicKeyHash) }
131  	};
132  	SecKeychainAttributeList attrList =
133  	{
134  		sizeof(attrs) / sizeof(SecKeychainAttribute),
135  		attrs
136  	};
137  	CSSM_CSP_HANDLE cspHandle = 0;
138  	const CSSM_KEY *cssmKey = NULL;
139      const CSSM_ACCESS_CREDENTIALS *accessCred = NULL;
140      CSSM_CC_HANDLE cc = 0;
141  	
142  	SecKeychainSearchRef _searchRef;
143  	throwIfError(SecKeychainSearchCreateFromAttributes(keychain, (SecItemClass) CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &_searchRef));
144  	CFRef<SecKeychainSearchRef> searchRef(_searchRef);
145  	
146  	SecKeychainItemRef _item;
147      if (SecKeychainSearchCopyNext(searchRef, &_item) != 0) {
148  		return NULL;  // XXX possibly should throw here?
149      }
150  	
151  	CFRef<SecKeyRef> keyItem(reinterpret_cast<SecKeyRef>(_item));
152  	throwIfError(SecKeyGetCSPHandle(keyItem, &cspHandle));
153  	throwIfError(SecKeyGetCSSMKey(keyItem, &cssmKey));
154      throwIfError(SecKeyGetCredentials(keyItem, CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault, &accessCred));
155      throwIfError(CSSM_CSP_CreateAsymmetricContext(cspHandle, cssmKey->KeyHeader.AlgorithmId, accessCred, cssmKey, CSSM_PADDING_PKCS1, &cc));
156  	CFDataRef result;
157  	
158  	try
159  	{		
160  		CssmMemoryFunctions memFuncs;
161  		throwIfError(CSSM_GetAPIMemoryFunctions(cspHandle, &memFuncs));
162  		CssmMemoryFunctionsAllocator allocator(memFuncs);
163  		
164  		const CssmData cipherBuf(const_cast<uint8 *>(inHeader.encryptedBlob), inHeader.encryptedBlobSize);
165  		CssmAutoData clearBuf(allocator);
166  		CssmAutoData remData(allocator);
167  		size_t bytesDecrypted;
168  		CSSM_RETURN crx = CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get());
169  		secinfo("FDERecovery", "decodePrivateKeyHeader: CSSM_DecryptData result: %d", crx);
170  		throwIfError(crx);
171  //		throwIfError(CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get()));
172  		clearBuf.length(bytesDecrypted);
173  //		rawKey.copy(clearBuf.get());
174  		result = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)clearBuf.get().data(), clearBuf.get().length());
175  //		result = parseKeyBlob(clearBuf.get());
176  	}
177  	catch(...)
178  	{
179  		CSSM_DeleteContext(cc);
180  		throw;
181  	}
182  	
183  	throwIfError(CSSM_DeleteContext(cc));
184  	
185  	return result;
186  }
187  
188  static void throwIfError(CSSM_RETURN rv)
189  {
190  	if (rv)
191  		CssmError::throwMe(rv);
192  }
193