Home | History | Annotate | Download | only in crypto
      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 
     11 namespace {
     12 
     13 // Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the
     14 // WINAPI calling convention.
     15 void* WINAPI MyCryptAlloc(size_t size) {
     16   return malloc(size);
     17 }
     18 
     19 void WINAPI MyCryptFree(void* p) {
     20   free(p);
     21 }
     22 
     23 }  // namespace
     24 
     25 namespace crypto {
     26 
     27 SignatureVerifier::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 
     33 SignatureVerifier::~SignatureVerifier() {
     34 }
     35 
     36 bool 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 
    112 void 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 
    118 bool 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 
    127 void SignatureVerifier::Reset() {
    128   hash_object_.reset();
    129   public_key_.reset();
    130   signature_.clear();
    131 }
    132 
    133 }  // namespace crypto
    134 
    135