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 ProofVerifyDetails* ProofVerifyDetailsChromium::Clone() const {
     37   ProofVerifyDetailsChromium* other = new ProofVerifyDetailsChromium;
     38   other->cert_verify_result = cert_verify_result;
     39   return other;
     40 }
     41 
     42 // A Job handles the verification of a single proof.  It is owned by the
     43 // ProofVerifier. If the verification can not complete synchronously, it
     44 // will notify the ProofVerifier upon completion.
     45 class ProofVerifierChromium::Job {
     46  public:
     47   Job(ProofVerifierChromium* proof_verifier,
     48       CertVerifier* cert_verifier,
     49       TransportSecurityState* transport_security_state,
     50       const BoundNetLog& net_log);
     51 
     52   // Starts the proof verification.  If |QUIC_PENDING| is returned, then
     53   // |callback| will be invoked asynchronously when the verification completes.
     54   QuicAsyncStatus VerifyProof(const std::string& hostname,
     55                               const std::string& server_config,
     56                               const std::vector<std::string>& certs,
     57                               const std::string& signature,
     58                               std::string* error_details,
     59                               scoped_ptr<ProofVerifyDetails>* verify_details,
     60                               ProofVerifierCallback* callback);
     61 
     62  private:
     63   enum State {
     64     STATE_NONE,
     65     STATE_VERIFY_CERT,
     66     STATE_VERIFY_CERT_COMPLETE,
     67   };
     68 
     69   int DoLoop(int last_io_result);
     70   void OnIOComplete(int result);
     71   int DoVerifyCert(int result);
     72   int DoVerifyCertComplete(int result);
     73 
     74   bool VerifySignature(const std::string& signed_data,
     75                        const std::string& signature,
     76                        const std::string& cert);
     77 
     78   // Proof verifier to notify when this jobs completes.
     79   ProofVerifierChromium* proof_verifier_;
     80 
     81   // The underlying verifier used for verifying certificates.
     82   scoped_ptr<SingleRequestCertVerifier> verifier_;
     83 
     84   TransportSecurityState* transport_security_state_;
     85 
     86   // |hostname| specifies the hostname for which |certs| is a valid chain.
     87   std::string hostname_;
     88 
     89   scoped_ptr<ProofVerifierCallback> callback_;
     90   scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
     91   std::string error_details_;
     92 
     93   // X509Certificate from a chain of DER encoded certificates.
     94   scoped_refptr<X509Certificate> cert_;
     95 
     96   State next_state_;
     97 
     98   BoundNetLog net_log_;
     99 
    100   DISALLOW_COPY_AND_ASSIGN(Job);
    101 };
    102 
    103 ProofVerifierChromium::Job::Job(
    104     ProofVerifierChromium* proof_verifier,
    105     CertVerifier* cert_verifier,
    106     TransportSecurityState* transport_security_state,
    107     const BoundNetLog& net_log)
    108     : proof_verifier_(proof_verifier),
    109       verifier_(new SingleRequestCertVerifier(cert_verifier)),
    110       transport_security_state_(transport_security_state),
    111       next_state_(STATE_NONE),
    112       net_log_(net_log) {
    113 }
    114 
    115 QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof(
    116     const string& hostname,
    117     const string& server_config,
    118     const vector<string>& certs,
    119     const string& signature,
    120     std::string* error_details,
    121     scoped_ptr<ProofVerifyDetails>* verify_details,
    122     ProofVerifierCallback* callback) {
    123   DCHECK(error_details);
    124   DCHECK(verify_details);
    125   DCHECK(callback);
    126 
    127   error_details->clear();
    128 
    129   if (STATE_NONE != next_state_) {
    130     *error_details = "Certificate is already set and VerifyProof has begun";
    131     DLOG(DFATAL) << *error_details;
    132     return QUIC_FAILURE;
    133   }
    134 
    135   verify_details_.reset(new ProofVerifyDetailsChromium);
    136 
    137   if (certs.empty()) {
    138     *error_details = "Failed to create certificate chain. Certs are empty.";
    139     DLOG(WARNING) << *error_details;
    140     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    141     *verify_details = verify_details_.Pass();
    142     return QUIC_FAILURE;
    143   }
    144 
    145   // Convert certs to X509Certificate.
    146   vector<StringPiece> cert_pieces(certs.size());
    147   for (unsigned i = 0; i < certs.size(); i++) {
    148     cert_pieces[i] = base::StringPiece(certs[i]);
    149   }
    150   cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces);
    151   if (!cert_.get()) {
    152     *error_details = "Failed to create certificate chain";
    153     DLOG(WARNING) << *error_details;
    154     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    155     *verify_details = verify_details_.Pass();
    156     return QUIC_FAILURE;
    157   }
    158 
    159   // We call VerifySignature first to avoid copying of server_config and
    160   // signature.
    161   if (!VerifySignature(server_config, signature, certs[0])) {
    162     *error_details = "Failed to verify signature of server config";
    163     DLOG(WARNING) << *error_details;
    164     verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
    165     *verify_details = verify_details_.Pass();
    166     return QUIC_FAILURE;
    167   }
    168 
    169   hostname_ = hostname;
    170 
    171   next_state_ = STATE_VERIFY_CERT;
    172   switch (DoLoop(OK)) {
    173     case OK:
    174       *verify_details = verify_details_.Pass();
    175       return QUIC_SUCCESS;
    176     case ERR_IO_PENDING:
    177       callback_.reset(callback);
    178       return QUIC_PENDING;
    179     default:
    180       *error_details = error_details_;
    181       *verify_details = verify_details_.Pass();
    182       return QUIC_FAILURE;
    183   }
    184 }
    185 
    186 int ProofVerifierChromium::Job::DoLoop(int last_result) {
    187   int rv = last_result;
    188   do {
    189     State state = next_state_;
    190     next_state_ = STATE_NONE;
    191     switch (state) {
    192       case STATE_VERIFY_CERT:
    193         DCHECK(rv == OK);
    194         rv = DoVerifyCert(rv);
    195         break;
    196       case STATE_VERIFY_CERT_COMPLETE:
    197         rv = DoVerifyCertComplete(rv);
    198         break;
    199       case STATE_NONE:
    200       default:
    201         rv = ERR_UNEXPECTED;
    202         LOG(DFATAL) << "unexpected state " << state;
    203         break;
    204     }
    205   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    206   return rv;
    207 }
    208 
    209 void ProofVerifierChromium::Job::OnIOComplete(int result) {
    210   int rv = DoLoop(result);
    211   if (rv != ERR_IO_PENDING) {
    212     scoped_ptr<ProofVerifierCallback> callback(callback_.Pass());
    213     // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
    214     scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.Pass());
    215     callback->Run(rv == OK, error_details_, &verify_details);
    216     // Will delete |this|.
    217     proof_verifier_->OnJobComplete(this);
    218   }
    219 }
    220 
    221 int ProofVerifierChromium::Job::DoVerifyCert(int result) {
    222   next_state_ = STATE_VERIFY_CERT_COMPLETE;
    223 
    224   int flags = 0;
    225   return verifier_->Verify(
    226       cert_.get(),
    227       hostname_,
    228       flags,
    229       SSLConfigService::GetCRLSet().get(),
    230       &verify_details_->cert_verify_result,
    231       base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
    232                  base::Unretained(this)),
    233       net_log_);
    234 }
    235 
    236 int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) {
    237   verifier_.reset();
    238 
    239   const CertVerifyResult& cert_verify_result =
    240       verify_details_->cert_verify_result;
    241   const CertStatus cert_status = cert_verify_result.cert_status;
    242   if (transport_security_state_ &&
    243       (result == OK ||
    244        (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) &&
    245       !transport_security_state_->CheckPublicKeyPins(
    246           hostname_,
    247           cert_verify_result.is_issued_by_known_root,
    248           cert_verify_result.public_key_hashes,
    249           &verify_details_->pinning_failure_log)) {
    250     result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
    251   }
    252 
    253   if (result != OK) {
    254     std::string error_string = ErrorToString(result);
    255     error_details_ = StringPrintf("Failed to verify certificate chain: %s",
    256                                   error_string.c_str());
    257     DLOG(WARNING) << error_details_;
    258   }
    259 
    260   // Exit DoLoop and return the result to the caller to VerifyProof.
    261   DCHECK_EQ(STATE_NONE, next_state_);
    262   return result;
    263 }
    264 
    265 bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data,
    266                                                  const string& signature,
    267                                                  const string& cert) {
    268   StringPiece spki;
    269   if (!asn1::ExtractSPKIFromDERCert(cert, &spki)) {
    270     DLOG(WARNING) << "ExtractSPKIFromDERCert failed";
    271     return false;
    272   }
    273 
    274   crypto::SignatureVerifier verifier;
    275 
    276   size_t size_bits;
    277   X509Certificate::PublicKeyType type;
    278   X509Certificate::GetPublicKeyInfo(cert_->os_cert_handle(), &size_bits,
    279                                     &type);
    280   if (type == X509Certificate::kPublicKeyTypeRSA) {
    281     crypto::SignatureVerifier::HashAlgorithm hash_alg =
    282         crypto::SignatureVerifier::SHA256;
    283     crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg;
    284     unsigned int hash_len = 32;  // 32 is the length of a SHA-256 hash.
    285 
    286     bool ok = verifier.VerifyInitRSAPSS(
    287         hash_alg, mask_hash_alg, hash_len,
    288         reinterpret_cast<const uint8*>(signature.data()), signature.size(),
    289         reinterpret_cast<const uint8*>(spki.data()), spki.size());
    290     if (!ok) {
    291       DLOG(WARNING) << "VerifyInitRSAPSS failed";
    292       return false;
    293     }
    294   } else if (type == X509Certificate::kPublicKeyTypeECDSA) {
    295     // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
    296     // RFC 5758:
    297     //   ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    298     //        us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
    299     //   ...
    300     //   When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
    301     //   ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
    302     //   as an AlgorithmIdentifier, the encoding MUST omit the parameters
    303     //   field.  That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
    304     //   component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
    305     //   SHA384, or ecdsa-with-SHA512.
    306     // See also RFC 5480, Appendix A.
    307     static const uint8 kECDSAWithSHA256AlgorithmID[] = {
    308       0x30, 0x0a,
    309         0x06, 0x08,
    310           0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
    311     };
    312 
    313     if (!verifier.VerifyInit(
    314             kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
    315             reinterpret_cast<const uint8*>(signature.data()),
    316             signature.size(),
    317             reinterpret_cast<const uint8*>(spki.data()),
    318             spki.size())) {
    319       DLOG(WARNING) << "VerifyInit failed";
    320       return false;
    321     }
    322   } else {
    323     LOG(ERROR) << "Unsupported public key type " << type;
    324     return false;
    325   }
    326 
    327   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(kProofSignatureLabel),
    328                         sizeof(kProofSignatureLabel));
    329   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
    330                         signed_data.size());
    331 
    332   if (!verifier.VerifyFinal()) {
    333     DLOG(WARNING) << "VerifyFinal failed";
    334     return false;
    335   }
    336 
    337   DVLOG(1) << "VerifyFinal success";
    338   return true;
    339 }
    340 
    341 ProofVerifierChromium::ProofVerifierChromium(
    342     CertVerifier* cert_verifier,
    343     TransportSecurityState* transport_security_state)
    344     : cert_verifier_(cert_verifier),
    345       transport_security_state_(transport_security_state) {
    346 }
    347 
    348 ProofVerifierChromium::~ProofVerifierChromium() {
    349   STLDeleteElements(&active_jobs_);
    350 }
    351 
    352 QuicAsyncStatus ProofVerifierChromium::VerifyProof(
    353     const std::string& hostname,
    354     const std::string& server_config,
    355     const std::vector<std::string>& certs,
    356     const std::string& signature,
    357     const ProofVerifyContext* verify_context,
    358     std::string* error_details,
    359     scoped_ptr<ProofVerifyDetails>* verify_details,
    360     ProofVerifierCallback* callback) {
    361   if (!verify_context) {
    362     *error_details = "Missing context";
    363     return QUIC_FAILURE;
    364   }
    365   const ProofVerifyContextChromium* chromium_context =
    366       reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
    367   scoped_ptr<Job> job(new Job(this,
    368                               cert_verifier_,
    369                               transport_security_state_,
    370                               chromium_context->net_log));
    371   QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs,
    372                                             signature, error_details,
    373                                             verify_details, callback);
    374   if (status == QUIC_PENDING) {
    375     active_jobs_.insert(job.release());
    376   }
    377   return status;
    378 }
    379 
    380 void ProofVerifierChromium::OnJobComplete(Job* job) {
    381   active_jobs_.erase(job);
    382   delete job;
    383 }
    384 
    385 }  // namespace net
    386