Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Side by Side Diff: net/cert/cert_verify_proc_builtin.cc

Issue 2755483008: Add initial CertVerifyProcBuiltin. (Closed)
Patch Set: type notimplemented properly Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017 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/cert_verify_proc_builtin.h"
6
7 #include <string>
8 #include <vector>
9
10 #if defined(USE_NSS_CERTS)
11 #include <cert.h>
12 #include <pk11pub.h>
13 #endif
14
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/sha1.h"
18 #include "base/strings/string_piece.h"
19 #include "crypto/sha2.h"
20 #include "net/base/net_errors.h"
21 #include "net/cert/asn1_util.h"
22 #include "net/cert/cert_status_flags.h"
23 #include "net/cert/cert_verify_proc.h"
24 #include "net/cert/cert_verify_result.h"
25 #include "net/cert/internal/cert_errors.h"
26 #include "net/cert/internal/cert_issuer_source_static.h"
27 #include "net/cert/internal/parsed_certificate.h"
28 #include "net/cert/internal/path_builder.h"
29 #include "net/cert/internal/signature_policy.h"
30 #include "net/cert/internal/trust_store_collection.h"
31 #include "net/cert/internal/trust_store_in_memory.h"
32 #include "net/cert/internal/verify_certificate_chain.h"
33 #include "net/cert/x509_certificate.h"
34 #include "net/cert/x509_util.h"
35 #include "net/der/encode_values.h"
36
37 #if defined(USE_NSS_CERTS)
38 #include "crypto/nss_util.h"
39 #include "net/cert/internal/cert_issuer_source_nss.h"
40 #include "net/cert/internal/trust_store_nss.h"
41 #include "net/cert/scoped_nss_types.h"
42 #endif
43
44 namespace net {
45
46 namespace {
47
48 class CertVerifyProcBuiltin : public CertVerifyProc {
49 public:
50 CertVerifyProcBuiltin();
51
52 bool SupportsAdditionalTrustAnchors() const override;
53 bool SupportsOCSPStapling() const override;
54
55 protected:
56 ~CertVerifyProcBuiltin() override;
57
58 private:
59 int VerifyInternal(X509Certificate* cert,
60 const std::string& hostname,
61 const std::string& ocsp_response,
62 int flags,
63 CRLSet* crl_set,
64 const CertificateList& additional_trust_anchors,
65 CertVerifyResult* verify_result) override;
66 };
67
68 CertVerifyProcBuiltin::CertVerifyProcBuiltin() {}
69
70 CertVerifyProcBuiltin::~CertVerifyProcBuiltin() {}
71
72 bool CertVerifyProcBuiltin::SupportsAdditionalTrustAnchors() const {
73 return true;
74 }
75
76 bool CertVerifyProcBuiltin::SupportsOCSPStapling() const {
77 // TODO(crbug.com/649017): Implement.
78 return false;
79 }
80
81 scoped_refptr<ParsedCertificate> ParseCertificateFromOSHandle(
82 X509Certificate::OSCertHandle cert_handle,
83 CertErrors* errors) {
84 std::string cert_bytes;
85 if (!X509Certificate::GetDEREncoded(cert_handle, &cert_bytes))
86 return nullptr;
87 return ParsedCertificate::Create(x509_util::CreateCryptoBuffer(cert_bytes),
88 {}, errors);
89 }
90
91 void AddIntermediatesToIssuerSource(X509Certificate* x509_cert,
92 CertIssuerSourceStatic* intermediates) {
93 const X509Certificate::OSCertHandles& cert_handles =
94 x509_cert->GetIntermediateCertificates();
95 CertErrors errors;
96 for (auto it = cert_handles.begin(); it != cert_handles.end(); ++it) {
97 scoped_refptr<ParsedCertificate> cert =
98 ParseCertificateFromOSHandle(*it, &errors);
99 if (cert)
100 intermediates->AddCert(std::move(cert));
101 // TODO(crbug.com/634443): Surface these parsing errors?
102 }
103 }
104
105 // The SystemTrustStore interface augments the TrustStore interface with some
106 // additional functionality:
107 //
108 // * Determine if a trust anchor was one of the known roots
109 // * Determine if a trust anchor was one of the "extra" ones that
110 // was specified during verification.
111 //
112 // Implementations of SystemTrustStore create an effective trust
113 // store that is the composition of:
114 //
115 // (1) System trust store
116 // (2) |additional_trust_anchors|.
117 // (3) Test certificates (if they are separate from system trust store)
118 class SystemTrustStore {
119 public:
120 virtual ~SystemTrustStore() {}
121
122 virtual TrustStore* GetTrustStore() = 0;
123
124 // TODO(eroman): Can this be exposed through the TrustStore
125 // interface instead?
126 virtual CertIssuerSource* GetCertIssuerSource() = 0;
127
128 // IsKnownRoot returns true if the given trust anchor is a standard one (as
129 // opposed to a user-installed root)
130 virtual bool IsKnownRoot(
131 const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
132
133 virtual bool IsAdditionalTrustAnchor(
134 const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
135 };
136
137 #if defined(USE_NSS_CERTS)
138 class SystemTrustStoreNSS : public SystemTrustStore {
139 public:
140 explicit SystemTrustStoreNSS(const CertificateList& additional_trust_anchors)
141 : trust_store_nss_(trustSSL) {
142 CertErrors errors;
143
144 trust_store_.AddTrustStore(&additional_trust_store_);
145 for (const auto& x509_cert : additional_trust_anchors) {
146 scoped_refptr<ParsedCertificate> cert =
147 ParseCertificateFromOSHandle(x509_cert->os_cert_handle(), &errors);
148 if (cert) {
149 additional_trust_store_.AddTrustAnchor(
150 TrustAnchor::CreateFromCertificateNoConstraints(std::move(cert)));
151 }
152 // TODO(eroman): Surface parsing errors of additional trust anchor.
153 }
154
155 trust_store_.AddTrustStore(&trust_store_nss_);
156 }
157
158 TrustStore* GetTrustStore() override { return &trust_store_; }
159
160 CertIssuerSource* GetCertIssuerSource() override {
161 return &cert_issuer_source_nss_;
162 }
163
164 // IsKnownRoot returns true if the given trust anchor is a standard one (as
165 // opposed to a user-installed root)
166 bool IsKnownRoot(
167 const scoped_refptr<TrustAnchor>& trust_anchor) const override {
168 // TODO(eroman): Based on how the TrustAnchors are created by this
169 // integration, there will always be an associated certificate. However this
170 // contradicts the API for TrustAnchor that states it is optional.
171 DCHECK(trust_anchor->cert());
172
173 // TODO(eroman): The overall approach of IsKnownRoot() is inefficient -- it
174 // requires searching for the trust anchor by DER in NSS, however path
175 // building already had a handle to it.
176 SECItem der_cert;
177 der_cert.data =
178 const_cast<uint8_t*>(trust_anchor->cert()->der_cert().UnsafeData());
179 der_cert.len = trust_anchor->cert()->der_cert().Length();
180 der_cert.type = siDERCertBuffer;
181 ScopedCERTCertificate nss_cert(
182 CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), &der_cert));
183 if (!nss_cert)
184 return false;
185
186 return IsKnownRoot(nss_cert.get());
187 }
188
189 bool IsAdditionalTrustAnchor(
190 const scoped_refptr<TrustAnchor>& trust_anchor) const override {
191 return additional_trust_store_.Contains(trust_anchor.get());
192 }
193
194 private:
195 // TODO(eroman): This function was copied verbatim from
196 // cert_verify_proc_nss.cc
197 //
198 // IsKnownRoot returns true if the given certificate is one that we believe
199 // is a standard (as opposed to user-installed) root.
200 bool IsKnownRoot(CERTCertificate* root) const {
201 if (!root || !root->slot)
202 return false;
203
204 // This magic name is taken from
205 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw /builtins/constants.c&rev=1.13&mark=86,89#79
206 return 0 == strcmp(PK11_GetSlotName(root->slot), "NSS Builtin Objects");
207 }
208
209 TrustStoreCollection trust_store_;
210 TrustStoreInMemory additional_trust_store_;
211
212 TrustStoreNSS trust_store_nss_;
213 CertIssuerSourceNSS cert_issuer_source_nss_;
214 };
215 #endif
216
217 std::unique_ptr<SystemTrustStore> CreateSystemTrustStore(
218 const CertificateList& additional_trust_anchors) {
219 #if defined(USE_NSS_CERTS)
220 return base::MakeUnique<SystemTrustStoreNSS>(additional_trust_anchors);
221 #else
222 // TODO(crbug.com/649017): Integrate with other system trust stores.
223 NOTIMPLEMENTED();
224 return nullptr;
225 #endif
226 }
227
228 // Appends the SHA1 and SHA256 hashes of |spki_bytes| to |*hashes|.
229 void AppendPublicKeyHashes(const der::Input& spki_bytes,
230 HashValueVector* hashes) {
231 HashValue sha1(HASH_VALUE_SHA1);
232 base::SHA1HashBytes(spki_bytes.UnsafeData(), spki_bytes.Length(),
233 sha1.data());
234 hashes->push_back(sha1);
235
236 HashValue sha256(HASH_VALUE_SHA256);
237 crypto::SHA256HashString(spki_bytes.AsStringPiece(), sha256.data(),
238 crypto::kSHA256Length);
239 hashes->push_back(sha256);
240 }
241
242 // Appends the SubjectPublicKeyInfo hashes for all certificates (and trust
243 // anchor) in |partial_path| to |*hashes|.
244 void AppendPublicKeyHashes(const CertPathBuilder::ResultPath& partial_path,
245 HashValueVector* hashes) {
246 for (const scoped_refptr<ParsedCertificate>& cert : partial_path.path.certs)
247 AppendPublicKeyHashes(cert->tbs().spki_tlv, hashes);
248
249 if (partial_path.path.trust_anchor)
250 AppendPublicKeyHashes(partial_path.path.trust_anchor->spki(), hashes);
251 }
252
253 // Sets the bits on |cert_status| for all the errors encountered during the path
254 // building of |partial_path|.
255 void MapPathBuilderErrorsToCertStatus(
256 const CertPathBuilder::ResultPath& partial_path,
257 const std::string& hostname,
258 CertStatus* cert_status) {
259 // If path building was successful, there are no errors to map (there may have
260 // been warnings but they do not map to CertStatus).
261 if (partial_path.valid)
262 return;
263
264 LOG(ERROR) << "CertVerifyProcBuiltin for " << hostname << " failed:\n"
265 << partial_path.errors.ToDebugString();
266
267 if (partial_path.errors.ContainsError(kRsaModulusTooSmall))
268 *cert_status |= CERT_STATUS_WEAK_KEY;
269
270 if (partial_path.errors.ContainsError(kValidityFailedNotAfter) ||
271 partial_path.errors.ContainsError(kValidityFailedNotBefore)) {
272 *cert_status |= CERT_STATUS_DATE_INVALID;
273 }
274
275 // IMPORTANT: If the path was invalid for a reason that was not
276 // explicity checked above, set a general error. This is important as
277 // |cert_status| is what ultimately indicates whether verification was
278 // successful or not (absense of errors implies success).
279 if (!IsCertStatusError(*cert_status))
280 *cert_status |= CERT_STATUS_INVALID;
281 }
282
283 X509Certificate::OSCertHandle CreateOSCertHandle(
284 const scoped_refptr<ParsedCertificate>& certificate) {
285 return X509Certificate::CreateOSCertHandleFromBytes(
286 reinterpret_cast<const char*>(certificate->der_cert().UnsafeData()),
287 certificate->der_cert().Length());
288 }
289
290 // Creates a X509Certificate (chain) to return as the verified result.
291 //
292 // * |target_cert|: The original X509Certificate that was passed in to
293 // VerifyInternal()
294 // * |path|: The result (possibly failed) from path building.
295 scoped_refptr<X509Certificate> CreateVerifiedCertChain(
296 X509Certificate* target_cert,
297 const CertPathBuilder::ResultPath& path) {
298 X509Certificate::OSCertHandles intermediates;
299
300 // Skip the first certificate in the path as that is the target certificate
301 for (size_t i = 1; i < path.path.certs.size(); ++i)
302 intermediates.push_back(CreateOSCertHandle(path.path.certs[i]));
303
304 if (path.path.trust_anchor) {
305 // TODO(eroman): This assumes that TrustAnchor::cert() cannot be null,
306 // which disagrees with the documentation.
307 intermediates.push_back(CreateOSCertHandle(path.path.trust_anchor->cert()));
308 }
309
310 scoped_refptr<X509Certificate> result = X509Certificate::CreateFromHandle(
311 target_cert->os_cert_handle(), intermediates);
312
313 for (const X509Certificate::OSCertHandle handle : intermediates)
314 X509Certificate::FreeOSCertHandle(handle);
315
316 return result;
317 }
318
319 // TODO(crbug.com/649017): Make use of |flags|, |crl_set|, and |ocsp_response|.
320 // Also handle key usages, policies and EV.
321 //
322 // Any failure short-circuits from the function must set
323 // |verify_result->cert_status|.
324 void DoVerify(X509Certificate* input_cert,
325 const std::string& hostname,
326 const std::string& ocsp_response,
327 int flags,
328 CRLSet* crl_set,
329 const CertificateList& additional_trust_anchors,
330 CertVerifyResult* verify_result) {
331 CertErrors errors;
332
333 // Parse the target certificate.
334 scoped_refptr<ParsedCertificate> target =
335 ParseCertificateFromOSHandle(input_cert->os_cert_handle(), &errors);
336 if (!target) {
337 // TODO(crbug.com/634443): Surface these parsing errors?
338 verify_result->cert_status |= CERT_STATUS_INVALID;
339 return;
340 }
341
342 std::unique_ptr<SystemTrustStore> trust_store =
343 CreateSystemTrustStore(additional_trust_anchors);
344
345 // TODO(eroman): The path building code in this file enforces its idea of weak
346 // keys, and separately cert_verify_proc.cc also checks the chains with its
347 // own policy. These policies should be aligned, to give path building the
348 // best chance of finding a good path.
349 // Another difference to resolve is the path building here does not check the
350 // target certificate's key strength, whereas cert_verify_proc.cc does.
351 SimpleSignaturePolicy signature_policy(1024);
352
353 // Use the current time.
354 der::GeneralizedTime verification_time;
355 if (!der::EncodeTimeAsGeneralizedTime(base::Time::Now(),
356 &verification_time)) {
357 // This really shouldn't be possible unless Time::Now() returned
358 // something crazy.
359 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
360 return;
361 }
362
363 // Initialize the path builder.
364 CertPathBuilder::Result result;
365 CertPathBuilder path_builder(target, trust_store->GetTrustStore(),
366 &signature_policy, verification_time, &result);
367
368 // Allow the path builder to discover intermediates from the trust store.
369 if (trust_store->GetCertIssuerSource())
370 path_builder.AddCertIssuerSource(trust_store->GetCertIssuerSource());
371
372 // Allow the path builder to discover the explicitly provided intermediates in
373 // |input_cert|.
374 CertIssuerSourceStatic intermediates;
375 AddIntermediatesToIssuerSource(input_cert, &intermediates);
376 path_builder.AddCertIssuerSource(&intermediates);
377
378 // TODO(crbug.com/649017): Allow the path builder to discover intermediates
379 // through AIA fetching.
380
381 path_builder.Run();
382
383 if (result.best_result_index >= result.paths.size()) {
384 // TODO(crbug.com/634443): What errors to communicate? Maybe the path
385 // builder should always return some partial path (even if just containing
386 // the target), then there is a CertErrors to test.
387 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
388 return;
389 }
390
391 // Use the best path that was built. This could be a partial path, or it could
392 // be a valid complete path.
393 const CertPathBuilder::ResultPath& partial_path =
394 *result.paths[result.best_result_index].get();
395
396 if (partial_path.path.trust_anchor) {
397 verify_result->is_issued_by_known_root =
398 trust_store->IsKnownRoot(partial_path.path.trust_anchor);
399
400 verify_result->is_issued_by_additional_trust_anchor =
401 trust_store->IsAdditionalTrustAnchor(partial_path.path.trust_anchor);
402 } else {
403 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
404 }
405
406 verify_result->verified_cert =
407 CreateVerifiedCertChain(input_cert, partial_path);
408
409 AppendPublicKeyHashes(partial_path, &verify_result->public_key_hashes);
410 MapPathBuilderErrorsToCertStatus(partial_path, hostname,
411 &verify_result->cert_status);
412 }
413
414 int CertVerifyProcBuiltin::VerifyInternal(
415 X509Certificate* input_cert,
416 const std::string& hostname,
417 const std::string& ocsp_response,
418 int flags,
419 CRLSet* crl_set,
420 const CertificateList& additional_trust_anchors,
421 CertVerifyResult* verify_result) {
422 DoVerify(input_cert, hostname, ocsp_response, flags, crl_set,
423 additional_trust_anchors, verify_result);
424
425 return IsCertStatusError(verify_result->cert_status)
426 ? MapCertStatusToNetError(verify_result->cert_status)
427 : OK;
428 }
429
430 } // namespace
431
432 scoped_refptr<CertVerifyProc> CreateCertVerifyProcBuiltin() {
433 return scoped_refptr<CertVerifyProc>(new CertVerifyProcBuiltin());
434 }
435
436 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698