1 // Copyright 2020 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 "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h"
6 
7 #include <cstdint>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 
12 #include "absl/strings/escaping.h"
13 #include "absl/strings/match.h"
14 #include "absl/strings/str_cat.h"
15 #include "absl/strings/str_replace.h"
16 #include "absl/strings/string_view.h"
17 #include "openssl/sha.h"
18 #include "quiche/quic/core/crypto/certificate_view.h"
19 #include "quiche/quic/core/quic_time.h"
20 #include "quiche/quic/core/quic_types.h"
21 #include "quiche/quic/core/quic_utils.h"
22 #include "quiche/quic/platform/api/quic_bug_tracker.h"
23 #include "quiche/quic/platform/api/quic_logging.h"
24 #include "quiche/common/quiche_text_utils.h"
25 
26 namespace quic {
27 namespace {
28 
29 constexpr size_t kFingerprintLength = SHA256_DIGEST_LENGTH * 3 - 1;
30 
31 // Assumes that the character is normalized to lowercase beforehand.
IsNormalizedHexDigit(char c)32 bool IsNormalizedHexDigit(char c) {
33   return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
34 }
35 
NormalizeFingerprint(CertificateFingerprint & fingerprint)36 void NormalizeFingerprint(CertificateFingerprint& fingerprint) {
37   fingerprint.fingerprint =
38       quiche::QuicheTextUtils::ToLower(fingerprint.fingerprint);
39 }
40 
41 }  // namespace
42 
43 constexpr char CertificateFingerprint::kSha256[];
44 constexpr char WebTransportHash::kSha256[];
45 
Clone() const46 ProofVerifyDetails* WebTransportFingerprintProofVerifier::Details::Clone()
47     const {
48   return new Details(*this);
49 }
50 
WebTransportFingerprintProofVerifier(const QuicClock * clock,int max_validity_days)51 WebTransportFingerprintProofVerifier::WebTransportFingerprintProofVerifier(
52     const QuicClock* clock, int max_validity_days)
53     : clock_(clock),
54       max_validity_days_(max_validity_days),
55       // Add an extra second to max validity to accomodate various edge cases.
56       max_validity_(
57           QuicTime::Delta::FromSeconds(max_validity_days * 86400 + 1)) {}
58 
AddFingerprint(CertificateFingerprint fingerprint)59 bool WebTransportFingerprintProofVerifier::AddFingerprint(
60     CertificateFingerprint fingerprint) {
61   NormalizeFingerprint(fingerprint);
62   if (!absl::EqualsIgnoreCase(fingerprint.algorithm,
63                               CertificateFingerprint::kSha256)) {
64     QUIC_DLOG(WARNING) << "Algorithms other than SHA-256 are not supported";
65     return false;
66   }
67   if (fingerprint.fingerprint.size() != kFingerprintLength) {
68     QUIC_DLOG(WARNING) << "Invalid fingerprint length";
69     return false;
70   }
71   for (size_t i = 0; i < fingerprint.fingerprint.size(); i++) {
72     char current = fingerprint.fingerprint[i];
73     if (i % 3 == 2) {
74       if (current != ':') {
75         QUIC_DLOG(WARNING)
76             << "Missing colon separator between the bytes of the hash";
77         return false;
78       }
79     } else {
80       if (!IsNormalizedHexDigit(current)) {
81         QUIC_DLOG(WARNING) << "Fingerprint must be in hexadecimal";
82         return false;
83       }
84     }
85   }
86 
87   std::string normalized =
88       absl::StrReplaceAll(fingerprint.fingerprint, {{":", ""}});
89   std::string normalized_bytes;
90   if (!absl::HexStringToBytes(normalized, &normalized_bytes)) {
91     QUIC_DLOG(WARNING) << "Fingerprint hexadecimal is invalid";
92     return false;
93   }
94   hashes_.push_back(
95       WebTransportHash{fingerprint.algorithm, std::move(normalized_bytes)});
96   return true;
97 }
98 
AddFingerprint(WebTransportHash hash)99 bool WebTransportFingerprintProofVerifier::AddFingerprint(
100     WebTransportHash hash) {
101   if (hash.algorithm != CertificateFingerprint::kSha256) {
102     QUIC_DLOG(WARNING) << "Algorithms other than SHA-256 are not supported";
103     return false;
104   }
105   if (hash.value.size() != SHA256_DIGEST_LENGTH) {
106     QUIC_DLOG(WARNING) << "Invalid fingerprint length";
107     return false;
108   }
109   hashes_.push_back(std::move(hash));
110   return true;
111 }
112 
VerifyProof(const std::string &,const uint16_t,const std::string &,QuicTransportVersion,absl::string_view,const std::vector<std::string> &,const std::string &,const std::string &,const ProofVerifyContext *,std::string * error_details,std::unique_ptr<ProofVerifyDetails> * details,std::unique_ptr<ProofVerifierCallback>)113 QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyProof(
114     const std::string& /*hostname*/, const uint16_t /*port*/,
115     const std::string& /*server_config*/,
116     QuicTransportVersion /*transport_version*/, absl::string_view /*chlo_hash*/,
117     const std::vector<std::string>& /*certs*/, const std::string& /*cert_sct*/,
118     const std::string& /*signature*/, const ProofVerifyContext* /*context*/,
119     std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
120     std::unique_ptr<ProofVerifierCallback> /*callback*/) {
121   *error_details =
122       "QUIC crypto certificate verification is not supported in "
123       "WebTransportFingerprintProofVerifier";
124   QUIC_BUG(quic_bug_10879_1) << *error_details;
125   *details = std::make_unique<Details>(Status::kInternalError);
126   return QUIC_FAILURE;
127 }
128 
VerifyCertChain(const std::string &,const uint16_t,const std::vector<std::string> & certs,const std::string &,const std::string &,const ProofVerifyContext *,std::string * error_details,std::unique_ptr<ProofVerifyDetails> * details,uint8_t *,std::unique_ptr<ProofVerifierCallback>)129 QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyCertChain(
130     const std::string& /*hostname*/, const uint16_t /*port*/,
131     const std::vector<std::string>& certs, const std::string& /*ocsp_response*/,
132     const std::string& /*cert_sct*/, const ProofVerifyContext* /*context*/,
133     std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
134     uint8_t* /*out_alert*/,
135     std::unique_ptr<ProofVerifierCallback> /*callback*/) {
136   if (certs.empty()) {
137     *details = std::make_unique<Details>(Status::kInternalError);
138     *error_details = "No certificates provided";
139     return QUIC_FAILURE;
140   }
141 
142   if (!HasKnownFingerprint(certs[0])) {
143     *details = std::make_unique<Details>(Status::kUnknownFingerprint);
144     *error_details = "Certificate does not match any fingerprint";
145     return QUIC_FAILURE;
146   }
147 
148   std::unique_ptr<CertificateView> view =
149       CertificateView::ParseSingleCertificate(certs[0]);
150   if (view == nullptr) {
151     *details = std::make_unique<Details>(Status::kCertificateParseFailure);
152     *error_details = "Failed to parse the certificate";
153     return QUIC_FAILURE;
154   }
155 
156   if (!HasValidExpiry(*view)) {
157     *details = std::make_unique<Details>(Status::kExpiryTooLong);
158     *error_details =
159         absl::StrCat("Certificate expiry exceeds the configured limit of ",
160                      max_validity_days_, " days");
161     return QUIC_FAILURE;
162   }
163 
164   if (!IsWithinValidityPeriod(*view)) {
165     *details = std::make_unique<Details>(Status::kExpired);
166     *error_details =
167         "Certificate has expired or has validity listed in the future";
168     return QUIC_FAILURE;
169   }
170 
171   if (!IsKeyTypeAllowedByPolicy(*view)) {
172     *details = std::make_unique<Details>(Status::kDisallowedKeyAlgorithm);
173     *error_details =
174         absl::StrCat("Certificate uses a disallowed public key type (",
175                      PublicKeyTypeToString(view->public_key_type()), ")");
176     return QUIC_FAILURE;
177   }
178 
179   *details = std::make_unique<Details>(Status::kValidCertificate);
180   return QUIC_SUCCESS;
181 }
182 
183 std::unique_ptr<ProofVerifyContext>
CreateDefaultContext()184 WebTransportFingerprintProofVerifier::CreateDefaultContext() {
185   return nullptr;
186 }
187 
HasKnownFingerprint(absl::string_view der_certificate)188 bool WebTransportFingerprintProofVerifier::HasKnownFingerprint(
189     absl::string_view der_certificate) {
190   // https://w3c.github.io/webtransport/#verify-a-certificate-hash
191   const std::string hash = RawSha256(der_certificate);
192   for (const WebTransportHash& reference : hashes_) {
193     if (reference.algorithm != WebTransportHash::kSha256) {
194       QUIC_BUG(quic_bug_10879_2) << "Unexpected non-SHA-256 hash";
195       continue;
196     }
197     if (hash == reference.value) {
198       return true;
199     }
200   }
201   return false;
202 }
203 
HasValidExpiry(const CertificateView & certificate)204 bool WebTransportFingerprintProofVerifier::HasValidExpiry(
205     const CertificateView& certificate) {
206   if (!certificate.validity_start().IsBefore(certificate.validity_end())) {
207     return false;
208   }
209 
210   const QuicTime::Delta duration_seconds =
211       certificate.validity_end() - certificate.validity_start();
212   return duration_seconds <= max_validity_;
213 }
214 
IsWithinValidityPeriod(const CertificateView & certificate)215 bool WebTransportFingerprintProofVerifier::IsWithinValidityPeriod(
216     const CertificateView& certificate) {
217   QuicWallTime now = clock_->WallNow();
218   return now.IsAfter(certificate.validity_start()) &&
219          now.IsBefore(certificate.validity_end());
220 }
221 
IsKeyTypeAllowedByPolicy(const CertificateView & certificate)222 bool WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
223     const CertificateView& certificate) {
224   switch (certificate.public_key_type()) {
225     // https://github.com/w3c/webtransport/pull/375 defines P-256 as an MTI
226     // algorithm, and prohibits RSA.  We also allow P-384 and Ed25519.
227     case PublicKeyType::kP256:
228     case PublicKeyType::kP384:
229     case PublicKeyType::kEd25519:
230       return true;
231     case PublicKeyType::kRsa:
232       // TODO(b/213614428): this should be false by default.
233       return true;
234     default:
235       return false;
236   }
237 }
238 
239 }  // namespace quic
240