• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/app/signature_validator_win.h"
6 
7 #include <atlstr.h>
8 #include <softpub.h>
9 #include <wincrypt.h>
10 #include <windows.h>
11 #include <wintrust.h>
12 
13 #include <algorithm>
14 
15 #include "base/files/file_path.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/time/time.h"
20 #include "base/win/scoped_handle.h"
21 #include "crypto/sha2.h"
22 
23 namespace {
24 
ExtractPublicKeyHash(const CERT_CONTEXT * cert_context,std::string * public_key_hash)25 bool ExtractPublicKeyHash(const CERT_CONTEXT* cert_context,
26                           std::string* public_key_hash) {
27   public_key_hash->clear();
28 
29   CRYPT_BIT_BLOB crypt_blob =
30       cert_context->pCertInfo->SubjectPublicKeyInfo.PublicKey;
31 
32   // Key blobs that are not an integral number of bytes are unsupported.
33   if (crypt_blob.cUnusedBits != 0)
34     return false;
35 
36   uint8 hash[crypto::kSHA256Length] = {};
37 
38   base::StringPiece key_bytes(reinterpret_cast<char*>(
39       crypt_blob.pbData), crypt_blob.cbData);
40   crypto::SHA256HashString(key_bytes, hash, crypto::kSHA256Length);
41 
42   *public_key_hash = StringToLowerASCII(base::HexEncode(hash, arraysize(hash)));
43   return true;
44 }
45 
46 // The traits class for HCERTSTORE handles that can be closed via
47 // CertCloseStore() API.
48 class CertStoreHandleTraits {
49  public:
50   typedef HCERTSTORE Handle;
51 
52   // Closes the handle.
CloseHandle(HCERTSTORE handle)53   static bool CloseHandle(HCERTSTORE handle) {
54     return CertCloseStore(handle, 0) != FALSE;
55   }
56 
57   // Returns true if the handle value is valid.
IsHandleValid(HCERTSTORE handle)58   static bool IsHandleValid(HCERTSTORE handle) {
59     return handle != NULL;
60   }
61 
62   // Returns NULL handle value.
NullHandle()63   static HANDLE NullHandle() {
64     return NULL;
65   }
66 
67  private:
68   DISALLOW_IMPLICIT_CONSTRUCTORS(CertStoreHandleTraits);
69 };
70 
71 typedef base::win::GenericScopedHandle<CertStoreHandleTraits,
72     base::win::DummyVerifierTraits> ScopedCertStoreHandle;
73 
74 // Class: CertInfo
75 //
76 // CertInfo holds all sensible details of a certificate. During verification of
77 // a signature, one CertInfo object is made for each certificate encountered in
78 // the signature.
79 class CertInfo {
80  public:
CertInfo(const CERT_CONTEXT * given_cert_context)81   explicit CertInfo(const CERT_CONTEXT* given_cert_context)
82       : cert_context_(NULL) {
83     if (given_cert_context) {
84       // CertDuplicateCertificateContext just increases reference count of a
85       // given CERT_CONTEXT.
86       cert_context_ = CertDuplicateCertificateContext(given_cert_context);
87       not_valid_before_ = cert_context_->pCertInfo->NotBefore;
88       not_valid_after_ = cert_context_->pCertInfo->NotAfter;
89 
90       ExtractPublicKeyHash(cert_context_, &public_key_hash_);
91     }
92   }
93 
~CertInfo()94   ~CertInfo() {  // Decrement reference count, if needed.
95     if (cert_context_) {
96       CertFreeCertificateContext(cert_context_);
97       cert_context_ = NULL;
98     }
99   }
100 
101   // IsValidNow() functions returns true if this certificate is valid at this
102   // moment, based on the validity period specified in the certificate.
IsValidNow() const103   bool IsValidNow() const {
104     // we cannot directly get current time in FILETIME format.
105     // so first get it in SYSTEMTIME format and convert it into FILETIME.
106     base::Time now = base::Time::NowFromSystemTime();
107     FILETIME filetime_now = now.ToFileTime();
108     // CompareFileTime() is a windows function
109     return ((CompareFileTime(&filetime_now, &not_valid_before_) > 0) &&
110             (CompareFileTime(&filetime_now, &not_valid_after_) < 0));
111   }
112 
public_key_hash() const113   std::string public_key_hash() const {
114     return public_key_hash_;
115   }
116 
117  private:
118   // cert_context structure, defined by Crypto API, contains all the info
119   // about the certificate.
120   const CERT_CONTEXT* cert_context_;
121 
122   // Lower-case hex SHA-256 hash of the certificate subject's public key.
123   std::string public_key_hash_;
124 
125   // Validity period start-date
126   FILETIME not_valid_before_;
127 
128   // Validity period end-date
129   FILETIME not_valid_after_;
130 };
131 
132 }  // namespace
133 
VerifyAuthenticodeSignature(const base::FilePath & signed_file)134 bool VerifyAuthenticodeSignature(const base::FilePath& signed_file) {
135   // Don't pop up any windows
136   const HWND window_mode = reinterpret_cast<HWND>(INVALID_HANDLE_VALUE);
137 
138   // Verify file & certificates using the Microsoft Authenticode Policy
139   // Provider.
140   GUID verification_type = WINTRUST_ACTION_GENERIC_VERIFY_V2;
141 
142   // Info for the file we're going to verify.
143   WINTRUST_FILE_INFO file_info = {};
144   file_info.cbStruct = sizeof(file_info);
145   file_info.pcwszFilePath = signed_file.value().c_str();
146 
147   // Info for request to WinVerifyTrust.
148   WINTRUST_DATA trust_data = {};
149   trust_data.cbStruct = sizeof(trust_data);
150   trust_data.dwUIChoice = WTD_UI_NONE;               // no graphics
151   trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;  // no revocation checking
152   trust_data.dwUnionChoice = WTD_CHOICE_FILE;        // check a file
153   trust_data.pFile = &file_info;                     // check this file
154   trust_data.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
155 
156   // From the WinVerifyTrust documentation:
157   //   http://msdn2.microsoft.com/en-us/library/aa388208.aspx:
158   //   "If the trust provider verifies that the subject is trusted
159   //   for the specified action, the return value is zero. No other
160   //   value besides zero should be considered a successful return."
161   LONG result = WinVerifyTrust(window_mode, &verification_type, &trust_data);
162   return !result;
163 }
164 
VerifySignerIsGoogle(const base::FilePath & signed_file,const std::string & subject_name,const std::vector<std::string> & expected_hashes)165 bool VerifySignerIsGoogle(const base::FilePath& signed_file,
166                           const std::string& subject_name,
167                           const std::vector<std::string>& expected_hashes) {
168   if (signed_file.empty())
169     return false;
170 
171   // If successful, cert_store will be populated by a store containing all the
172   // certificates related to the file signature.
173   HCERTSTORE cert_store = NULL;
174 
175   BOOL succeeded = CryptQueryObject(
176       CERT_QUERY_OBJECT_FILE,
177       signed_file.value().c_str(),
178       CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
179       CERT_QUERY_FORMAT_FLAG_ALL,
180       0,               // Reserved: must be 0.
181       NULL,
182       NULL,
183       NULL,
184       &cert_store,
185       NULL,            // Do not return the message.
186       NULL);           // Do not return additional context.
187 
188   ScopedCertStoreHandle scoped_cert_store(cert_store);
189 
190   if (!succeeded || !scoped_cert_store.IsValid())
191     return false;
192 
193   PCCERT_CONTEXT cert_context_ptr = NULL;
194   cert_context_ptr = CertFindCertificateInStore(
195       cert_store,
196       X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
197       0,
198       CERT_FIND_SUBJECT_STR,
199       base::UTF8ToWide(subject_name).c_str(),
200       cert_context_ptr);
201 
202   // No cert found with this subject, so stop here.
203   if (!cert_context_ptr)
204     return false;
205 
206   CertInfo cert_info(cert_context_ptr);
207 
208   CertFreeCertificateContext(cert_context_ptr);
209   cert_context_ptr = NULL;
210 
211   // Check the hashes to make sure subject isn't being faked, and the time
212   // to make sure the cert is current.
213   std::vector<std::string>::const_iterator it = std::find(
214       expected_hashes.begin(),
215       expected_hashes.end(),
216       cert_info.public_key_hash());
217   if (it == expected_hashes.end() || !cert_info.IsValidNow())
218     return false;
219 
220   return true;
221 }
222