Home | History | Annotate | Download | only in crypto
      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 "net/quic/crypto/proof_verifier_chromium.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback_helpers.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/logging.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/stl_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "crypto/signature_verifier.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/base/net_log.h"
     18 #include "net/cert/asn1_util.h"
     19 #include "net/cert/cert_status_flags.h"
     20 #include "net/cert/cert_verifier.h"
     21 #include "net/cert/cert_verify_result.h"
     22 #include "net/cert/single_request_cert_verifier.h"
     23 #include "net/cert/x509_certificate.h"
     24 #include "net/cert/x509_util.h"
     25 #include "net/http/transport_security_state.h"
     26 #include "net/quic/crypto/crypto_protocol.h"
     27 #include "net/ssl/ssl_config_service.h"
     28 
     29 using base::StringPiece;
     30 using base::StringPrintf;
     31 using std::string;
     32 using std::vector;
     33 
     34 namespace net {
     35 
     36 // A Job handles the verification of a single proof.  It is owned by the
     37 // ProofVerifier. If the verification can not complete synchronously, it
     38 // will notify the ProofVerifier upon completion.
     39 class ProofVerifierChromium::Job {
     40  public:
     41   Job(ProofVerifierChromium* proof_verifier,
     42       CertVerifier* cert_verifier,
     43       TransportSecurityState* transport_security_state,
     44       const BoundNetLog& net_log);
     45 
     46   // Starts the proof verification.  If |QUIC_PENDING| is returned, then
     47   // |callback| will be invoked asynchronously when the verification completes.
     48   QuicAsyncStatus VerifyProof(const std::string& hostname,
     49                               const std::string& server_config,
     50                               const std::vector<std::string>& certs,
     51                               const std::string& signature,
     52                               std::string* error_details,
     53                               scoped_ptr<ProofVerifyDetails>* verify_details,
     54                               ProofVerifierCallback* callback);
     55 
     56  private:
     57   enum State {
     58     STATE_NONE,
     59     STATE_VERIFY_CERT,
     60     STATE_VERIFY_CERT_COMPLETE,
     61   };
     62 
     63   int DoLoop(int last_io_result);
     64   void OnIOComplete(int result);
     65   int DoVerifyCert(int result);
     66   int DoVerifyCertComplete(int result);
     67 
     68   bool VerifySignature(const std::string& signed_data,
     69                        const std::string& signature,
     70                        const std::string& cert);
     71 
     72   // Proof verifier to notify when this jobs completes.
     73   ProofVerifierChromium* proof_verifier_;
     74 
     75   // The underlying verifier used for verifying certificates.
     76   scoped_ptr<SingleRequestCertVerifier> verifier_;
     77 
     78   TransportSecurityState* transport_security_state_;
     79 
     80   // |hostname| specifies the hostname for which |certs| is a valid chain.
     81   std::string hostname_;
     82 
     83   scoped_ptr<ProofVerifierCallback> callback_;
     84   scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
     85   std::string error_details_;
     86 
     87   // X509Certificate from a chain of DER encoded certificates.
     88   scoped_refptr<X509Certificate> cert_;
     89 
     90   State next_state_;
     91 
     92   BoundNetLog net_log_;
     93 
     94   DISALLOW_COPY_AND_ASSIGN(Job);
     95 };
     96 
     97 ProofVerifierChromium::Job::Job(
     98     ProofVerifierChromium* proof_verifier,
     99     CertVerifier* cert_verifier,
    100     TransportSecurityState* transport_security_state,
    101     const BoundNetLog& net_log)
    102     : proof_verifier_(proof_verifier),
    103       verifier_(new SingleRequestCertVerifier(cert_verifier)),
    104       transport_security_state_(transport_security_state),
    105       next_state_(STATE_NONE),
    106       net_log_(net_log) {
    107 }
    108 
    109 QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof(
    110     const string& hostname,
    111     const string& server_config,
    112     const vector<string>& certs,
    113     const string& signature,
    114     std::string* error_details,
    115     scoped_ptr<ProofVerifyDetails>* verify_details,
    116     ProofVerifierCallback* callback) {
    117   DCHECK(error_details);
    118   DCHECK(verify_details);
    119   DCHECK(callback);
    120 
    121   error_details->clear();
    122 
    123   if (STATE_NONE != next_state_) {
    124     *error_details = "Certificate is already set and VerifyProof has begun";
    125     DLOG(DFATAL) << *error_details;
    126     return QUIC_FAILURE;
    127   }
    128 
    129   verify_details_.reset(new ProofVerifyDetailsChromium);
    130 
    131   if (certs.empty()) {
    132     *error_details = "Failed to create certificate chain. Certs are empty.";
    133     DLOG(WARNING) << *error_details;
    134     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    135     verify_details->reset(verify_details_.release());
    136     return QUIC_FAILURE;
    137   }
    138 
    139   // Convert certs to X509Certificate.
    140   vector<StringPiece> cert_pieces(certs.size());
    141   for (unsigned i = 0; i < certs.size(); i++) {
    142     cert_pieces[i] = base::StringPiece(certs[i]);
    143   }
    144   cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces);
    145   if (!cert_.get()) {
    146     *error_details = "Failed to create certificate chain";
    147     DLOG(WARNING) << *error_details;
    148     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    149     verify_details->reset(verify_details_.release());
    150     return QUIC_FAILURE;
    151   }
    152 
    153   // We call VerifySignature first to avoid copying of server_config and
    154   // signature.
    155   if (!VerifySignature(server_config, signature, certs[0])) {
    156     *error_details = "Failed to verify signature of server config";
    157     DLOG(WARNING) << *error_details;
    158     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    159     verify_details->reset(verify_details_.release());
    160     return QUIC_FAILURE;
    161   }
    162 
    163   hostname_ = hostname;
    164 
    165   next_state_ = STATE_VERIFY_CERT;
    166   switch (DoLoop(OK)) {
    167     case OK:
    168       verify_details->reset(verify_details_.release());
    169       return QUIC_SUCCESS;
    170     case ERR_IO_PENDING:
    171       callback_.reset(callback);
    172       return QUIC_PENDING;
    173     default:
    174       *error_details = error_details_;
    175       verify_details->reset(verify_details_.release());
    176       return QUIC_FAILURE;
    177   }
    178 }
    179 
    180 int ProofVerifierChromium::Job::DoLoop(int last_result) {
    181   int rv = last_result;
    182   do {
    183     State state = next_state_;
    184     next_state_ = STATE_NONE;
    185     switch (state) {
    186       case STATE_VERIFY_CERT:
    187         DCHECK(rv == OK);
    188         rv = DoVerifyCert(rv);
    189         break;
    190       case STATE_VERIFY_CERT_COMPLETE:
    191         rv = DoVerifyCertComplete(rv);
    192         break;
    193       case STATE_NONE:
    194       default:
    195         rv = ERR_UNEXPECTED;
    196         LOG(DFATAL) << "unexpected state " << state;
    197         break;
    198     }
    199   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    200   return rv;
    201 }
    202 
    203 void ProofVerifierChromium::Job::OnIOComplete(int result) {
    204   int rv = DoLoop(result);
    205   if (rv != ERR_IO_PENDING) {
    206     scoped_ptr<ProofVerifierCallback> callback(callback_.release());
    207     // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
    208     scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.release());
    209     callback->Run(rv == OK, error_details_, &verify_details);
    210     // Will delete |this|.
    211     proof_verifier_->OnJobComplete(this);
    212   }
    213 }
    214 
    215 int ProofVerifierChromium::Job::DoVerifyCert(int result) {
    216   next_state_ = STATE_VERIFY_CERT_COMPLETE;
    217 
    218   int flags = 0;
    219   return verifier_->Verify(
    220       cert_.get(),
    221       hostname_,
    222       flags,
    223       SSLConfigService::GetCRLSet().get(),
    224       &verify_details_->cert_verify_result,
    225       base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
    226                  base::Unretained(this)),
    227       net_log_);
    228 }
    229 
    230 int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) {
    231   verifier_.reset();
    232 
    233 #if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS)
    234   // TODO(wtc): The following code was copied from ssl_client_socket_nss.cc.
    235   // Convert it to a new function that can be called by both files. These
    236   // variables simulate the arguments to the new function.
    237   const CertVerifyResult& cert_verify_result =
    238       verify_details_->cert_verify_result;
    239   bool sni_available = true;
    240   const std::string& host = hostname_;
    241   TransportSecurityState* transport_security_state = transport_security_state_;
    242   std::string* pinning_failure_log = &verify_details_->pinning_failure_log;
    243 
    244   // Take care of any mandates for public key pinning.
    245   //
    246   // Pinning is only enabled for official builds to make sure that others don't
    247   // end up with pins that cannot be easily updated.
    248   //
    249   // TODO(agl): We might have an issue here where a request for foo.example.com
    250   // merges into a SPDY connection to www.example.com, and gets a different
    251   // certificate.
    252 
    253   // Perform pin validation if, and only if, all these conditions obtain:
    254   //
    255   // * a TransportSecurityState object is available;
    256   // * the server's certificate chain is valid (or suffers from only a minor
    257   //   error);
    258   // * the server's certificate chain chains up to a known root (i.e. not a
    259   //   user-installed trust anchor); and
    260   // * the build is recent (very old builds should fail open so that users
    261   //   have some chance to recover).
    262   //
    263   const CertStatus cert_status = cert_verify_result.cert_status;
    264   if (transport_security_state &&
    265       (result == OK ||
    266        (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) &&
    267       cert_verify_result.is_issued_by_known_root &&
    268       TransportSecurityState::IsBuildTimely()) {
    269     if (transport_security_state->HasPublicKeyPins(host, sni_available)) {
    270       if (!transport_security_state->CheckPublicKeyPins(
    271               host,
    272               sni_available,
    273               cert_verify_result.public_key_hashes,
    274               pinning_failure_log)) {
    275         LOG(ERROR) << *pinning_failure_log;
    276         result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
    277         UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false);
    278         TransportSecurityState::ReportUMAOnPinFailure(host);
    279       } else {
    280         UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true);
    281       }
    282     }
    283   }
    284 #endif
    285 
    286   if (result <= ERR_FAILED) {
    287     error_details_ = StringPrintf("Failed to verify certificate chain: %s",
    288                                   ErrorToString(result));
    289     DLOG(WARNING) << error_details_;
    290     result = ERR_FAILED;
    291   }
    292 
    293   // Exit DoLoop and return the result to the caller to VerifyProof.
    294   DCHECK_EQ(STATE_NONE, next_state_);
    295   return result;
    296 }
    297 
    298 bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data,
    299                                             const string& signature,
    300                                             const string& cert) {
    301   StringPiece spki;
    302   if (!asn1::ExtractSPKIFromDERCert(cert, &spki)) {
    303     DLOG(WARNING) << "ExtractSPKIFromDERCert failed";
    304     return false;
    305   }
    306 
    307   crypto::SignatureVerifier verifier;
    308 
    309   size_t size_bits;
    310   X509Certificate::PublicKeyType type;
    311   X509Certificate::GetPublicKeyInfo(cert_->os_cert_handle(), &size_bits,
    312                                     &type);
    313   if (type == X509Certificate::kPublicKeyTypeRSA) {
    314     crypto::SignatureVerifier::HashAlgorithm hash_alg =
    315         crypto::SignatureVerifier::SHA256;
    316     crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg;
    317     unsigned int hash_len = 32;  // 32 is the length of a SHA-256 hash.
    318 
    319     bool ok = verifier.VerifyInitRSAPSS(
    320         hash_alg, mask_hash_alg, hash_len,
    321         reinterpret_cast<const uint8*>(signature.data()), signature.size(),
    322         reinterpret_cast<const uint8*>(spki.data()), spki.size());
    323     if (!ok) {
    324       DLOG(WARNING) << "VerifyInitRSAPSS failed";
    325       return false;
    326     }
    327   } else if (type == X509Certificate::kPublicKeyTypeECDSA) {
    328     // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
    329     // RFC 5758:
    330     //   ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    331     //        us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
    332     //   ...
    333     //   When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
    334     //   ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
    335     //   as an AlgorithmIdentifier, the encoding MUST omit the parameters
    336     //   field.  That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
    337     //   component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
    338     //   SHA384, or ecdsa-with-SHA512.
    339     // See also RFC 5480, Appendix A.
    340     static const uint8 kECDSAWithSHA256AlgorithmID[] = {
    341       0x30, 0x0a,
    342         0x06, 0x08,
    343           0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
    344     };
    345 
    346     if (!verifier.VerifyInit(
    347             kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
    348             reinterpret_cast<const uint8*>(signature.data()),
    349             signature.size(),
    350             reinterpret_cast<const uint8*>(spki.data()),
    351             spki.size())) {
    352       DLOG(WARNING) << "VerifyInit failed";
    353       return false;
    354     }
    355   } else {
    356     LOG(ERROR) << "Unsupported public key type " << type;
    357     return false;
    358   }
    359 
    360   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(kProofSignatureLabel),
    361                         sizeof(kProofSignatureLabel));
    362   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
    363                         signed_data.size());
    364 
    365   if (!verifier.VerifyFinal()) {
    366     DLOG(WARNING) << "VerifyFinal failed";
    367     return false;
    368   }
    369 
    370   DVLOG(1) << "VerifyFinal success";
    371   return true;
    372 }
    373 
    374 ProofVerifierChromium::ProofVerifierChromium(
    375     CertVerifier* cert_verifier,
    376     TransportSecurityState* transport_security_state)
    377     : cert_verifier_(cert_verifier),
    378       transport_security_state_(transport_security_state) {
    379 }
    380 
    381 ProofVerifierChromium::~ProofVerifierChromium() {
    382   STLDeleteElements(&active_jobs_);
    383 }
    384 
    385 QuicAsyncStatus ProofVerifierChromium::VerifyProof(
    386     const std::string& hostname,
    387     const std::string& server_config,
    388     const std::vector<std::string>& certs,
    389     const std::string& signature,
    390     const ProofVerifyContext* verify_context,
    391     std::string* error_details,
    392     scoped_ptr<ProofVerifyDetails>* verify_details,
    393     ProofVerifierCallback* callback) {
    394   if (!verify_context) {
    395     *error_details = "Missing context";
    396     return QUIC_FAILURE;
    397   }
    398   const ProofVerifyContextChromium* chromium_context =
    399       reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
    400   scoped_ptr<Job> job(new Job(this,
    401                               cert_verifier_,
    402                               transport_security_state_,
    403                               chromium_context->net_log));
    404   QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs,
    405                                             signature, error_details,
    406                                             verify_details, callback);
    407   if (status == QUIC_PENDING) {
    408     active_jobs_.insert(job.release());
    409   }
    410   return status;
    411 }
    412 
    413 void ProofVerifierChromium::OnJobComplete(Job* job) {
    414   active_jobs_.erase(job);
    415   delete job;
    416 }
    417 
    418 }  // namespace net
    419