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

Side by Side Diff: extensions/common/cast/cast_cert_validator.cc

Issue 1924323002: Move Cast certificate verification code from extensions to components. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Simplified OWNERS files Created 4 years, 7 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
OLDNEW
(Empty)
1 // Copyright 2014 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 "extensions/common/cast/cast_cert_validator.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <memory>
12 #include <utility>
13
14 #include "base/memory/ptr_util.h"
15 #include "base/memory/singleton.h"
16 #include "net/cert/internal/certificate_policies.h"
17 #include "net/cert/internal/extended_key_usage.h"
18 #include "net/cert/internal/parse_certificate.h"
19 #include "net/cert/internal/parse_name.h"
20 #include "net/cert/internal/signature_algorithm.h"
21 #include "net/cert/internal/signature_policy.h"
22 #include "net/cert/internal/verify_certificate_chain.h"
23 #include "net/cert/internal/verify_signed_data.h"
24 #include "net/der/input.h"
25
26 namespace extensions {
27 namespace api {
28 namespace cast_crypto {
29 namespace {
30
31 // -------------------------------------------------------------------------
32 // Cast trust anchors.
33 // -------------------------------------------------------------------------
34
35 // There are two trusted roots for Cast certificate chains:
36 //
37 // (1) CN=Cast Root CA (kCastRootCaDer)
38 // (2) CN=Eureka Root CA (kEurekaRootCaDer)
39 //
40 // These constants are defined by the files included next:
41
42 #include "extensions/common/cast/cast_root_ca_cert_der-inc.h"
43 #include "extensions/common/cast/eureka_root_ca_der-inc.h"
44
45 // Singleton for the Cast trust store.
46 class CastTrustStore {
47 public:
48 static CastTrustStore* GetInstance() {
49 return base::Singleton<CastTrustStore,
50 base::LeakySingletonTraits<CastTrustStore>>::get();
51 }
52
53 static net::TrustStore& Get() { return GetInstance()->store_; }
54
55 private:
56 friend struct base::DefaultSingletonTraits<CastTrustStore>;
57
58 CastTrustStore() {
59 // Initialize the trust store with two root certificates.
60 CHECK(store_.AddTrustedCertificateWithoutCopying(kCastRootCaDer,
61 sizeof(kCastRootCaDer)));
62 CHECK(store_.AddTrustedCertificateWithoutCopying(kEurekaRootCaDer,
63 sizeof(kEurekaRootCaDer)));
64 }
65
66 net::TrustStore store_;
67 DISALLOW_COPY_AND_ASSIGN(CastTrustStore);
68 };
69
70 using ExtensionsMap = std::map<net::der::Input, net::ParsedExtension>;
71
72 // Helper that looks up an extension by OID given a map of extensions.
73 bool GetExtensionValue(const ExtensionsMap& extensions,
74 const net::der::Input& oid,
75 net::der::Input* value) {
76 auto it = extensions.find(oid);
77 if (it == extensions.end())
78 return false;
79 *value = it->second.value;
80 return true;
81 }
82
83 // Returns the OID for the Audio-Only Cast policy
84 // (1.3.6.1.4.1.11129.2.5.2) in DER form.
85 net::der::Input AudioOnlyPolicyOid() {
86 static const uint8_t kAudioOnlyPolicy[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
87 0xD6, 0x79, 0x02, 0x05, 0x02};
88 return net::der::Input(kAudioOnlyPolicy);
89 }
90
91 // Cast certificates rely on RSASSA-PKCS#1 v1.5 with SHA-1 for signatures.
92 //
93 // The following signature policy specifies which signature algorithms (and key
94 // sizes) are acceptable. It is used when verifying a chain of certificates, as
95 // well as when verifying digital signature using the target certificate's
96 // SPKI.
97 //
98 // This particular policy allows for:
99 // * ECDSA, RSA-SSA, and RSA-PSS
100 // * Supported EC curves: P-256, P-384, P-521.
101 // * Hashes: All SHA hashes including SHA-1 (despite being known weak).
102 // * RSA keys must have a modulus at least 2048-bits long.
103 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
104 return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
105 }
106
107 class CertVerificationContextImpl : public CertVerificationContext {
108 public:
109 // Save a copy of the passed in public key (DER) and common name (text).
110 CertVerificationContextImpl(const net::der::Input& spki,
111 const base::StringPiece& common_name)
112 : spki_(spki.AsString()), common_name_(common_name.as_string()) {}
113
114 bool VerifySignatureOverData(const base::StringPiece& signature,
115 const base::StringPiece& data) const override {
116 // This code assumes the signature algorithm was RSASSA PKCS#1 v1.5 with
117 // SHA-1.
118 // TODO(eroman): Is it possible to use other hash algorithms?
119 auto signature_algorithm =
120 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha1);
121
122 // Use the same policy as was used for verifying signatures in
123 // certificates. This will ensure for instance that the key used is at
124 // least 2048-bits long.
125 auto signature_policy = CreateCastSignaturePolicy();
126
127 return net::VerifySignedData(
128 *signature_algorithm, net::der::Input(data),
129 net::der::BitString(net::der::Input(signature), 0),
130 net::der::Input(&spki_), signature_policy.get());
131 }
132
133 std::string GetCommonName() const override { return common_name_; }
134
135 private:
136 std::string spki_;
137 std::string common_name_;
138 };
139
140 // Helper that extracts the Common Name from a certificate's subject field. On
141 // success |common_name| contains the text for the attribute (unescaped, so
142 // will depend on the encoding used, but for Cast device certs it should
143 // be ASCII).
144 bool GetCommonNameFromSubject(const net::der::Input& subject_tlv,
145 std::string* common_name) {
146 net::RDNSequence rdn_sequence;
147 if (!net::ParseName(subject_tlv, &rdn_sequence))
148 return false;
149
150 for (const net::RelativeDistinguishedName& rdn : rdn_sequence) {
151 for (const auto& atv : rdn) {
152 if (atv.type == net::TypeCommonNameOid()) {
153 return atv.ValueAsStringUnsafe(common_name);
154 }
155 }
156 }
157 return false;
158 }
159
160 // Returns true if the extended key usage list |ekus| contains client auth.
161 bool HasClientAuth(const std::vector<net::der::Input>& ekus) {
162 for (const auto& oid : ekus) {
163 if (oid == net::ClientAuth())
164 return true;
165 }
166 return false;
167 }
168
169 // Checks properties on the target certificate.
170 //
171 // * The Key Usage must include Digital Signature
172 // * THe Extended Key Usage must includ TLS Client Auth
173 // * May have the policy 1.3.6.1.4.1.11129.2.5.2 to indicate it
174 // is an audio-only device.
175 WARN_UNUSED_RESULT bool CheckTargetCertificate(
176 const net::der::Input& cert_der,
177 std::unique_ptr<CertVerificationContext>* context,
178 CastDeviceCertPolicy* policy) {
179 // TODO(eroman): Simplify this. The certificate chain verification
180 // function already parses this stuff, awkward to re-do it here.
181
182 net::ParsedCertificate cert;
183 if (!net::ParseCertificate(cert_der, &cert))
184 return false;
185
186 net::ParsedTbsCertificate tbs;
187 if (!net::ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs))
188 return false;
189
190 // Get the extensions.
191 if (!tbs.has_extensions)
192 return false;
193 ExtensionsMap extensions;
194 if (!net::ParseExtensions(tbs.extensions_tlv, &extensions))
195 return false;
196
197 net::der::Input extension_value;
198
199 // Get the Key Usage extension.
200 if (!GetExtensionValue(extensions, net::KeyUsageOid(), &extension_value))
201 return false;
202 net::der::BitString key_usage;
203 if (!net::ParseKeyUsage(extension_value, &key_usage))
204 return false;
205
206 // Ensure Key Usage contains digitalSignature.
207 if (!key_usage.AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
208 return false;
209
210 // Get the Extended Key Usage extension.
211 if (!GetExtensionValue(extensions, net::ExtKeyUsageOid(), &extension_value))
212 return false;
213 std::vector<net::der::Input> ekus;
214 if (!net::ParseEKUExtension(extension_value, &ekus))
215 return false;
216
217 // Ensure Extended Key Usage contains client auth.
218 if (!HasClientAuth(ekus))
219 return false;
220
221 // Check for an optional audio-only policy extension.
222 *policy = CastDeviceCertPolicy::NONE;
223 if (GetExtensionValue(extensions, net::CertificatePoliciesOid(),
224 &extension_value)) {
225 std::vector<net::der::Input> policies;
226 if (!net::ParseCertificatePoliciesExtension(extension_value, &policies))
227 return false;
228
229 // Look for an audio-only policy. Disregard any other policy found.
230 if (std::find(policies.begin(), policies.end(), AudioOnlyPolicyOid()) !=
231 policies.end()) {
232 *policy = CastDeviceCertPolicy::AUDIO_ONLY;
233 }
234 }
235
236 // Get the Common Name for the certificate.
237 std::string common_name;
238 if (!GetCommonNameFromSubject(tbs.subject_tlv, &common_name))
239 return false;
240
241 context->reset(new CertVerificationContextImpl(tbs.spki_tlv, common_name));
242 return true;
243 }
244
245
246 // Converts a base::Time::Exploded to a net::der::GeneralizedTime.
247 net::der::GeneralizedTime ConvertExplodedTime(
248 const base::Time::Exploded& exploded) {
249 net::der::GeneralizedTime result;
250 result.year = exploded.year;
251 result.month = exploded.month;
252 result.day = exploded.day_of_month;
253 result.hours = exploded.hour;
254 result.minutes = exploded.minute;
255 result.seconds = exploded.second;
256 return result;
257 }
258
259 } // namespace
260
261 bool VerifyDeviceCert(const std::vector<std::string>& certs,
262 const base::Time::Exploded& time,
263 std::unique_ptr<CertVerificationContext>* context,
264 CastDeviceCertPolicy* policy) {
265 // The underlying verification function expects a sequence of
266 // der::Input, so wrap the data in it (cheap).
267 std::vector<net::der::Input> input_chain;
268 for (const auto& cert : certs)
269 input_chain.push_back(net::der::Input(&cert));
270
271 // Use a signature policy compatible with Cast's PKI.
272 auto signature_policy = CreateCastSignaturePolicy();
273
274 // Do RFC 5280 compatible certificate verification using the two Cast
275 // trust anchors and Cast signature policy.
276 if (!net::VerifyCertificateChain(input_chain, CastTrustStore::Get(),
277 signature_policy.get(),
278 ConvertExplodedTime(time))) {
279 return false;
280 }
281
282 // Check properties of the leaf certificate (key usage, policy), and construct
283 // a CertVerificationContext that uses its public key.
284 return CheckTargetCertificate(input_chain[0], context, policy);
285 }
286
287 std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
288 const base::StringPiece& spki) {
289 // Use a bogus CommonName, since this is just exposed for testing signature
290 // verification by unittests.
291 return base::WrapUnique(
292 new CertVerificationContextImpl(net::der::Input(spki), "CommonName"));
293 }
294
295 bool AddTrustAnchorForTest(const uint8_t* data, size_t length) {
296 return CastTrustStore::Get().AddTrustedCertificateWithoutCopying(data,
297 length);
298 }
299
300 } // namespace cast_crypto
301 } // namespace api
302 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/common/cast/cast_cert_validator.h ('k') | extensions/common/cast/cast_cert_validator_test_helpers.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698