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