]> git.saurik.com Git - apple/security.git/blobdiff - Keychain/KeyItem.cpp
Security-54.1.3.tar.gz
[apple/security.git] / Keychain / KeyItem.cpp
index bfb10ec0edea4479b0e54150fdece00265f13eb8..6404a06c4c29503a0c78d10595dcfbfb1facb1ba 100644 (file)
 //
 #include <Security/KeyItem.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <Security/cssmtype.h>
+#include <Security/Access.h>
+#include <Security/Keychains.h>
+#include <Security/KeyItem.h>
+#include <Security/wrapkey.h>
+#include <Security/globals.h>
+
+// @@@ This needs to be shared.
+static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
+static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB);
 
 using namespace KeychainCore;
 
@@ -96,3 +106,361 @@ KeyItem::cssmKey()
 
        return *mKey;
 }
+
+const AccessCredentials *
+KeyItem::getCredentials(
+       CSSM_ACL_AUTHORIZATION_TAG operation,
+       SecCredentialType credentialType)
+{
+       // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
+       AclFactory factory;
+       switch (credentialType)
+       {
+       case kSecCredentialTypeDefault:
+               return globals().credentials();
+       case kSecCredentialTypeWithUI:
+               return factory.promptCred();
+       case kSecCredentialTypeNoUI:
+               return factory.nullCred();
+       default:
+               MacOSError::throwMe(paramErr);
+       }
+}
+
+void 
+KeyItem::createPair(
+       Keychain keychain,
+       CSSM_ALGORITHMS algorithm,
+       uint32 keySizeInBits,
+       CSSM_CC_HANDLE contextHandle,
+       CSSM_KEYUSE publicKeyUsage,
+       uint32 publicKeyAttr,
+       CSSM_KEYUSE privateKeyUsage,
+       uint32 privateKeyAttr,
+       RefPointer<Access> initialAccess,
+       RefPointer<KeyItem> &outPublicKey, 
+       RefPointer<KeyItem> &outPrivateKey)
+{
+       bool freeKeys = false;
+       bool deleteContext = false;
+
+       if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
+               MacOSError::throwMe(errSecInvalidKeychain);
+
+       SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+       CssmClient::CSP csp(keychain->csp());
+       CssmClient::CSP appleCsp(gGuidAppleCSP);
+
+       // Generate a random label to use initially
+       CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
+       uint8 labelBytes[20];
+       CssmData label(labelBytes, sizeof(labelBytes));
+       random.generate(label, label.Length);
+
+       // Create a Access::Maker for the initial owner of the private key.
+       ResourceControlContext rcc;
+       memset(&rcc, 0, sizeof(rcc));
+       Access::Maker maker;
+       // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.  Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated.
+       maker.initialOwner(rcc);
+       // Create the cred we need to manipulate the keys until we actually set a new access control for them.
+       const AccessCredentials *cred = maker.cred();
+
+       CSSM_KEY publicCssmKey, privateCssmKey;
+       memset(&publicCssmKey, 0, sizeof(publicCssmKey));
+       memset(&privateCssmKey, 0, sizeof(privateCssmKey));
+
+       CSSM_CC_HANDLE ccHandle = 0;
+
+       try
+       {
+               CSSM_RETURN status;
+               if (contextHandle)
+                               ccHandle = contextHandle;
+               else
+               {
+                       status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
+                       if (status)
+                               CssmError::throwMe(status);
+                       deleteContext = true;
+               }
+       
+               CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
+               CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
+               CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
+               status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
+               if (status)
+                       CssmError::throwMe(status);
+       
+               // Generate the keypair
+               status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
+               if (status)
+                       CssmError::throwMe(status);
+               freeKeys = true;
+
+               // Find the keys we just generated in the DL to get SecKeyRef's to them
+               // so we can change the label to be the hash of the public key, and
+               // fix up other attributes.
+
+               // Look up public key in the DLDB.
+               DbAttributes pubDbAttributes;
+               DbUniqueRecord pubUniqueId;
+               SSDbCursor dbPubCursor(ssDb, 1);
+               dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
+               dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
+               CssmClient::Key publicKey;
+               if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
+                       MacOSError::throwMe(errSecItemNotFound);
+
+               // Look up private key in the DLDB.
+               DbAttributes privDbAttributes;
+               DbUniqueRecord privUniqueId;
+               SSDbCursor dbPrivCursor(ssDb, 1);
+               dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
+               dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
+               CssmClient::Key privateKey;
+               if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
+                       MacOSError::throwMe(errSecItemNotFound);
+
+               // Convert reference public key to a raw key so we can use it
+               // in the appleCsp.
+               CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
+               wrap.cred(cred);
+               CssmClient::Key rawPubKey = wrap(publicKey);
+
+               // Calculate the hash of the public key using the appleCSP.
+               CssmClient::PassThrough passThrough(appleCsp);
+               void *outData;
+               CssmData *cssmData;
+
+               /* 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 bythe CSP, in the App's memory, and returned
+               * in *outData. */
+               passThrough.key(rawPubKey);
+               passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
+               cssmData = reinterpret_cast<CssmData *>(outData);
+               CssmData &pubKeyHash = *cssmData;
+
+               std::string description(initialAccess->promptDescription());
+               // Set the label of the public key to the public key hash.
+               // Set the PrintName of the public key to the description in the acl.
+               pubDbAttributes.add(kSecKeyLabel, pubKeyHash);
+               pubDbAttributes.add(kSecKeyPrintName, description);
+               pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+
+               // Set the label of the private key to the public key hash.
+               // Set the PrintName of the private key to the description in the acl.
+               privDbAttributes.add(kSecKeyLabel, pubKeyHash);
+               privDbAttributes.add(kSecKeyPrintName, description);
+               privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+
+               // @@@ Not exception safe!
+               csp.allocator().free(cssmData->Data);
+               csp.allocator().free(cssmData);
+
+               // Finally fix the acl and owner of the private key to the specified access control settings.
+               initialAccess->setAccess(*privateKey, maker);
+
+               // Make the public key acl completely open
+               Access pubKeyAccess;
+               pubKeyAccess.setAccess(*publicKey, maker);
+
+               // Create keychain items which will represent the keys.
+               outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
+               outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
+       }
+       catch (...)
+       {
+               if (freeKeys)
+               {
+                       // Delete the keys if something goes wrong so we don't end up with inaccesable keys in the database.
+                       CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
+                       CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
+               }
+               
+               if (deleteContext)
+                       CSSM_DeleteContext(ccHandle);
+
+               throw;
+       }
+
+       if (freeKeys)
+       {
+               CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
+               CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
+       }
+
+       if (deleteContext)
+               CSSM_DeleteContext(ccHandle);
+}
+
+void
+KeyItem::importPair(
+       Keychain keychain,
+       const CSSM_KEY &publicWrappedKey,
+       const CSSM_KEY &privateWrappedKey,
+       RefPointer<Access> initialAccess,
+       RefPointer<KeyItem> &outPublicKey, 
+       RefPointer<KeyItem> &outPrivateKey)
+{
+       bool freePublicKey = false;
+       bool freePrivateKey = false;
+       bool deleteContext = false;
+
+       if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
+               MacOSError::throwMe(errSecInvalidKeychain);
+
+       SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+       CssmClient::CSP csp(keychain->csp());
+       CssmClient::CSP appleCsp(gGuidAppleCSP);
+
+       // Create a Access::Maker for the initial owner of the private key.
+       ResourceControlContext rcc;
+       memset(&rcc, 0, sizeof(rcc));
+       Access::Maker maker;
+       // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.  Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated.
+       maker.initialOwner(rcc);
+       // Create the cred we need to manipulate the keys until we actually set a new access control for them.
+       const AccessCredentials *cred = maker.cred();
+
+       CSSM_KEY publicCssmKey, privateCssmKey;
+       memset(&publicCssmKey, 0, sizeof(publicCssmKey));
+       memset(&privateCssmKey, 0, sizeof(privateCssmKey));
+
+       CSSM_CC_HANDLE ccHandle = 0;
+
+       try
+       {
+               CSSM_RETURN status;
+
+               // Calculate the hash of the public key using the appleCSP.
+               CssmClient::PassThrough passThrough(appleCsp);
+               void *outData;
+               CssmData *cssmData;
+
+               /* 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 bythe CSP, in the App's memory, and returned
+               * in *outData. */
+               passThrough.key(&publicWrappedKey);
+               passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
+               cssmData = reinterpret_cast<CssmData *>(outData);
+               CssmData &pubKeyHash = *cssmData;
+
+               status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.AlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
+               if (status)
+                       CssmError::throwMe(status);
+               deleteContext = true;
+       
+               CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
+               CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
+               CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
+               status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
+               if (status)
+                       CssmError::throwMe(status);
+       
+               // Unwrap the the keys
+               status = CSSM_UnwrapKey(
+                       ccHandle,
+                       NULL,
+                       &publicWrappedKey,
+                       publicWrappedKey.KeyHeader.KeyUsage,
+                       publicWrappedKey.KeyHeader.KeyAttr,
+                       &pubKeyHash,
+                       &rcc,
+                       &publicCssmKey,
+                       NULL);
+               if (status)
+                       CssmError::throwMe(status);
+               freePublicKey = true;
+
+               status = CSSM_UnwrapKey(
+                       ccHandle,
+                       NULL,
+                       &privateWrappedKey,
+                       privateWrappedKey.KeyHeader.KeyUsage,
+                       privateWrappedKey.KeyHeader.KeyAttr,
+                       &pubKeyHash,
+                       &rcc,
+                       &privateCssmKey,
+                       NULL);
+
+               if (status)
+                       CssmError::throwMe(status);
+
+               freePrivateKey = true;
+
+               // Find the keys we just generated in the DL to get SecKeyRef's to them
+               // so we can change the label to be the hash of the public key, and
+               // fix up other attributes.
+
+               // Look up public key in the DLDB.
+               DbAttributes pubDbAttributes;
+               DbUniqueRecord pubUniqueId;
+               SSDbCursor dbPubCursor(ssDb, 1);
+               dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
+               dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
+               CssmClient::Key publicKey;
+               if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
+                       MacOSError::throwMe(errSecItemNotFound);
+
+               // Look up private key in the DLDB.
+               DbAttributes privDbAttributes;
+               DbUniqueRecord privUniqueId;
+               SSDbCursor dbPrivCursor(ssDb, 1);
+               dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
+               dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
+               CssmClient::Key privateKey;
+               if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
+                       MacOSError::throwMe(errSecItemNotFound);
+
+               // @@@ Not exception safe!
+               csp.allocator().free(cssmData->Data);
+               csp.allocator().free(cssmData);
+
+               std::string description(initialAccess->promptDescription());
+               // Set the label of the public key to the public key hash.
+               // Set the PrintName of the public key to the description in the acl.
+               pubDbAttributes.add(kSecKeyPrintName, description);
+               pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+
+               // Set the label of the private key to the public key hash.
+               // Set the PrintName of the private key to the description in the acl.
+               privDbAttributes.add(kSecKeyPrintName, description);
+               privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+
+               // Finally fix the acl and owner of the private key to the specified access control settings.
+               initialAccess->setAccess(*privateKey, maker);
+
+               // Make the public key acl completely open
+               Access pubKeyAccess;
+               pubKeyAccess.setAccess(*publicKey, maker);
+
+               // Create keychain items which will represent the keys.
+               outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
+               outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
+       }
+       catch (...)
+       {
+               if (freePublicKey)
+                       CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
+               if (freePrivateKey)
+                       CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
+               
+               if (deleteContext)
+                       CSSM_DeleteContext(ccHandle);
+
+               throw;
+       }
+
+       if (freePublicKey)
+               CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
+       if (freePrivateKey)
+               CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
+
+       if (deleteContext)
+               CSSM_DeleteContext(ccHandle);
+}