| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "crypto/rsa_private_key.h" | 
 |  | 
 | #include <list> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/scoped_ptr.h" | 
 | #include "crypto/cssm_init.h" | 
 |  | 
 | namespace crypto { | 
 |  | 
 | // static | 
 | RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { | 
 |   scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); | 
 |  | 
 |   CSSM_CC_HANDLE cc_handle; | 
 |   CSSM_RETURN crtn; | 
 |   crtn = CSSM_CSP_CreateKeyGenContext(GetSharedCSPHandle(), CSSM_ALGID_RSA, | 
 |                                       num_bits, NULL, NULL, NULL, NULL, NULL, | 
 |                                       &cc_handle); | 
 |   if (crtn) { | 
 |     NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   CSSM_DATA label = { 9, | 
 |       const_cast<uint8*>(reinterpret_cast<const uint8*>("temp_key")) }; | 
 |   crtn = CSSM_GenerateKeyPair(cc_handle, | 
 |       CSSM_KEYUSE_VERIFY, | 
 |       CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, | 
 |       result->public_key(), CSSM_KEYUSE_SIGN, | 
 |       CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, NULL, | 
 |       result->key()); | 
 |   CSSM_DeleteContext(cc_handle); | 
 |   if (crtn) { | 
 |     NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   return result.release(); | 
 | } | 
 |  | 
 | // static | 
 | RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { | 
 |   NOTIMPLEMENTED(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | // static | 
 | RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( | 
 |     const std::vector<uint8>& input) { | 
 |   if (input.empty()) | 
 |     return NULL; | 
 |  | 
 |   scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); | 
 |  | 
 |   CSSM_KEY key; | 
 |   memset(&key, 0, sizeof(key)); | 
 |   key.KeyData.Data = const_cast<uint8*>(&input.front()); | 
 |   key.KeyData.Length = input.size(); | 
 |   key.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; | 
 |   key.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; | 
 |   key.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; | 
 |   key.KeyHeader.AlgorithmId = CSSM_ALGID_RSA; | 
 |   key.KeyHeader.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; | 
 |   key.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; | 
 |   key.KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; | 
 |  | 
 |   CSSM_KEY_SIZE key_size; | 
 |   CSSM_RETURN crtn; | 
 |   crtn = CSSM_QueryKeySizeInBits( | 
 |       GetSharedCSPHandle(), CSSM_INVALID_HANDLE, &key, &key_size); | 
 |   if (crtn) { | 
 |     NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; | 
 |     return NULL; | 
 |   } | 
 |   key.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; | 
 |  | 
 |   // Perform a NULL unwrap operation on the key so that result's key_ | 
 |   // instance variable points to a key that can be released via CSSM_FreeKey(). | 
 |   CSSM_ACCESS_CREDENTIALS creds; | 
 |   memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); | 
 |   CSSM_CC_HANDLE cc_handle; | 
 |   crtn = CSSM_CSP_CreateSymmetricContext(GetSharedCSPHandle(), CSSM_ALGID_NONE, | 
 |       CSSM_ALGMODE_NONE, &creds, NULL, NULL, CSSM_PADDING_NONE, 0, &cc_handle); | 
 |   if (crtn) { | 
 |     NOTREACHED() << "CSSM_CSP_CreateSymmetricContext failed: " << crtn; | 
 |     return NULL; | 
 |   } | 
 |   CSSM_DATA label_data, desc_data = { 0, NULL }; | 
 |   label_data.Data = | 
 |       const_cast<uint8*>(reinterpret_cast<const uint8*>("unwrapped")); | 
 |   label_data.Length = 9; | 
 |   crtn = CSSM_UnwrapKey(cc_handle, NULL, &key, CSSM_KEYUSE_ANY, | 
 |       CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label_data, | 
 |       NULL, result->key(), &desc_data); | 
 |   if (crtn) { | 
 |     NOTREACHED() << "CSSM_UnwrapKey failed: " << crtn; | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   // Extract a public key from the private key. | 
 |   // Apple doesn't accept CSSM_KEYBLOB_RAW_FORMAT_X509 as a valid key | 
 |   // format when attempting to generate certs, so use PKCS1 instead. | 
 |   PrivateKeyInfoCodec codec(true); | 
 |   std::vector<uint8> private_key_data; | 
 |   private_key_data.assign(key.KeyData.Data, | 
 |                           key.KeyData.Data + key.KeyData.Length); | 
 |   if (!codec.Import(private_key_data)) { | 
 |     return NULL; | 
 |   } | 
 |   std::vector<uint8> public_key_data; | 
 |   if (!codec.ExportPublicKey(&public_key_data)) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   CSSM_KEY* public_key = result->public_key(); | 
 |   size_t size = public_key_data.size(); | 
 |   public_key->KeyData.Data = reinterpret_cast<uint8*>(CSSMMalloc(size)); | 
 |   if (!public_key->KeyData.Data) { | 
 |     NOTREACHED() << "CSSMMalloc failed"; | 
 |     return NULL; | 
 |   } | 
 |   memcpy(public_key->KeyData.Data, &public_key_data.front(), size); | 
 |   public_key->KeyData.Length = size; | 
 |   public_key->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | 
 |   public_key->KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; | 
 |   public_key->KeyHeader.BlobType = CSSM_KEYBLOB_RAW; | 
 |   public_key->KeyHeader.AlgorithmId = CSSM_ALGID_RSA; | 
 |   public_key->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; | 
 |   public_key->KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; | 
 |   public_key->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; | 
 |  | 
 |   crtn = CSSM_QueryKeySizeInBits( | 
 |       GetSharedCSPHandle(), CSSM_INVALID_HANDLE, public_key, &key_size); | 
 |   if (crtn) { | 
 |     DLOG(ERROR) << "CSSM_QueryKeySizeInBits failed " << crtn; | 
 |     return NULL; | 
 |   } | 
 |   public_key->KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; | 
 |  | 
 |   return result.release(); | 
 | } | 
 |  | 
 | // static | 
 | RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( | 
 |     const std::vector<uint8>& input) { | 
 |   NOTIMPLEMENTED(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | // static | 
 | RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( | 
 |     const std::vector<uint8>& input) { | 
 |   NOTIMPLEMENTED(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | RSAPrivateKey::RSAPrivateKey() { | 
 |   memset(&key_, 0, sizeof(key_)); | 
 |   memset(&public_key_, 0, sizeof(public_key_)); | 
 |  | 
 |   EnsureCSSMInit(); | 
 | } | 
 |  | 
 | RSAPrivateKey::~RSAPrivateKey() { | 
 |   if (key_.KeyData.Data) { | 
 |     CSSM_FreeKey(GetSharedCSPHandle(), NULL, &key_, CSSM_FALSE); | 
 |   } | 
 |   if (public_key_.KeyData.Data) { | 
 |     CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key_, CSSM_FALSE); | 
 |   } | 
 | } | 
 |  | 
 | RSAPrivateKey* RSAPrivateKey::Copy() const { | 
 |   std::vector<uint8> key_bytes; | 
 |   if (!ExportPrivateKey(&key_bytes)) | 
 |     return NULL; | 
 |   return CreateFromPrivateKeyInfo(key_bytes); | 
 | } | 
 |  | 
 | bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) const { | 
 |   if (!key_.KeyData.Data || !key_.KeyData.Length) { | 
 |     return false; | 
 |   } | 
 |   output->clear(); | 
 |   output->insert(output->end(), key_.KeyData.Data, | 
 |                  key_.KeyData.Data + key_.KeyData.Length); | 
 |   return true; | 
 | } | 
 |  | 
 | bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) const { | 
 |   PrivateKeyInfoCodec private_key_info(true); | 
 |   std::vector<uint8> private_key_data; | 
 |   private_key_data.assign(key_.KeyData.Data, | 
 |                           key_.KeyData.Data + key_.KeyData.Length); | 
 |   return (private_key_info.Import(private_key_data) && | 
 |           private_key_info.ExportPublicKeyInfo(output)); | 
 | } | 
 |  | 
 | }  // namespace crypto |