| Index: net/cert/expect_staple_report.cc
 | 
| diff --git a/net/cert/expect_staple_report.cc b/net/cert/expect_staple_report.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..8d6e52a5c1376bf8c5d5f21b0ebf0ba7723fe46d
 | 
| --- /dev/null
 | 
| +++ b/net/cert/expect_staple_report.cc
 | 
| @@ -0,0 +1,126 @@
 | 
| +// Copyright 2016 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "expect_staple_report.h"
 | 
| +
 | 
| +#include "base/time/time.h"
 | 
| +#include "net/cert/internal/parse_ocsp.h"
 | 
| +#include "net/cert/x509_certificate.h"
 | 
| +#include "net/der/parse_values.h"
 | 
| +
 | 
| +namespace net {
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +der::GeneralizedTime ConvertBaseTime(const base::Time& time) {
 | 
| +  base::Time::Exploded exploded;
 | 
| +  time.UTCExplode(&exploded);
 | 
| +
 | 
| +  der::GeneralizedTime result;
 | 
| +  result.year = exploded.year;
 | 
| +  result.month = exploded.month;
 | 
| +  result.day = exploded.day_of_month;
 | 
| +  result.hours = exploded.hour;
 | 
| +  result.minutes = exploded.minute;
 | 
| +  result.seconds = exploded.second;
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +// Checks that thisUpdate <= verify_time < nextUpdate, and that thisUpdate >=
 | 
| +// verify_time - max_age.
 | 
| +bool CheckOCSPDateValid(const OCSPSingleResponse& response,
 | 
| +                        const base::Time& verify_time,
 | 
| +                        const base::TimeDelta& max_age) {
 | 
| +  if (response.has_next_update &&
 | 
| +      (response.next_update <= response.this_update)) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  // Place |verify_time| in the bounds.
 | 
| +  der::GeneralizedTime verify_time_der = ConvertBaseTime(verify_time);
 | 
| +  if (response.this_update > verify_time_der)
 | 
| +    return false;
 | 
| +  if (response.has_next_update && (response.next_update <= verify_time_der))
 | 
| +    return false;
 | 
| +
 | 
| +  // Enforce |max_age|.
 | 
| +  der::GeneralizedTime lower_bound = ConvertBaseTime(verify_time - max_age);
 | 
| +  if (response.this_update < lower_bound)
 | 
| +    return false;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool CompareCertIDToCertificate(const OCSPCertID& cert_id,
 | 
| +                                const X509Certificate& certificate) {
 | 
| +  // TODO(dadrian): Verify name and key hashes. https://crbug.com/620005
 | 
| +  der::Input serial(&certificate.serial_number());
 | 
| +  return serial == cert_id.serial_number;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +ExpectStapleReport::ExpectStapleReport() : staple_error_(StapleError::OK) {}
 | 
| +
 | 
| +ExpectStapleReport::~ExpectStapleReport() {}
 | 
| +
 | 
| +// static
 | 
| +std::unique_ptr<ExpectStapleReport> ExpectStapleReport::FromRawOCSPResponse(
 | 
| +    const std::string& raw_response,
 | 
| +    const base::Time& verify_time,
 | 
| +    const base::TimeDelta& max_age,
 | 
| +    const X509Certificate& verified_certificate) {
 | 
| +  std::unique_ptr<ExpectStapleReport> out(new ExpectStapleReport);
 | 
| +  out->verify_time_ = verify_time;
 | 
| +  der::Input response_der(&raw_response);
 | 
| +
 | 
| +  OCSPResponse response;
 | 
| +  if (!ParseOCSPResponse(response_der, &response)) {
 | 
| +    out->staple_error_ = StapleError::PARSE_RESPONSE;
 | 
| +    return out;
 | 
| +  }
 | 
| +
 | 
| +  // If the OCSP response isn't status SUCCESSFUL, don't parse the rest of the
 | 
| +  // data.
 | 
| +  if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) {
 | 
| +    out->staple_error_ = StapleError::BAD_RESPONSE;
 | 
| +    return out;
 | 
| +  }
 | 
| +
 | 
| +  OCSPResponseData response_data;
 | 
| +  if (!ParseOCSPResponseData(response.data, &response_data)) {
 | 
| +    out->staple_error_ = StapleError::PARSE_RESPONSE_DATA;
 | 
| +    return out;
 | 
| +  }
 | 
| +
 | 
| +  // TODO(svaldez): Unify with GetOCSPCertStatus.
 | 
| +  bool contains_correct_response = false;
 | 
| +  for (const auto& single_response_der : response_data.responses) {
 | 
| +    OCSPSingleResponse single_response;
 | 
| +    if (!ParseOCSPSingleResponse(single_response_der, &single_response))
 | 
| +      continue;
 | 
| +    SingleResult single_result;
 | 
| +    single_result.status = single_response.cert_status.status;
 | 
| +    single_result.is_date_valid =
 | 
| +        CheckOCSPDateValid(single_response, verify_time, max_age);
 | 
| +    OCSPCertID cert_id;
 | 
| +    if (ParseOCSPCertID(single_response.cert_id_tlv, &cert_id)) {
 | 
| +      single_result.is_correct_certificate =
 | 
| +          CompareCertIDToCertificate(cert_id, verified_certificate);
 | 
| +    }
 | 
| +    if (single_result.is_date_valid && single_result.is_correct_certificate &&
 | 
| +        single_result.status == OCSPCertStatus::Status::GOOD) {
 | 
| +      contains_correct_response = true;
 | 
| +    }
 | 
| +    out->stapled_responses_.push_back(single_result);
 | 
| +  }
 | 
| +
 | 
| +  if (!contains_correct_response) {
 | 
| +    out->staple_error_ = StapleError::NO_MATCHING_RESPONSE;
 | 
| +  } else {
 | 
| +    out->staple_error_ = StapleError::OK;
 | 
| +  }
 | 
| +  return out;
 | 
| +}
 | 
| +
 | 
| +}  // namespace net
 | 
| 
 |