Index: base/signatures.cc |
diff --git a/base/signatures.cc b/base/signatures.cc |
deleted file mode 100644 |
index 6d40f8606edbbc03443899449b4bea9851142a4e..0000000000000000000000000000000000000000 |
--- a/base/signatures.cc |
+++ /dev/null |
@@ -1,1113 +0,0 @@ |
-// Copyright 2003-2010 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
-// ======================================================================== |
-// |
-// signatures.cpp |
-// |
-// Classes and functions related to crypto-hashes of buffers and digital |
-// signatures of buffers. |
- |
-#include "omaha/base/signatures.h" |
-#include <wincrypt.h> |
-#include <memory.h> |
-#pragma warning(disable : 4245) |
-// C4245 : conversion from 'type1' to 'type2', signed/unsigned mismatch |
-#include <atlenc.h> |
-#pragma warning(default : 4245) |
-#include <vector> |
-#include "base/scoped_ptr.h" |
-#include "omaha/base/const_utils.h" |
-#include "omaha/base/debug.h" |
-#include "omaha/base/error.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/scoped_any.h" |
-#include "omaha/base/string.h" |
-#include "omaha/base/utils.h" |
- |
-namespace omaha { |
- |
-const ALG_ID kHashAlgorithm = CALG_SHA1; |
-const DWORD kEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; |
-const DWORD kProviderType = PROV_RSA_FULL; |
-const DWORD kCertificateNameType = CERT_NAME_SIMPLE_DISPLAY_TYPE; |
-const DWORD kKeyPairType = AT_SIGNATURE; |
- |
-// Maximum file size allowed for performing authentication. |
-const int kMaxFileSizeForAuthentication = 512 * 1024 * 1024; // 512MB |
- |
-// Buffer size used to read files from disk. |
-const int kFileReadBufferSize = 128 * 1024; |
- |
-namespace CryptDetails { |
- |
- // Useful scoped pointers for working with CryptoAPI objects |
- |
- void crypt_release_context(HCRYPTPROV provider) { |
- UTIL_LOG(L3, (L"Releasing HCRYPTPROV 0x%08lx", provider)); |
- BOOL b = ::CryptReleaseContext(provider, 0 /*flags*/); |
- ASSERT(b, (L"")); |
- } |
- |
- void crypt_close_store(HCERTSTORE store) { |
- UTIL_LOG(L3, (L"Releasing HCERTSTORE 0x%08lx", store)); |
- BOOL b = ::CertCloseStore(store, 0 /*flags*/); |
- ASSERT(b, (L"")); |
- ASSERT(::GetLastError() != CRYPT_E_PENDING_CLOSE, (L"")); |
- } |
- |
- void crypt_free_certificate(PCCERT_CONTEXT certificate) { |
- UTIL_LOG(L3, (L"Releasing PCCERT_CONTEXT 0x%08lx", certificate)); |
- BOOL b = ::CertFreeCertificateContext(certificate); |
- ASSERT(b, (L"")); |
- } |
- |
- void crypt_destroy_key(HCRYPTKEY key) { |
- UTIL_LOG(L3, (L"Releasing HCRYPTKEY 0x%08lx", key)); |
- BOOL b = ::CryptDestroyKey(key); |
- ASSERT(b, (L"")); |
- } |
- |
- void crypt_destroy_hash(HCRYPTHASH hash) { |
- UTIL_LOG(L3, (L"Releasing HCRYPTHASH 0x%08lx", hash)); |
- BOOL b = ::CryptDestroyHash(hash); |
- ASSERT(b, (L"")); |
- } |
- |
- typedef close_fun<void (*)(HCRYPTHASH), |
- crypt_destroy_hash> smart_destroy_hash; |
- typedef scoped_any<HCRYPTHASH, smart_destroy_hash, null_t> scoped_crypt_hash; |
-} |
- |
-// Base64 encode/decode functions are part of ATL Server |
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in, |
- std::vector<byte>* encoded, |
- bool break_into_lines) { |
- ASSERT(encoded, (L"")); |
- |
- if (buffer_in.empty()) { |
- encoded->resize(0); |
- return S_OK; |
- } |
- |
- int32 encoded_len = |
- Base64EncodeGetRequiredLength( |
- buffer_in.size(), |
- break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF); |
- ASSERT(encoded_len > 0, (L"")); |
- |
- encoded->resize(encoded_len); |
- int32 str_out_len = encoded_len; |
- |
- BOOL result = Base64Encode( |
- &buffer_in.front(), |
- buffer_in.size(), |
- reinterpret_cast<char*>(&encoded->front()), |
- &str_out_len, |
- break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF); |
- if (!result) |
- return E_FAIL; |
- ASSERT(str_out_len <= encoded_len, (L"")); |
- if (str_out_len < encoded_len) |
- encoded->resize(str_out_len); |
- |
- return S_OK; |
-} |
- |
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in, |
- CStringA* encoded, |
- bool break_into_lines) { |
- ASSERT(encoded, (L"")); |
- |
- if (buffer_in.empty()) { |
- return S_OK; |
- } |
- |
- std::vector<byte> buffer_out; |
- RET_IF_FAILED(Encode(buffer_in, &buffer_out, break_into_lines)); |
- encoded->Append(reinterpret_cast<const char*>(&buffer_out.front()), |
- buffer_out.size()); |
- |
- return S_OK; |
-} |
- |
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in, |
- CString* encoded, |
- bool break_into_lines) { |
- ASSERT(encoded, (L"")); |
- |
- CStringA string_out; |
- RET_IF_FAILED(Encode(buffer_in, &string_out, break_into_lines)); |
- *encoded = string_out; |
- |
- return S_OK; |
-} |
- |
-HRESULT Base64::Decode(const std::vector<byte>& encoded, |
- std::vector<byte>* buffer_out) { |
- ASSERT(buffer_out, (L"")); |
- |
- size_t encoded_len = encoded.size(); |
- int32 required_len = Base64DecodeGetRequiredLength(encoded_len); |
- |
- buffer_out->resize(required_len); |
- |
- if (required_len == 0) { |
- return S_OK; |
- } |
- |
- int32 bytes_written = required_len; |
- BOOL result = Base64Decode(reinterpret_cast<const char*>(&encoded.front()), |
- encoded_len, |
- &buffer_out->front(), |
- &bytes_written); |
- if (!result) |
- return E_FAIL; |
- ASSERT(bytes_written <= required_len, (L"")); |
- if (bytes_written < required_len) { |
- buffer_out->resize(bytes_written); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT Base64::Decode(const CStringA& encoded, std::vector<byte>* buffer_out) { |
- ASSERT(buffer_out, (L"")); |
- |
- size_t encoded_len = encoded.GetLength(); |
- std::vector<byte> buffer_in(encoded_len); |
- if (encoded_len != 0) { |
- ::memcpy(&buffer_in.front(), encoded.GetString(), encoded_len); |
- } |
- |
- return Decode(buffer_in, buffer_out); |
-} |
- |
-// Base64 in a CString -> binary |
-HRESULT Base64::Decode(const CString& encoded, std::vector<byte>* buffer_out) { |
- ASSERT(buffer_out, (L"")); |
- |
- CW2A encoded_a(encoded.GetString()); |
- |
- size_t encoded_len = ::strlen(encoded_a); |
- std::vector<byte> buffer_in(encoded_len); |
- if (encoded_len != 0) { |
- ::memcpy(&buffer_in.front(), encoded_a, encoded_len); |
- } |
- |
- return Decode(buffer_in, buffer_out); |
-} |
- |
-CryptoHash::CryptoHash() { |
-} |
- |
-CryptoHash::~CryptoHash() { |
-} |
- |
-HRESULT CryptoHash::Compute(const TCHAR* filepath, |
- uint64 max_len, |
- std::vector<byte>* hash_out) { |
- ASSERT1(filepath); |
- ASSERT1(hash_out); |
- |
- std::vector<CString> filepaths; |
- filepaths.push_back(filepath); |
- return Compute(filepaths, max_len, hash_out); |
-} |
- |
-HRESULT CryptoHash::Compute(const std::vector<CString>& filepaths, |
- uint64 max_len, |
- std::vector<byte>* hash_out) { |
- ASSERT1(filepaths.size() > 0); |
- ASSERT1(hash_out); |
- |
- return ComputeOrValidate(filepaths, max_len, NULL, hash_out); |
-} |
- |
-HRESULT CryptoHash::Compute(const std::vector<byte>& buffer_in, |
- std::vector<byte>* hash_out) { |
- ASSERT1(buffer_in.size() > 0); |
- ASSERT1(hash_out); |
- |
- return ComputeOrValidate(buffer_in, NULL, hash_out); |
-} |
- |
-HRESULT CryptoHash::Validate(const TCHAR* filepath, |
- uint64 max_len, |
- const std::vector<byte>& hash_in) { |
- ASSERT1(filepath); |
- ASSERT1(hash_in.size() == kHashSize); |
- |
- std::vector<CString> filepaths; |
- filepaths.push_back(filepath); |
- return Validate(filepaths, max_len, hash_in); |
-} |
- |
-HRESULT CryptoHash::Validate(const std::vector<CString>& filepaths, |
- uint64 max_len, |
- const std::vector<byte>& hash_in) { |
- ASSERT1(hash_in.size() == kHashSize); |
- |
- return ComputeOrValidate(filepaths, max_len, &hash_in, NULL); |
-} |
- |
- |
-HRESULT CryptoHash::Validate(const std::vector<byte>& buffer_in, |
- const std::vector<byte>& hash_in) { |
- ASSERT1(buffer_in.size() > 0); |
- ASSERT1(hash_in.size() == kHashSize); |
- |
- return ComputeOrValidate(buffer_in, &hash_in, NULL); |
-} |
- |
-HRESULT CryptoHash::ComputeOrValidate(const std::vector<CString>& filepaths, |
- uint64 max_len, |
- const std::vector<byte>* hash_in, |
- std::vector<byte>* hash_out) { |
- ASSERT1(filepaths.size() > 0); |
- ASSERT1(hash_in && !hash_out || !hash_in && hash_out); |
- UTIL_LOG(L1, (_T("[CryptoHash::ComputeOrValidate]"))); |
- |
- std::vector<byte> buf(kFileReadBufferSize); |
- uint64 curr_len = 0; |
- |
- CryptDetails::scoped_crypt_context scoped_csp_handle; |
- if (0 == ::CryptAcquireContext(address(scoped_csp_handle), |
- NULL, |
- NULL, // Use OS-default CSP. |
- kProviderType, |
- CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptAcquireContext failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- |
- CryptDetails::scoped_crypt_hash scoped_hash_handle; |
- if (0 == ::CryptCreateHash(get(scoped_csp_handle), |
- kHashAlgorithm, |
- NULL, |
- 0, |
- address(scoped_hash_handle))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptCreateHash failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- |
- for (size_t i = 0; i < filepaths.size(); ++i) { |
- scoped_hfile file_handle(::CreateFile(filepaths[i], |
- FILE_READ_DATA, |
- FILE_SHARE_READ, |
- NULL, |
- OPEN_EXISTING, |
- FILE_ATTRIBUTE_NORMAL, |
- NULL)); |
- if (!file_handle) { |
- return HRESULTFromLastError(); |
- } |
- |
- if (max_len) { |
- LARGE_INTEGER file_size = {0}; |
- if (!::GetFileSizeEx(get(file_handle), &file_size)) { |
- return HRESULTFromLastError(); |
- } |
- curr_len += file_size.QuadPart; |
- if (curr_len > max_len) { |
- UTIL_LOG(LE, (_T("[exceed max len][curr_len=%lu][max_len=%lu]"), |
- curr_len, max_len)); |
- return E_FAIL; |
- } |
- } |
- |
- DWORD bytes_read = 0; |
- do { |
- if (!::ReadFile(get(file_handle), |
- &buf[0], |
- buf.size(), |
- &bytes_read, |
- NULL)) { |
- return HRESULTFromLastError(); |
- } |
- |
- if (bytes_read > 0) { |
- if (0 == ::CryptHashData(get(scoped_hash_handle), |
- &buf[0], |
- bytes_read, |
- 0)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptHashData failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- } |
- } while (bytes_read == buf.size()); |
- } |
- |
- DWORD digest_size = kHashSize; |
- byte digest_data[kHashSize] = {}; |
- if (0 == ::CryptGetHashParam(get(scoped_hash_handle), |
- HP_HASHVAL, |
- digest_data, |
- &digest_size, |
- 0)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptGetHashParam failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- if (digest_size != kHashSize) { |
- UTIL_LOG(LE, (_T("[CryptGetHashParam returned %d bytes]"), digest_size)); |
- return E_UNEXPECTED; |
- } |
- |
- if (hash_in) { |
- int res = ::memcmp(&hash_in->front(), digest_data, kHashSize); |
- if (res == 0) { |
- return S_OK; |
- } |
- |
- std::vector<byte> calculated_hash(kHashSize); |
- ::memcpy(&calculated_hash.front(), digest_data, kHashSize); |
- CStringA base64_encoded_hash; |
- Base64::Encode(calculated_hash, &base64_encoded_hash, false); |
- CString hash = AnsiToWideString(base64_encoded_hash, |
- base64_encoded_hash.GetLength()); |
- REPORT_LOG(L1, (_T("[actual hash=%s]"), hash)); |
- return SIGS_E_INVALID_SIGNATURE; |
- } else { |
- hash_out->resize(kHashSize); |
- ::memcpy(&hash_out->front(), digest_data, kHashSize); |
- return S_OK; |
- } |
-} |
- |
-HRESULT CryptoHash::ComputeOrValidate(const std::vector<byte>& buffer_in, |
- const std::vector<byte>* hash_in, |
- std::vector<byte>* hash_out) { |
- ASSERT1(hash_in && !hash_out || !hash_in && hash_out); |
- UTIL_LOG(L1, (_T("[CryptoHash::ComputeOrValidate]"))); |
- |
- CryptDetails::scoped_crypt_context scoped_csp_handle; |
- if (0 == ::CryptAcquireContext(address(scoped_csp_handle), |
- NULL, |
- NULL, // Use OS-default CSP. |
- kProviderType, |
- CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptAcquireContext failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- |
- CryptDetails::scoped_crypt_hash scoped_hash_handle; |
- if (0 == ::CryptCreateHash(get(scoped_csp_handle), |
- kHashAlgorithm, |
- NULL, |
- 0, |
- address(scoped_hash_handle))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptCreateHash failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- |
- if (!buffer_in.empty()) { |
- if (0 == ::CryptHashData(get(scoped_hash_handle), |
- &buffer_in.front(), |
- buffer_in.size(), |
- 0)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptHashData failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- } |
- |
- DWORD digest_size = kHashSize; |
- byte digest_data[kHashSize] = {}; |
- if (0 == ::CryptGetHashParam(get(scoped_hash_handle), |
- HP_HASHVAL, |
- digest_data, |
- &digest_size, |
- 0)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[CryptGetHashParam failed][0x%08lX]"), hr)); |
- return hr; |
- } |
- if (digest_size != kHashSize) { |
- UTIL_LOG(LE, (_T("[CryptGetHashParam returned %d bytes]"), digest_size)); |
- return E_UNEXPECTED; |
- } |
- |
- if (hash_in) { |
- int res = ::memcmp(&hash_in->front(), digest_data, kHashSize); |
- return (res == 0) ? S_OK : SIGS_E_INVALID_SIGNATURE; |
- } else { |
- hash_out->resize(kHashSize); |
- ::memcpy(&hash_out->front(), digest_data, kHashSize); |
- return S_OK; |
- } |
-} |
- |
-// To sign data you need a CSP with the proper private key installed. |
-// To get a signing certificate you start with a PFX file. This file |
-// encodes a "certificate store" which can hold more than one |
-// certificate. (In general it can hold a certificate chain, but we |
-// only use the signing certificate.) There are special APIs to verify |
-// the format of a PFX file and read it into a new certificate store. A |
-// password must be specified to read the PFX file as it is encrypted. |
-// The password was set when the PFX file was exported or otherwise |
-// created. Then you search for the proper certificate in the store |
-// (using the subject_name which tells who the certificate was issued |
-// to). Finally, to get a CSP with the certificate's private key |
-// available there is a special API, CryptAcquireCertificatePrivateKey, |
-// that takes a CSP and a certificate and makes the private key of the |
-// certificate the private key of the CSP. |
- |
-CryptoSigningCertificate::CryptoSigningCertificate() : key_spec_(0) { |
-} |
- |
-CryptoSigningCertificate::~CryptoSigningCertificate() { |
-} |
- |
-HRESULT CryptoSigningCertificate::ImportCertificate( |
- const TCHAR * filepath, |
- const TCHAR * password, |
- const TCHAR * subject_name) { |
- ASSERT(filepath, (L"")); |
- ASSERT(password, (L"")); |
- |
- std::vector<byte> buffer; |
- HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"['%s' not read, hr 0x%08lx]", filepath, hr)); |
- return hr; |
- } |
- return ImportCertificate(buffer, password, subject_name); |
-} |
- |
-HRESULT CryptoSigningCertificate::ImportCertificate( |
- const std::vector<byte>& certificate_in, |
- const TCHAR * password, |
- const TCHAR * subject_name) { |
- ASSERT(password, (L"")); |
- ASSERT1(!certificate_in.empty()); |
- |
- UTIL_LOG(L2, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[%d bytes, subject_name '%s']", |
- certificate_in.size(), subject_name ? subject_name : L"")); |
- |
- // CryptoAPI treats the certificate as a "blob" |
- CRYPT_DATA_BLOB blob; |
- blob.cbData = certificate_in.size(); |
- blob.pbData = const_cast<BYTE*>(&certificate_in.front()); |
- |
- // Ensure that it is PFX formatted |
- BOOL b = ::PFXIsPFXBlob(&blob); |
- if (!b) { |
- ASSERT(0, (L"Invalid PFX certificate, err 0x%08lx", ::GetLastError())); |
- return SIGS_E_INVALID_PFX_CERTIFICATE; |
- } |
- |
- // Make sure the password checks out |
- b = ::PFXVerifyPassword(&blob, password, 0 /* flags */); |
- if (!b) { |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[invalid password, err 0x%08lx]", ::GetLastError())); |
- return SIGS_E_INVALID_PASSWORD; |
- } |
- |
- // Do the import from the certificate to a new certificate store |
- // TODO(omaha): Check that this is in fact a new certificate store, not an |
- // existing one. If it is an existing one we'll need to delete the |
- // certificate later. |
- // The last parameter to ::PFXImportCertStore() is 0, indicating that we want |
- // the CSP to be "silent"; i.e., not prompt. |
- reset(store_, ::PFXImportCertStore(&blob, password, 0)); |
- if (!store_) { |
- DWORD err = ::GetLastError(); |
- ASSERT(0, (L"Failed to import PFX certificate into a certificate store, " |
- L"err 0x%08lx", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[new store 0x%08lx]", get(store_))); |
- |
- // Now that we have a store, look for the correct certificate. (There may |
- // have been more than one in the PFX file, e.g., a certificate chain.) |
- PCCERT_CONTEXT certificate_context = NULL; |
- while ((certificate_context = |
- ::CertEnumCertificatesInStore(get(store_), |
- certificate_context)) != NULL) { |
- // Have a certificate, does it look like the right one? Check the name |
- DWORD name_len = ::CertGetNameString(certificate_context, |
- kCertificateNameType, |
- 0 /*flags*/, |
- NULL, |
- NULL, |
- 0); |
- if (name_len <= 1) { |
- // Name attribute not found - should never happen |
- ASSERT(0, (L"CryptoSigningCertificate::ImportCertificate failed to get " |
- L"certificate name length, err 0x%08lx", ::GetLastError())); |
- continue; |
- } |
- // name_len includes the terminating null |
- |
- std::vector<TCHAR> name; |
- name.resize(name_len); |
- ASSERT1(!name.empty()); |
- DWORD name_len2 = ::CertGetNameString(certificate_context, |
- kCertificateNameType, |
- 0, |
- NULL, |
- &name.front(), |
- name_len); |
- ASSERT(name_len2 == name_len, (L"")); |
- |
- UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[found '%s' in store]", &name.front())); |
- |
- // Check the name if the user so desires. (If subject_name == NULL then |
- // the first certificate found is used.) |
- if (subject_name && (0 != String_StrNCmp(&name.front(), |
- subject_name, |
- ::lstrlen(subject_name), |
- false))) { |
- // name mismatch |
- UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[not the right certificate, we're looking for '%s']", |
- subject_name)); |
- continue; |
- } |
- |
- // This is the right certificate |
- subject_name_ = &name.front(); |
- reset(certificate_, certificate_context); |
- UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" |
- L"[new certificate 0x%08lx]", get(certificate_))); |
- break; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT CryptoSigningCertificate::GetCSPContext(HCRYPTPROV* csp_context) { |
- ASSERT(csp_context, (L"")); |
- ASSERT(get(certificate_), (L"")); |
- |
- // CSP may have already been used - reset it |
- reset(csp_); |
- |
- // Create a CSP context using the private key of the certificate we imported |
- // earlier. |
- HCRYPTPROV csp = NULL; |
- BOOL must_free_csp = FALSE; |
- BOOL b = ::CryptAcquireCertificatePrivateKey(get(certificate_), |
- 0 /*flags*/, |
- 0 /*reserved*/, |
- &csp, |
- &key_spec_, |
- &must_free_csp); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- ASSERT(0, (L"CryptoSigningCertificate::GetCSPContext " |
- L"CryptAcquireCertificatePrivateKey failed, err 0x%08lx", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- |
- // (Funky API returns a boolean which tells you whether it is your |
- // responsibility to delete the CSP context or not.) |
- if (must_free_csp) { |
- reset(csp_, csp); |
- } |
- if (get(csp_)) { |
- UTIL_LOG(L3, (L"[CryptoSigningCertificate::GetCSPContext new CSP 0x%08lx]", |
- get(csp_))); |
- } |
- |
- ASSERT(key_spec_ == AT_SIGNATURE || key_spec_ == AT_KEYEXCHANGE, (L"")); |
- if (key_spec_ != kKeyPairType) { |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[requires a AT_SIGNATURE type key]")); |
- return SIGS_E_INVALID_KEY_TYPE; |
- } |
- |
-#ifdef _DEBUG |
- // Which CSP did we get? |
- char csp_name[256] = {0}; |
- DWORD csp_name_len = arraysize(csp_name); |
- b = ::CryptGetProvParam(csp, |
- PP_NAME, |
- reinterpret_cast<BYTE*>(&csp_name[0]), |
- &csp_name_len, |
- 0 /*flags*/); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[error getting CSP name, err 0x%08lx]", err)); |
- } |
- DWORD csp_prov_type; |
- DWORD csp_prov_type_len = sizeof(csp_prov_type); |
- b = ::CryptGetProvParam(csp, |
- PP_PROVTYPE, |
- reinterpret_cast<BYTE*>(&csp_prov_type), |
- &csp_prov_type_len, |
- 0 /*flags*/); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[error getting CSP provtype, err 0x%08lx]", err)); |
- } |
- char csp_container[256] = {0}; |
- DWORD csp_container_len = arraysize(csp_container); |
- b = ::CryptGetProvParam(csp, |
- PP_CONTAINER, |
- reinterpret_cast<BYTE*>(&csp_container[0]), |
- &csp_container_len, |
- 0 /*flags*/); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[error getting CSP current container name, err 0x%08lx]", |
- err)); |
- } |
- UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[have CSP '%S' (provtype %d) key container '%S']", |
- csp_name, csp_prov_type, csp_container)); |
- // End of which CSP did we get |
-#endif |
- |
- *csp_context = csp; |
- |
- UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]" |
- L"[getting CSP with private key from certificate]" |
- L"[HCRYPTPROV 0x%08lx]", csp)); |
- |
- return S_OK; |
-} |
- |
-// To sign some data using CryptoAPI you first hash it into a hash |
-// object, then sign it using the CSP. The CSP needs to have the |
-// private key, of type AT_SIGNATURE, in it already, as it isn't a |
-// parameter of the CryptSignHash API. The CryptoSigningCertificate |
-// can provide such a CSP. |
- |
-CryptoComputeSignature::CryptoComputeSignature( |
- CryptoSigningCertificate* certificate) |
- : certificate_(certificate) { |
-} |
- |
-CryptoComputeSignature::~CryptoComputeSignature() { |
-} |
- |
-HRESULT CryptoComputeSignature::Sign(TCHAR const * const filepath, |
- uint32 max_len, |
- std::vector<byte>* signature_out) { |
- ASSERT(filepath, (L"")); |
- std::vector<byte> buffer; |
- HRESULT hr = ReadEntireFile(filepath, max_len, &buffer); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" |
- L"['%s not read, hr 0x%08lx]", filepath, hr)); |
- return hr; |
- } |
- return Sign(buffer, signature_out); |
-} |
- |
-HRESULT CryptoComputeSignature::Sign(const std::vector<byte>& buffer_in, |
- std::vector<byte>* signature_out) { |
- ASSERT(signature_out, (L"")); |
- ASSERT1(!buffer_in.empty()); |
- |
- UTIL_LOG(L2, (L"[CryptoComputeSignature::Sign]" |
- L"[buffer of %d bytes]", buffer_in.size())); |
- |
- // Get the proper CSP with the private key (certificate retains ownership) |
- HCRYPTPROV csp = NULL; |
- HRESULT hr = certificate_->GetCSPContext(&csp); |
- ASSERT(SUCCEEDED(hr) && csp, (L"")); |
- |
- // Hash the data |
- CryptDetails::scoped_crypt_hash hash; |
- BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash)); |
- if (!b) { |
- // hash is now invalid, but might not be NULL, so stomp on it |
- DWORD err = ::GetLastError(); |
- ASSERT(!hash, (L"")); |
- UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" |
- L"[could not create hash, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- UTIL_LOG(L3, (L"CryptoComputeSignature::Sign new hash 0x%08lx", get(hash))); |
- |
- b = ::CryptHashData(get(hash), &buffer_in.front(), buffer_in.size(), 0); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" |
- L"[could not hash data, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- |
- // Sign the hash (first get length, then allocate buffer and do real signing) |
- DWORD signature_len = 0; |
- b = ::CryptSignHash(get(hash), |
- kKeyPairType, |
- NULL, |
- 0 /*flags*/, |
- NULL, |
- &signature_len); |
- if (!b && ::GetLastError() != ERROR_MORE_DATA) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" |
- L"[could not compute size of signature, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- signature_out->resize(signature_len); |
- b = ::CryptSignHash(get(hash), |
- kKeyPairType, |
- NULL, |
- 0, |
- &signature_out->front(), |
- &signature_len); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" |
- L"[could not compute signature, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- ASSERT(signature_len == signature_out->size(), (L"")); |
- |
- UTIL_LOG(L3, (L"[CryptoComputeSignature::Sign]" |
- L"[have %d byte signature]", signature_out->size())); |
- |
- return S_OK; |
-} |
- |
-// To verify signed data you need a CSP, and you also need the public |
-// key extracted from a certificate. The CSP can be any RSA CSP on the |
-// machine, the default one is fine. To get the public key you start |
-// by importing a certificate in standard "DER encoded" format. That |
-// returns a giant data structure, one field of which is the public key |
-// in a format that CryptoAPI understands. You import this public key |
-// into the CSP with the CryptImportPublicKey() API, and then create a |
-// key object from it suitable for use with the verification API. |
- |
-CryptoSignatureVerificationCertificate::CryptoSignatureVerificationCertificate() { // NOLINT |
-} |
- |
-CryptoSignatureVerificationCertificate::~CryptoSignatureVerificationCertificate() { // NOLINT |
-} |
- |
-HRESULT CryptoSignatureVerificationCertificate::ImportCertificate( |
- const TCHAR * filepath, |
- const TCHAR * subject_name) { |
- ASSERT(filepath, (L"")); |
- std::vector<byte> buffer; |
- HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" |
- L"['%s' not read, hr 0x%08lx]", filepath, hr)); |
- return hr; |
- } |
- return ImportCertificate(buffer, subject_name); |
-} |
- |
-HRESULT CryptoSignatureVerificationCertificate::ImportCertificate( |
- const std::vector<byte>& certificate_in, |
- const TCHAR * subject_name) { |
- // Import the certificate |
- ASSERT1(!certificate_in.empty()); |
- reset(certificate_, ::CertCreateCertificateContext(kEncodingType, |
- &certificate_in.front(), |
- certificate_in.size())); |
- if (!certificate_) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" |
- L"[could not import certificate, err 0x%08lx]", err)); |
- return SIGS_E_INVALID_DER_CERTIFICATE; |
- } |
- UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" |
- L"[new certificate 0x%08lx]", get(certificate_))); |
- |
- // Get certificate's subject name |
- DWORD name_len = ::CertGetNameString(get(certificate_), |
- kCertificateNameType, |
- 0 /*flags*/, |
- NULL, |
- NULL, |
- 0); |
- if (name_len <= 1) { |
- // Name attribute not found - should never happen |
- ASSERT(0, (L"CryptoSignatureVerificationCertificate failed to get " |
- L"certificate name length, err 0x%08lx", ::GetLastError())); |
- return E_FAIL; |
- } |
- // name_len includes the terminating NULL |
- |
- std::vector <TCHAR> name; |
- name.resize(name_len); |
- ASSERT1(!name.empty()); |
- DWORD name_len2 = ::CertGetNameString(get(certificate_), |
- kCertificateNameType, |
- 0, |
- NULL, |
- &name.front(), |
- name_len); |
- ASSERT(name_len2 == name_len, (L"")); |
- |
- UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" |
- L"['%s' is subject of certificate]", &name.front())); |
- |
- subject_name_ = &name.front(); |
- |
- // Check the name if the user so desires. |
- if (subject_name && (0 != String_StrNCmp(&name.front(), |
- subject_name, |
- ::lstrlen(subject_name), false))) { |
- // name mismatch |
- UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" |
- L"[not the right certificate, we're looking for '%s']", |
- subject_name)); |
- return E_FAIL; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT CryptoSignatureVerificationCertificate::GetCSPContextAndKey( |
- HCRYPTPROV* csp_context, |
- HCRYPTKEY* public_key) { |
- ASSERT(csp_context, (L"")); |
- ASSERT(public_key, (L"")); |
- ASSERT(get(certificate_), (L"")); |
- |
- // Get the public key out of the certificate |
- PCERT_INFO cert_info = get(certificate_)->pCertInfo; |
- ASSERT(cert_info, (L"")); |
- PCERT_PUBLIC_KEY_INFO public_key_info = &cert_info->SubjectPublicKeyInfo; |
- ASSERT(public_key_info, (L"")); |
- |
- // Reset the CSP and key in case it has been used already |
- reset(key_); |
- reset(csp_); |
- |
- // Get the default CSP. With CRYPT_VERIFYCONTEXT don't need to worry |
- // about creating/destroying a key container. |
- // TODO(omaha): Why wasn't PROV_RSA_SIG available? Maybe looking for the |
- // default isn't a good idea? |
- BOOL b = ::CryptAcquireContext(address(csp_), |
- NULL, |
- NULL, |
- kProviderType, |
- CRYPT_VERIFYCONTEXT|CRYPT_SILENT); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[GetCSPContextAndKey]" |
- L"[failed to acquire CSP, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]" |
- L"[new CSP 0x%08lx]", get(csp_))); |
- |
- // Convert the public key in encoded form into a CryptoAPI HCRYPTKEY |
- b = ::CryptImportPublicKeyInfo(get(csp_), |
- kEncodingType, |
- public_key_info, |
- address(key_)); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[GetCSPContextAndKey]" |
- L"[failed to import public key, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]" |
- L"[new key 0x%08lx]", get(key_))); |
- |
- *csp_context = get(csp_); |
- *public_key = get(key_); |
- |
- return S_OK; |
-} |
- |
-// To verify the signature of some data using CryptoAPI you first hash |
-// it into a hash object, then verify it using the CSP and a public key. |
-// In this case the CryptVerifySignature takes the key (of type |
-// AT_SIGNATURE) as a separate parameter. The |
-// CryptoSignatureVerificationCertificate can provide the proper CSP and |
-// the public key from the certificate. |
- |
-CryptoVerifySignature::CryptoVerifySignature( |
- CryptoSignatureVerificationCertificate& certificate) |
- : certificate_(&certificate) { |
-} |
- |
-CryptoVerifySignature::~CryptoVerifySignature() { |
-} |
- |
-HRESULT CryptoVerifySignature::Validate(const TCHAR* filepath, |
- uint32 max_len, |
- const std::vector<byte>& signature_in) { |
- ASSERT(filepath, (L"")); |
- std::vector<byte> buffer; |
- HRESULT hr = ReadEntireFile(filepath, max_len, &buffer); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]" |
- L"['%s' not read, hr 0x%08lx]", filepath, hr)); |
- return hr; |
- } |
- return Validate(buffer, signature_in); |
-} |
- |
-HRESULT CryptoVerifySignature::Validate(const std::vector<byte>& buffer_in, |
- const std::vector<byte>& signature_in) { |
- ASSERT(certificate_, (L"")); |
- ASSERT1(!buffer_in.empty()); |
- ASSERT1(!signature_in.empty()); |
- |
- UTIL_LOG(L2, (L"[CryptoVerifySignature::Validate]" |
- L"[buffer of %d bytes, signature of %d bytes]", |
- buffer_in.size(), signature_in.size())); |
- |
- // Get the CSP context and the public key from the certificate |
- HCRYPTPROV csp = NULL; |
- HCRYPTKEY key = NULL; |
- HRESULT hr = certificate_->GetCSPContextAndKey(&csp, &key); |
- ASSERT(SUCCEEDED(hr) && csp && key, (L"")); |
- |
- // Hash the data |
- CryptDetails::scoped_crypt_hash hash; |
- BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash)); |
- if (!b) { |
- // hash is now invalid, but might not be NULL, so stomp on it |
- DWORD err = ::GetLastError(); |
- ASSERT(!hash, (L"")); |
- UTIL_LOG(LE, (L"[CrypoVerifySignature::Validate]" |
- L"[could not create hash], err 0x%08lx", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- UTIL_LOG(L3, (L"CryptoVerifySignature::Validate new hash 0x%08lx", hash)); |
- |
- b = ::CryptHashData(get(hash), |
- &buffer_in.front(), |
- buffer_in.size(), |
- 0 /*flags*/); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
- UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]" |
- L"[could not hash data, err 0x%08lx]", err)); |
- return HRESULT_FROM_WIN32(err); |
- } |
- |
- // Verify the hash |
- b = ::CryptVerifySignature(get(hash), |
- &signature_in.front(), |
- signature_in.size(), |
- key, |
- NULL, |
- 0 /*flags*/); |
- if (!b) { |
- DWORD err = ::GetLastError(); |
-#ifdef LOGGING |
- CString encoded_signature; |
- Base64::Encode(signature_in, &encoded_signature, false); |
- |
- UTIL_LOG(LE, (_T("CryptoVerifySignature::Validate could not ") |
- _T("verify signature, err 0x%08lx with sig \"%s\""), |
- err, encoded_signature)); |
-#endif |
- if (err == NTE_BAD_SIGNATURE) |
- return SIGS_E_INVALID_SIGNATURE; |
- else |
- return HRESULT_FROM_WIN32(err); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT SignData(const TCHAR* certificate_path, |
- const TCHAR* certificate_password, |
- const TCHAR* certificate_subject_name, |
- const std::vector<byte>& data, |
- CString* signature_base64) { |
- ASSERT(certificate_path, (L"")); |
- ASSERT(certificate_password, (L"")); |
- // certificate_subject_name can be NULL |
- ASSERT(signature_base64, (L"")); |
- |
- CryptoSigningCertificate certificate; |
- RET_IF_FAILED(certificate.ImportCertificate(certificate_path, |
- certificate_password, |
- certificate_subject_name)); |
- |
- CryptoComputeSignature signer(&certificate); |
- std::vector<byte> signature; |
- RET_IF_FAILED(signer.Sign(data, &signature)); |
- RET_IF_FAILED(Base64::Encode(signature, signature_base64, false)); |
- |
- return S_OK; |
-} |
- |
-HRESULT VerifyData(const TCHAR* certificate_path, |
- const TCHAR* certificate_subject_name, |
- const std::vector<byte>& data, |
- const TCHAR* signature_base64) { |
- ASSERT(certificate_path, (L"")); |
- // certificate_subject_name can be NULL |
- ASSERT(signature_base64, (L"")); |
- |
- std::vector<byte> signature; |
- RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature)); |
- |
- CryptoSignatureVerificationCertificate certificate; |
- RET_IF_FAILED(certificate.ImportCertificate(certificate_path, |
- certificate_subject_name)); |
- |
- CryptoVerifySignature verifier(certificate); |
- RET_IF_FAILED(verifier.Validate(data, signature)); |
- |
- return S_OK; |
-} |
- |
-HRESULT VerifyData(const std::vector<byte>& certificate_buffer, |
- const TCHAR* certificate_subject_name, |
- const std::vector<byte>& data, |
- const TCHAR* signature_base64) { |
- // certificate_subject_name can be NULL |
- ASSERT(signature_base64, (L"")); |
- |
- std::vector<byte> signature; |
- RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature)); |
- |
- CryptoSignatureVerificationCertificate certificate; |
- RET_IF_FAILED(certificate.ImportCertificate(certificate_buffer, |
- certificate_subject_name)); |
- |
- CryptoVerifySignature verifier(certificate); |
- RET_IF_FAILED(verifier.Validate(data, signature)); |
- |
- return S_OK; |
-} |
- |
-// TODO(omaha): function is missing the unit test. |
-HRESULT AuthenticateFiles(const std::vector<CString>& files, |
- const CString& hash) { |
- ASSERT1(!files.empty()); |
- |
- std::vector<byte> hash_vector; |
- RET_IF_FAILED(Base64::Decode(hash, &hash_vector)); |
- if (hash_vector.size() != CryptoHash::kHashSize) { |
- return E_INVALIDARG; |
- } |
- |
- CryptoHash crypto; |
- return crypto.Validate(files, kMaxFileSizeForAuthentication, hash_vector); |
-} |
- |
-} // namespace omaha |
- |