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