diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-10 16:36:34 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-10 16:36:34 +0000 |
commit | db7286d3355528b36de6f0d7fa7f8d9929566511 (patch) | |
tree | 11034e15b9b3da3715b3da7bdb164baf0471e280 /base/crypto | |
parent | e30d979b70b4cb4b8e59f36b20c13a8d7b1257e5 (diff) | |
download | chromium_src-db7286d3355528b36de6f0d7fa7f8d9929566511.zip chromium_src-db7286d3355528b36de6f0d7fa7f8d9929566511.tar.gz chromium_src-db7286d3355528b36de6f0d7fa7f8d9929566511.tar.bz2 |
Implement the signature verification API.
R=erikkay,avi
BUG=6305
Review URL: http://codereview.chromium.org/63089
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13508 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/crypto')
-rw-r--r-- | base/crypto/cssm_init.cc | 72 | ||||
-rw-r--r-- | base/crypto/cssm_init.h | 17 | ||||
-rw-r--r-- | base/crypto/signature_verifier.h | 105 | ||||
-rw-r--r-- | base/crypto/signature_verifier_mac.cc | 143 | ||||
-rw-r--r-- | base/crypto/signature_verifier_nss.cc | 114 | ||||
-rw-r--r-- | base/crypto/signature_verifier_unittest.cc | 259 | ||||
-rw-r--r-- | base/crypto/signature_verifier_win.cc | 148 |
7 files changed, 858 insertions, 0 deletions
diff --git a/base/crypto/cssm_init.cc b/base/crypto/cssm_init.cc new file mode 100644 index 0000000..3483ef9 --- /dev/null +++ b/base/crypto/cssm_init.cc @@ -0,0 +1,72 @@ +// 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/cssm_init.h" + +#include <Security/cssm.h> + +#include "base/logging.h" +#include "base/singleton.h" + +// When writing crypto code for Mac OS X, you may find the following +// documentation useful: +// - Common Security: CDSA and CSSM, Version 2 (with corrigenda) +// http://www.opengroup.org/security/cdsa.htm +// - Apple Cryptographic Service Provider Functional Specification +// - CryptoSample: http://developer.apple.com/SampleCode/CryptoSample/ + +namespace { + +class CSSMInitSingleton { + public: + CSSMInitSingleton() : inited_(false), loaded_(false) { + static CSSM_VERSION version = {2, 0}; + // TODO(wtc): what should our caller GUID be? + static const CSSM_GUID test_guid = { + 0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 } + }; + CSSM_RETURN crtn; + CSSM_PVC_MODE pvc_policy = CSSM_PVC_NONE; + crtn = CSSM_Init(&version, CSSM_PRIVILEGE_SCOPE_NONE, &test_guid, + CSSM_KEY_HIERARCHY_NONE, &pvc_policy, NULL); + if (crtn) { + NOTREACHED(); + return; + } + inited_ = true; + + crtn = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + loaded_ = true; + } + + ~CSSMInitSingleton() { + CSSM_RETURN crtn; + if (loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (inited_) { + crtn = CSSM_Terminate(); + DCHECK(crtn == CSSM_OK); + } + } + + private: + bool inited_; // True if CSSM_Init has been called successfully. + bool loaded_; // True if CSSM_ModuleLoad has been called successfully. +}; + +} // namespace + +namespace base { + +void EnsureCSSMInit() { + Singleton<CSSMInitSingleton>::get(); +} + +} // namespace base diff --git a/base/crypto/cssm_init.h b/base/crypto/cssm_init.h new file mode 100644 index 0000000..20eda50 --- /dev/null +++ b/base/crypto/cssm_init.h @@ -0,0 +1,17 @@ +// 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. + +#ifndef BASE_CRYPTO_CSSM_INIT_H_ +#define BASE_CRYPTO_CSSM_INIT_H_ + +namespace base { + +// Initialize CSSM if it isn't already initialized. This must be called before +// any other CSSM functions. This function is thread-safe, and CSSM will only +// ever be initialized once. CSSM will be properly shut down on program exit. +void EnsureCSSMInit(); + +} // namespace base + +#endif // BASE_CRYPTO_CSSM_INIT_H_ diff --git a/base/crypto/signature_verifier.h b/base/crypto/signature_verifier.h new file mode 100644 index 0000000..c7e532d --- /dev/null +++ b/base/crypto/signature_verifier.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef BASE_CRYPTO_SIGNATURE_VERIFIER_H_ +#define BASE_CRYPTO_SIGNATURE_VERIFIER_H_ + +#include "build/build_config.h" + +#if defined(OS_LINUX) +#include <cryptoht.h> +#elif defined(OS_MACOSX) +#include <Security/cssm.h> +#elif defined(OS_WIN) +#include <windows.h> +#include <wincrypt.h> +#endif + +#include <vector> + +#include "base/basictypes.h" + +namespace base { + +// The SignatureVerifier class verifies a signature using a bare public key +// (as opposed to a certificate). +class SignatureVerifier { + public: + SignatureVerifier(); + ~SignatureVerifier(); + + // Streaming interface: + + // Initiates a signature verification operation. This should be followed + // by one or more VerifyUpdate calls and a VerifyFinal call. + // + // The signature algorithm is specified as a DER encoded ASN.1 + // AlgorithmIdentifier structure: + // AlgorithmIdentifier ::= SEQUENCE {
+ // algorithm OBJECT IDENTIFIER,
+ // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // The signature is encoded according to the signature algorithm, but it + // must not be further encoded in an ASN.1 BIT STRING. + // Note: An RSA signatures is actually a big integer. It must be in the + // big-endian byte order. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING } + bool VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len); + + // Feeds a piece of the data to the signature verifier. + void VerifyUpdate(const uint8* data_part, int data_part_len); + + // Concludes a signature verification operation. Returns true if the + // signature is valid. Returns false if the signature is invalid or an + // error occurred. + bool VerifyFinal(); + + // Note: we can provide a one-shot interface if there is interest: + // bool Verify(const uint8* data, + // int data_len, + // const uint8* signature_algorithm, + // int signature_algorithm_len, + // const uint8* signature, + // int signature_len, + // const uint8* public_key_info, + // int public_key_info_len); + + private: + void Reset(); + + std::vector<uint8> signature_; + +#if defined(OS_LINUX) + VFYContext* vfy_context_; +#elif defined(OS_MACOSX) + std::vector<uint8> public_key_info_; + + CSSM_CSP_HANDLE csp_handle_; + + CSSM_CC_HANDLE sig_handle_; + + CSSM_KEY public_key_; +#elif defined(OS_WIN) + HCRYPTPROV provider_; + + HCRYPTHASH hash_object_; + + HCRYPTKEY public_key_; +#endif +}; + +} // namespace base + +#endif // BASE_CRYPTO_SIGNATURE_VERIFIER_H_ diff --git a/base/crypto/signature_verifier_mac.cc b/base/crypto/signature_verifier_mac.cc new file mode 100644 index 0000000..7dcd869 --- /dev/null +++ b/base/crypto/signature_verifier_mac.cc @@ -0,0 +1,143 @@ +// 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/signature_verifier.h" + +#include <stdlib.h> + +#include "base/crypto/cssm_init.h" +#include "base/logging.h" + +namespace { + +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 +}; + +} // namespace + +namespace base { + +SignatureVerifier::SignatureVerifier() : csp_handle_(0), sig_handle_(0) { + 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); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); + if (csp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); + DCHECK(crtn == CSSM_OK); + } +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + public_key_info_.assign(public_key_info, + public_key_info + public_key_info_len); + + CSSM_ALGORITHMS key_alg = CSSM_ALGID_RSA; // TODO(wtc): hardcoded. + + memset(&public_key_, 0, sizeof(public_key_)); + public_key_.KeyData.Data = const_cast<uint8*>(&public_key_info_[0]); + public_key_.KeyData.Length = public_key_info_.size(); + public_key_.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key_.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key_.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_X509; + public_key_.KeyHeader.AlgorithmId = key_alg; + public_key_.KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key_.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key_.KeyHeader.KeyUsage = CSSM_KEYUSE_VERIFY; + CSSM_KEY_SIZE key_size; + CSSM_RETURN crtn; + crtn = CSSM_QueryKeySizeInBits(csp_handle_, NULL, &public_key_, &key_size); + if (crtn) { + NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; + return false; + } + public_key_.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + + // TODO(wtc): decode signature_algorithm... + CSSM_ALGORITHMS sig_alg = CSSM_ALGID_SHA1WithRSA; + + crtn = CSSM_CSP_CreateSignatureContext(csp_handle_, sig_alg, NULL, + &public_key_, &sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + crtn = CSSM_VerifyDataInit(sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + CSSM_DATA data; + data.Data = const_cast<uint8*>(data_part); + data.Length = data_part_len; + CSSM_RETURN crtn = CSSM_VerifyDataUpdate(sig_handle_, &data, 1); + DCHECK(crtn == CSSM_OK); +} + +bool SignatureVerifier::VerifyFinal() { + CSSM_DATA sig; + sig.Data = const_cast<uint8*>(&signature_[0]); + sig.Length = signature_.size(); + CSSM_RETURN crtn = CSSM_VerifyDataFinal(sig_handle_, &sig); + Reset(); + + // crtn is CSSMERR_CSP_VERIFY_FAILED if signature verification fails. + return (crtn == CSSM_OK); +} + +void SignatureVerifier::Reset() { + CSSM_RETURN crtn; + if (sig_handle_) { + crtn = CSSM_DeleteContext(sig_handle_); + DCHECK(crtn == CSSM_OK); + sig_handle_ = 0; + } + signature_.clear(); + + // Can't call CSSM_FreeKey on public_key_ because we constructed + // public_key_ manually. +} + +} // namespace base + diff --git a/base/crypto/signature_verifier_nss.cc b/base/crypto/signature_verifier_nss.cc new file mode 100644 index 0000000..901f1fc --- /dev/null +++ b/base/crypto/signature_verifier_nss.cc @@ -0,0 +1,114 @@ +// 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/signature_verifier.h" + +#include <cryptohi.h> +#include <keyhi.h> +#include <stdlib.h> + +#include "base/logging.h" +#include "base/nss_init.h" + +namespace base { + +SignatureVerifier::SignatureVerifier() : vfy_context_(NULL) { + EnsureNSSInit(); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + + CERTSubjectPublicKeyInfo* spki = NULL; + SECItem spki_der; + spki_der.type = siBuffer; + spki_der.data = const_cast<uint8*>(public_key_info); + spki_der.len = public_key_info_len; + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); + if (!spki) + return false; + SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. + if (!public_key) + return false; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECKEY_DestroyPublicKey(public_key); + return false; + } + + SECItem sig_alg_der; + sig_alg_der.type = siBuffer; + sig_alg_der.data = const_cast<uint8*>(signature_algorithm); + sig_alg_der.len = signature_algorithm_len; + SECAlgorithmID sig_alg_id; + SECStatus rv; + rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, SECOID_AlgorithmIDTemplate, + &sig_alg_der); + if (rv != SECSuccess) { + SECKEY_DestroyPublicKey(public_key); + PORT_FreeArena(arena, PR_TRUE); + return false; + } + + SECItem sig; + sig.type = siBuffer; + sig.data = const_cast<uint8*>(signature); + sig.len = signature_len; + SECOidTag hash_alg_tag; + vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig, + &sig_alg_id, &hash_alg_tag, + NULL); + SECKEY_DestroyPublicKey(public_key); // Done with public_key. + PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. + if (!vfy_context_) { + // A corrupted RSA signature could be detected without the data, so + // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE + // (-8182). + return false; + } + + rv = VFY_Begin(vfy_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); + DCHECK(rv == SECSuccess); +} + +bool SignatureVerifier::VerifyFinal() { + SECStatus rv = VFY_End(vfy_context_); + Reset(); + + // If signature verification fails, the error code is + // SEC_ERROR_BAD_SIGNATURE (-8182). + return (rv == SECSuccess); +} + +void SignatureVerifier::Reset() { + if (vfy_context_) { + VFY_DestroyContext(vfy_context_, PR_TRUE); + vfy_context_ = NULL; + } + signature_.clear(); +} + +} // namespace base + diff --git a/base/crypto/signature_verifier_unittest.cc b/base/crypto/signature_verifier_unittest.cc new file mode 100644 index 0000000..6c7fdd9 --- /dev/null +++ b/base/crypto/signature_verifier_unittest.cc @@ -0,0 +1,259 @@ +// 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/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SignatureVerifierTest, BasicTest) { + // The input data in this test comes from real certificates. + // + // tbs_certificate ("to-be-signed certificate", the part of a certificate + // that is signed), signature_algorithm, and algorithm come from the + // certificate of bugs.webkit.org. + // + // public_key_info comes from the certificate of the issuer, Go Daddy Secure + // Certification Authority. + // + // The bytes in the array initializers are formatted to expose the DER + // encoding of the ASN.1 structures. + + // The data that is signed is the following ASN.1 structure: + // TBSCertificate ::= SEQUENCE {
+ // ... -- omitted, not important
+ // } + const uint8 tbs_certificate[1017] = { + 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5) + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, + 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, + 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, + 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, + 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, + 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, + 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, + 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, + 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, + 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, + 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, + 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, + 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, + 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, + 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, + 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, + 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, + 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, + 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, + 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, + 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, + 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, + 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, + 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67 + }; + + // The signature algorithm is specified as the following ASN.1 structure: + // AlgorithmIdentifier ::= SEQUENCE {
+ // algorithm OBJECT IDENTIFIER,
+ // parameters ANY DEFINED BY algorithm OPTIONAL } + // + const uint8 signature_algorithm[15] = { + 0x30, 0x0d, // a SEQUENCE of length 13 (0xd) + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + // 1.2.840.113549.1.1.5 - sha1WithRSAEncryption + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, // a NULL of length 0 + }; + + // RSA signature, a big integer in the big-endian byte order. + const uint8 signature[256] = { + 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, + 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, + 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, + 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, + 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, + 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, + 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, + 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, + 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, + 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, + 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, + 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, + 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, + 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, + 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, + 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, + 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, + 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, + 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, + 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, + 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, + 0x3c, 0xd8, 0x48, 0x8a + }; + + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING } + const uint8 public_key_info[294] = { + 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122) + // algorithm + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // a NULL of length 0 + // subjectPublicKey + 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f) + 0x00, // number of unused bits + 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a) + // modulus + 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101) + 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec, + 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61, + 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7, + 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, + 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, + 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, + 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9, + 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95, + 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00, + 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, + 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, + 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, + 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48, + 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66, + 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8, + 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, + 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, + 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, + 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0, + 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5, + 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81, + 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, + 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, + 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, + 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe, + 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, + // public exponent + 0x02, 0x03, // an INTEGER of length 3 + 0x01, 0x00, 0x01 + }; + + // We use the signature verifier to perform four signature verification + // tests. + base::SignatureVerifier verifier; + bool ok; + + // Test 1: feed all of the data to the verifier at once (a single + // VerifyUpdate call). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 2: feed the data to the verifier in three parts (three VerifyUpdate + // calls). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, 256); + verifier.VerifyUpdate(tbs_certificate + 256, 256); + verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 3: verify the signature with incorrect data. + uint8 bad_tbs_certificate[sizeof(tbs_certificate)]; + memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate)); + bad_tbs_certificate[10] += 1; // Corrupt one byte of the data. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + + // Test 4: verify a bad signature. + uint8 bad_signature[sizeof(signature)]; + memcpy(bad_signature, signature, sizeof(signature)); + bad_signature[10] += 1; // Corrupt one byte of the signature. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + bad_signature, sizeof(bad_signature), + public_key_info, sizeof(public_key_info)); + + // A crypto library (e.g., NSS) may detect that the signature is corrupted + // and cause VerifyInit to return false, so it is fine for 'ok' to be false. + if (ok) { + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + } +} diff --git a/base/crypto/signature_verifier_win.cc b/base/crypto/signature_verifier_win.cc new file mode 100644 index 0000000..be1ecec --- /dev/null +++ b/base/crypto/signature_verifier_win.cc @@ -0,0 +1,148 @@ +// 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/signature_verifier.h" + +#include "base/logging.h" + +#pragma comment(lib, "crypt32.lib") + +namespace { + +// Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the +// WINAPI calling convention. +void* WINAPI MyCryptAlloc(size_t size) { + return malloc(size); +} + +void WINAPI MyCryptFree(void* p) { + free(p); +} + +} // namespace + +namespace base { + +SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) { + if (!CryptAcquireContext(&provider_, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + provider_ = 0; +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); + if (provider_) { + BOOL ok = CryptReleaseContext(provider_, 0); + DCHECK(ok); + } +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.reserve(signature_len); + // CryptoAPI uses big integers in the little-endian byte order, so we need + // to first swap the order of signature bytes. + for (int i = signature_len - 1; i >= 0; --i) + signature_.push_back(signature[i]); + + CRYPT_DECODE_PARA decode_para; + decode_para.cbSize = sizeof(decode_para); + decode_para.pfnAlloc = MyCryptAlloc; + decode_para.pfnFree = MyCryptFree; + CERT_PUBLIC_KEY_INFO* cert_public_key_info = NULL; + DWORD struct_len = 0; + BOOL ok; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_PUBLIC_KEY_INFO, + public_key_info, + public_key_info_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &cert_public_key_info, + &struct_len); + if (!ok) + return false; + + ok = CryptImportPublicKeyInfo(provider_, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + cert_public_key_info, &public_key_); + free(cert_public_key_info); + if (!ok) + return false; + + CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id; + struct_len = 0; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_ALGORITHM_IDENTIFIER, + signature_algorithm, + signature_algorithm_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &signature_algorithm_id, + &struct_len); + DCHECK(ok || GetLastError() == ERROR_FILE_NOT_FOUND); + ALG_ID hash_alg_id; + if (ok) { + hash_alg_id = CALG_MD4; // Initialize to a weak hash algorithm that we + // don't support. + if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA)) + hash_alg_id = CALG_SHA1; + else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA)) + hash_alg_id = CALG_MD5; + free(signature_algorithm_id); + DCHECK(hash_alg_id != CALG_MD4); + if (hash_alg_id == CALG_MD4) + return false; // Unsupported hash algorithm. + } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { + // TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2. We + // may be able to encapsulate signature_algorithm in a dummy SignedContent + // and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO. For now, + // just hardcode the hash algorithm to be SHA-1. + hash_alg_id = CALG_SHA1; + } else { + return false; + } + + ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, &hash_object_); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0); + DCHECK(ok) << "CryptHashData failed: " << GetLastError(); +} + +bool SignatureVerifier::VerifyFinal() { + BOOL ok = CryptVerifySignature(hash_object_, &signature_[0], + signature_.size(), public_key_, NULL, 0); + Reset(); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::Reset() { + BOOL ok; + if (hash_object_) { + ok = CryptDestroyHash(hash_object_); + DCHECK(ok); + hash_object_ = 0; + } + if (public_key_) { + ok = CryptDestroyKey(public_key_); + DCHECK(ok); + public_key_ = 0; + } + signature_.clear(); +} + +} // namespace base + |