Home | History | Annotate | Download | only in cert
      1 // Copyright 2014 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 "net/cert/sha256_legacy_support_win.h"
      6 
      7 #include <windows.h>
      8 #include <wincrypt.h>
      9 
     10 #include <cert.h>
     11 #include <keyhi.h>
     12 #include <secoid.h>
     13 
     14 #include "base/lazy_instance.h"
     15 #include "base/logging.h"
     16 #include "base/strings/string_piece.h"
     17 #include "base/win/windows_version.h"
     18 #include "crypto/scoped_nss_types.h"
     19 
     20 namespace net {
     21 
     22 namespace sha256_interception {
     23 
     24 namespace {
     25 
     26 bool IsSupportedSubjectType(DWORD subject_type) {
     27   switch (subject_type) {
     28     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB:
     29     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT:
     30     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL:
     31       return true;
     32   }
     33   return false;
     34 }
     35 
     36 bool IsSupportedIssuerType(DWORD issuer_type) {
     37   switch (issuer_type) {
     38     case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
     39     case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT:
     40     case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN:
     41       return true;
     42   }
     43   return false;
     44 }
     45 
     46 base::StringPiece GetSubjectSignature(DWORD subject_type,
     47                                       void* subject_data) {
     48   switch (subject_type) {
     49     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB: {
     50       CRYPT_DATA_BLOB* data_blob =
     51           reinterpret_cast<CRYPT_DATA_BLOB*>(subject_data);
     52       return base::StringPiece(reinterpret_cast<char*>(data_blob->pbData),
     53                                data_blob->cbData);
     54     }
     55     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT: {
     56       PCCERT_CONTEXT subject_cert =
     57           reinterpret_cast<PCCERT_CONTEXT>(subject_data);
     58       return base::StringPiece(
     59           reinterpret_cast<char*>(subject_cert->pbCertEncoded),
     60           subject_cert->cbCertEncoded);
     61     }
     62     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL: {
     63       PCCRL_CONTEXT subject_crl =
     64           reinterpret_cast<PCCRL_CONTEXT>(subject_data);
     65       return base::StringPiece(
     66           reinterpret_cast<char*>(subject_crl->pbCrlEncoded),
     67           subject_crl->cbCrlEncoded);
     68     }
     69   }
     70   return base::StringPiece();
     71 }
     72 
     73 PCERT_PUBLIC_KEY_INFO GetIssuerPublicKey(DWORD issuer_type,
     74                                          void* issuer_data) {
     75   switch (issuer_type) {
     76     case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
     77       return reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(issuer_data);
     78     case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT: {
     79       PCCERT_CONTEXT cert = reinterpret_cast<PCCERT_CONTEXT>(issuer_data);
     80       return &cert->pCertInfo->SubjectPublicKeyInfo;
     81     }
     82     case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: {
     83       PCCERT_CHAIN_CONTEXT chain =
     84           reinterpret_cast<PCCERT_CHAIN_CONTEXT>(issuer_data);
     85       PCCERT_CONTEXT cert = chain->rgpChain[0]->rgpElement[0]->pCertContext;
     86       return &cert->pCertInfo->SubjectPublicKeyInfo;
     87     }
     88   }
     89   return NULL;
     90 }
     91 
     92 }  // namespace
     93 
     94 BOOL CryptVerifyCertificateSignatureExHook(
     95     CryptVerifyCertificateSignatureExFunc original_func,
     96     HCRYPTPROV_LEGACY provider,
     97     DWORD encoding_type,
     98     DWORD subject_type,
     99     void* subject_data,
    100     DWORD issuer_type,
    101     void* issuer_data,
    102     DWORD flags,
    103     void* extra) {
    104   CHECK(original_func);
    105 
    106   // Only intercept if the arguments are supported.
    107   if (provider != NULL || (encoding_type != X509_ASN_ENCODING) ||
    108       !IsSupportedSubjectType(subject_type) || subject_data == NULL ||
    109       !IsSupportedIssuerType(issuer_type) || issuer_data == NULL) {
    110     return original_func(provider, encoding_type, subject_type, subject_data,
    111                          issuer_type, issuer_data, flags, extra);
    112   }
    113 
    114   base::StringPiece subject_signature =
    115       GetSubjectSignature(subject_type, subject_data);
    116   bool should_intercept = false;
    117 
    118   crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    119   CERTSignedData signed_data;
    120   memset(&signed_data, 0, sizeof(signed_data));
    121 
    122   // Attempt to decode the subject using the generic "Signed Data" template,
    123   // which all of the supported subject types match. If the signature
    124   // algorithm is RSA with one of the SHA-2 algorithms supported by NSS
    125   // (excluding SHA-224, which is pointless), then defer to the NSS
    126   // implementation. Otherwise, fall back and let the OS handle it (e.g.
    127   // in case there are any algorithm policies in effect).
    128   if (!subject_signature.empty()) {
    129     SECItem subject_sig_item;
    130     subject_sig_item.data = const_cast<unsigned char*>(
    131         reinterpret_cast<const unsigned char*>(subject_signature.data()));
    132     subject_sig_item.len = subject_signature.size();
    133     SECStatus rv = SEC_QuickDERDecodeItem(
    134         arena.get(), &signed_data, SEC_ASN1_GET(CERT_SignedDataTemplate),
    135         &subject_sig_item);
    136     if (rv == SECSuccess) {
    137       SECOidTag signature_alg =
    138           SECOID_GetAlgorithmTag(&signed_data.signatureAlgorithm);
    139       if (signature_alg == SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION ||
    140           signature_alg == SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION ||
    141           signature_alg == SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION) {
    142         should_intercept = true;
    143       }
    144     }
    145   }
    146 
    147   if (!should_intercept) {
    148     return original_func(provider, encoding_type, subject_type, subject_data,
    149                          issuer_type, issuer_data, flags, extra);
    150   }
    151 
    152   // Rather than attempting to synthesize a CERTSubjectPublicKeyInfo by hand,
    153   // just force the OS to do an ASN.1 encoding and then decode it back into
    154   // NSS. This is silly for performance, but safest for consistency.
    155   PCERT_PUBLIC_KEY_INFO issuer_public_key =
    156       GetIssuerPublicKey(issuer_type, issuer_data);
    157   if (!issuer_public_key) {
    158     SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
    159     return FALSE;
    160   }
    161 
    162   unsigned char* issuer_spki_data = NULL;
    163   DWORD issuer_spki_len = 0;
    164   if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
    165                            issuer_public_key, CRYPT_ENCODE_ALLOC_FLAG, NULL,
    166                            &issuer_spki_data, &issuer_spki_len)) {
    167     return FALSE;
    168   }
    169 
    170   SECItem nss_issuer_spki;
    171   nss_issuer_spki.data = issuer_spki_data;
    172   nss_issuer_spki.len = issuer_spki_len;
    173   CERTSubjectPublicKeyInfo* spki =
    174       SECKEY_DecodeDERSubjectPublicKeyInfo(&nss_issuer_spki);
    175   ::LocalFree(issuer_spki_data);
    176   if (!spki) {
    177     SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
    178     return FALSE;
    179   }
    180 
    181   // Attempt to actually verify the signed data. If it fails, synthesize the
    182   // failure as a generic "bad signature" and let CryptoAPI handle the rest.
    183   SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
    184       &signed_data, spki, NULL);
    185   SECKEY_DestroySubjectPublicKeyInfo(spki);
    186   if (rv != SECSuccess) {
    187     SetLastError(static_cast<DWORD>(NTE_BAD_SIGNATURE));
    188     return FALSE;
    189   }
    190   return TRUE;
    191 }
    192 
    193 }  // namespace sha256_interception
    194 
    195 }  // namespace net