1// Copyright (c) 2011 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 "crypto/signature_verifier.h"
6
7#include "base/logging.h"
8
9#pragma comment(lib, "crypt32.lib")
10
11namespace {
12
13// Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the
14// WINAPI calling convention.
15void* WINAPI MyCryptAlloc(size_t size) {
16  return malloc(size);
17}
18
19void WINAPI MyCryptFree(void* p) {
20  free(p);
21}
22
23}  // namespace
24
25namespace crypto {
26
27SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) {
28  if (!CryptAcquireContext(provider_.receive(), NULL, NULL,
29                           PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
30    provider_.reset();
31}
32
33SignatureVerifier::~SignatureVerifier() {
34}
35
36bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
37                                   int signature_algorithm_len,
38                                   const uint8* signature,
39                                   int signature_len,
40                                   const uint8* public_key_info,
41                                   int public_key_info_len) {
42  signature_.reserve(signature_len);
43  // CryptoAPI uses big integers in the little-endian byte order, so we need
44  // to first swap the order of signature bytes.
45  for (int i = signature_len - 1; i >= 0; --i)
46    signature_.push_back(signature[i]);
47
48  CRYPT_DECODE_PARA decode_para;
49  decode_para.cbSize = sizeof(decode_para);
50  decode_para.pfnAlloc = MyCryptAlloc;
51  decode_para.pfnFree = MyCryptFree;
52  CERT_PUBLIC_KEY_INFO* cert_public_key_info = NULL;
53  DWORD struct_len = 0;
54  BOOL ok;
55  ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
56                           X509_PUBLIC_KEY_INFO,
57                           public_key_info,
58                           public_key_info_len,
59                           CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
60                           &decode_para,
61                           &cert_public_key_info,
62                           &struct_len);
63  if (!ok)
64    return false;
65
66  ok = CryptImportPublicKeyInfo(provider_,
67                                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
68                                cert_public_key_info, public_key_.receive());
69  free(cert_public_key_info);
70  if (!ok)
71    return false;
72
73  CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id;
74  struct_len = 0;
75  ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
76                           X509_ALGORITHM_IDENTIFIER,
77                           signature_algorithm,
78                           signature_algorithm_len,
79                           CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
80                           &decode_para,
81                           &signature_algorithm_id,
82                           &struct_len);
83  DCHECK(ok || GetLastError() == ERROR_FILE_NOT_FOUND);
84  ALG_ID hash_alg_id;
85  if (ok) {
86    hash_alg_id = CALG_MD4;  // Initialize to a weak hash algorithm that we
87                             // don't support.
88    if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA))
89      hash_alg_id = CALG_SHA1;
90    else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA))
91      hash_alg_id = CALG_MD5;
92    free(signature_algorithm_id);
93    DCHECK(hash_alg_id != CALG_MD4);
94    if (hash_alg_id == CALG_MD4)
95      return false;  // Unsupported hash algorithm.
96  } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
97    // TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2.  We
98    // may be able to encapsulate signature_algorithm in a dummy SignedContent
99    // and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO.  For now,
100    // just hardcode the hash algorithm to be SHA-1.
101    hash_alg_id = CALG_SHA1;
102  } else {
103    return false;
104  }
105
106  ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, hash_object_.receive());
107  if (!ok)
108    return false;
109  return true;
110}
111
112void SignatureVerifier::VerifyUpdate(const uint8* data_part,
113                                     int data_part_len) {
114  BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0);
115  DCHECK(ok) << "CryptHashData failed: " << GetLastError();
116}
117
118bool SignatureVerifier::VerifyFinal() {
119  BOOL ok = CryptVerifySignature(hash_object_, &signature_[0],
120                                 signature_.size(), public_key_, NULL, 0);
121  Reset();
122  if (!ok)
123    return false;
124  return true;
125}
126
127void SignatureVerifier::Reset() {
128  hash_object_.reset();
129  public_key_.reset();
130  signature_.clear();
131}
132
133}  // namespace crypto
134
135