summaryrefslogtreecommitdiffstats
path: root/base/crypto
diff options
context:
space:
mode:
authorhawk@chromium.org <hawk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 21:05:01 +0000
committerhawk@chromium.org <hawk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 21:05:01 +0000
commit0691bdf48f7ae1af0f5490da1fab0b644068e0f1 (patch)
treeeddf39b179a75608c92e4719c7537266cb0f362c /base/crypto
parentaeba30900935cfbb729e3124e8e2151a581028d9 (diff)
downloadchromium_src-0691bdf48f7ae1af0f5490da1fab0b644068e0f1.zip
chromium_src-0691bdf48f7ae1af0f5490da1fab0b644068e0f1.tar.gz
chromium_src-0691bdf48f7ae1af0f5490da1fab0b644068e0f1.tar.bz2
Implement RSAPrivateKey for Mac OS X
TEST=none BUG=none Review URL: http://codereview.chromium.org/243070 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27897 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/crypto')
-rw-r--r--base/crypto/rsa_private_key.h8
-rw-r--r--base/crypto/rsa_private_key_mac.cc398
2 files changed, 405 insertions, 1 deletions
diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h
index 3d85ebb..a6b0e20 100644
--- a/base/crypto/rsa_private_key.h
+++ b/base/crypto/rsa_private_key.h
@@ -11,7 +11,7 @@
#include <cryptoht.h>
#include <keythi.h>
#elif defined(OS_MACOSX)
-// TODO(port);
+#include <Security/cssm.h>
#elif defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
@@ -43,6 +43,9 @@ class RSAPrivateKey {
#elif defined(OS_WIN)
HCRYPTPROV provider() { return provider_; }
HCRYPTKEY key() { return key_; }
+#elif defined(OS_MACOSX)
+ CSSM_CSP_HANDLE csp_handle() { return csp_handle_; }
+ CSSM_KEY_PTR key() { return &key_; }
#endif
// Exports the private key to a PKCS #1 PrivateKey block.
@@ -64,6 +67,9 @@ private:
HCRYPTPROV provider_;
HCRYPTKEY key_;
+#elif defined(OS_MACOSX)
+ CSSM_KEY key_;
+ CSSM_CSP_HANDLE csp_handle_;
#endif
DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey);
diff --git a/base/crypto/rsa_private_key_mac.cc b/base/crypto/rsa_private_key_mac.cc
new file mode 100644
index 0000000..0efbd14
--- /dev/null
+++ b/base/crypto/rsa_private_key_mac.cc
@@ -0,0 +1,398 @@
+// Copyright (c) 2009 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 "base/crypto/rsa_private_key.h"
+
+#include <list>
+
+#include "base/crypto/cssm_init.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+
+namespace {
+
+// ASN.1 encoding of the AlgorithmIdentifier from PKCS #8.
+const uint8 kRsaAlgorithmIdentifier[] = {
+ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+ 0x05, 0x00
+};
+
+// ASN.1 tags for some types we use.
+const uint8 kSequenceTag = 0x30;
+const uint8 kIntegerTag = 0x02;
+const uint8 kNullTag = 0x05;
+const uint8 kOctetStringTag = 0x04;
+const uint8 kBitStringTag = 0x03;
+
+// TODO(hawk): Move the App* functions into a shared location,
+// perhaps cssm_init.cc.
+void* AppMalloc(CSSM_SIZE size, void *alloc_ref) {
+ return malloc(size);
+}
+
+void AppFree(void* mem_ptr, void* alloc_ref) {
+ free(mem_ptr);
+}
+
+void* AppRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) {
+ return realloc(ptr, size);
+}
+
+void* AppCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) {
+ return calloc(num, size);
+}
+
+const CSSM_API_MEMORY_FUNCS mem_funcs = {
+ AppMalloc,
+ AppFree,
+ AppRealloc,
+ AppCalloc,
+ NULL
+};
+
+// Helper for error handling during key import.
+#define READ_ASSERT(truth) \
+ if (!(truth)) { \
+ NOTREACHED(); \
+ return false; \
+ }
+
+static void PrependBytesInOrder(uint8* val, int start, int num_bytes,
+ std::list<uint8>* data) {
+ while(num_bytes > start) {
+ --num_bytes;
+ data->push_front(val[num_bytes]);
+ }
+}
+
+// Helper to prepend an ASN.1 length field.
+static void PrependLength(size_t size, std::list<uint8>* data) {
+ // The high bit is used to indicate whether additional octets are needed to
+ // represent the length.
+ if (size < 0x80) {
+ data->push_front(static_cast<uint8>(size));
+ } else {
+ uint8 num_bytes = 0;
+ while (size > 0) {
+ data->push_front(static_cast<uint8>(size & 0xFF));
+ size >>= 8;
+ num_bytes++;
+ }
+ CHECK(num_bytes <= 4);
+ data->push_front(0x80 | num_bytes);
+ }
+}
+
+// Helper to prepend an ASN.1 type header.
+static void PrependTypeHeaderAndLength(uint8 type, uint32 length,
+ std::list<uint8>* output) {
+ PrependLength(length, output);
+ output->push_front(type);
+}
+
+// Helper to prepend an ASN.1 bit string
+static void PrependBitString(uint8* val, int num_bytes,
+ std::list<uint8>* output) {
+ // Start with the data.
+ PrependBytesInOrder(val, 0, num_bytes, output);
+ // Zero unused bits.
+ output->push_front(0);
+ // Add the length.
+ PrependLength(num_bytes + 1, output);
+ // Finally, add the bit string tag.
+ output->push_front(kBitStringTag);
+}
+
+// Helper to prepend an ASN.1 integer.
+static void PrependInteger(uint8* val, int num_bytes, std::list<uint8>* data) {
+ // ASN.1 integers are unpadded byte arrays, so skip any null padding bytes
+ // from the most-significant end of the integer.
+ int start = 0;
+ while (start < (num_bytes - 1) && val[start] == 0x00)
+ start++;
+
+ PrependBytesInOrder(val, start, num_bytes, data);
+
+ // ASN.1 integers are signed. To encode a positive integer whose sign bit
+ // (the most significant bit) would otherwise be set and make the number
+ // negative, ASN.1 requires a leading null byte to force the integer to be
+ // positive.
+ if ((val[start] & 0x80) != 0) {
+ data->push_front(0x00);
+ num_bytes++;
+ }
+
+ PrependTypeHeaderAndLength(kIntegerTag, num_bytes, data);
+}
+
+// Read an ASN.1 length field. This also checks that the length does not extend
+// beyond |end|.
+static bool ReadLength(uint8** pos, uint8* end, uint32* result) {
+ READ_ASSERT(*pos < end);
+ int length = 0;
+
+ // If the MSB is not set, the length is just the byte itself.
+ if (!(**pos & 0x80)) {
+ length = **pos;
+ (*pos)++;
+ } else {
+ // Otherwise, the lower 7 indicate the length of the length.
+ int length_of_length = **pos & 0x7F;
+ READ_ASSERT(length_of_length <= 4);
+ (*pos)++;
+ READ_ASSERT(*pos + length_of_length < end);
+
+ length = 0;
+ for (int i = 0; i < length_of_length; ++i) {
+ length <<= 8;
+ length |= **pos;
+ (*pos)++;
+ }
+ }
+
+ READ_ASSERT(*pos + length <= end);
+ if (result) *result = length;
+ return true;
+}
+
+// Read an ASN.1 type header and its length.
+static bool ReadTypeHeaderAndLength(uint8** pos, uint8* end,
+ uint8 expected_tag, uint32* length) {
+ READ_ASSERT(*pos < end);
+ READ_ASSERT(**pos == expected_tag);
+ (*pos)++;
+
+ return ReadLength(pos, end, length);
+}
+
+// Read an ASN.1 sequence declaration. This consumes the type header and length
+// field, but not the contents of the sequence.
+static bool ReadSequence(uint8** pos, uint8* end) {
+ return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL);
+}
+
+// Read the RSA AlgorithmIdentifier.
+static bool ReadAlgorithmIdentifier(uint8** pos, uint8* end) {
+ READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end);
+ READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier,
+ sizeof(kRsaAlgorithmIdentifier)) == 0);
+ (*pos) += sizeof(kRsaAlgorithmIdentifier);
+ return true;
+}
+
+// Read one of the two version fields in PrivateKeyInfo.
+static bool ReadVersion(uint8** pos, uint8* end) {
+ uint32 length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length))
+ return false;
+
+ // The version should be zero.
+ for (uint32 i = 0; i < length; ++i) {
+ READ_ASSERT(**pos == 0x00);
+ (*pos)++;
+ }
+
+ return true;
+}
+
+// Read an ASN.1 integer.
+static bool ReadInteger(uint8** pos, uint8* end, std::vector<uint8>* out) {
+ uint32 length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length) || !length)
+ return false;
+
+ // The first byte can be zero to force positiveness. We can ignore this.
+ if (**pos == 0x00) {
+ ++(*pos);
+ --length;
+ }
+
+ if (length)
+ out->insert(out->end(), *pos, (*pos) + length);
+
+ (*pos) += length;
+ return true;
+}
+
+} // namespace
+
+namespace base {
+
+// 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(result->csp_handle(), CSSM_ALGID_RSA,
+ num_bits, NULL, NULL, NULL, NULL, NULL,
+ &cc_handle);
+ if (crtn) {
+ NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn;
+ return NULL;
+ }
+
+ CSSM_KEY public_key;
+ memset(&public_key, 0, sizeof(CSSM_KEY));
+ 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,
+ &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;
+ }
+
+ // Public key is not needed.
+ CSSM_FreeKey(result->csp_handle(), NULL, &public_key, CSSM_FALSE);
+
+ return result.release();
+}
+
+// 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(result->csp_handle(), NULL, &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(result->csp_handle(), 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;
+ }
+
+ return result.release();
+}
+
+RSAPrivateKey::RSAPrivateKey() : csp_handle_(0) {
+ memset(&key_, 0, sizeof(key_));
+
+ EnsureCSSMInit();
+
+ static CSSM_VERSION version = {2, 0};
+ CSSM_RETURN crtn;
+ crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &mem_funcs, 0,
+ CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE,
+ NULL, 0, NULL, &csp_handle_);
+ DCHECK(crtn == CSSM_OK);
+}
+
+RSAPrivateKey::~RSAPrivateKey() {
+ if (csp_handle_) {
+ if (key_.KeyData.Data) {
+ CSSM_FreeKey(csp_handle_, NULL, &key_, CSSM_FALSE);
+ }
+ CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_);
+ DCHECK(crtn == CSSM_OK);
+ }
+}
+
+bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) {
+ 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) {
+ if (!key_.KeyData.Data || !key_.KeyData.Length) {
+ return false;
+ }
+
+ // Parse the private key info up to the public key values, ignoring
+ // the subsequent private key values.
+ uint8* src = key_.KeyData.Data;
+ uint8* end = src + key_.KeyData.Length;
+ std::vector<uint8> modulus;
+ std::vector<uint8> public_exponent;
+ if (!ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadAlgorithmIdentifier(&src, end) ||
+ !ReadTypeHeaderAndLength(&src, end, kOctetStringTag, NULL) ||
+ !ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadInteger(&src, end, &modulus) ||
+ !ReadInteger(&src, end, &public_exponent))
+ return false;
+
+ // Create a sequence with the modulus (n) and public exponent (e).
+ std::list<uint8> content;
+ PrependInteger(&public_exponent[0], static_cast<int>(public_exponent.size()),
+ &content);
+ PrependInteger(&modulus[0], static_cast<int>(modulus.size()), &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy the sequence with n and e into a buffer.
+ std::vector<uint8> bit_string;
+ for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i)
+ bit_string.push_back(*i);
+ content.clear();
+ // Add the sequence as the contents of a bit string.
+ PrependBitString(&bit_string[0], static_cast<int>(bit_string.size()),
+ &content);
+
+ // Add the RSA algorithm OID.
+ for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i)
+ content.push_front(kRsaAlgorithmIdentifier[i - 1]);
+
+ // Finally, wrap everything in a sequence.
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everything into the output.
+ output->reserve(content.size());
+ for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i)
+ output->push_back(*i);
+
+ return true;
+}
+
+} // namespace base