OLD | NEW |
---|---|
(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 <cert.h> | |
8 #include <cryptohi.h> | |
9 #include <pk11pub.h> | |
10 #include <seccomon.h> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "crypto/nss_util.h" | |
15 #include "crypto/scoped_nss_types.h" | |
16 #include "extensions/browser/api/cast_channel/cast_auth_ica.h" | |
17 | |
18 namespace extensions { | |
19 namespace core_api { | |
20 namespace cast_crypto { | |
21 | |
22 namespace { | |
23 | |
24 typedef scoped_ptr< | |
25 CERTCertificate, | |
26 crypto::NSSDestroyer<CERTCertificate, CERT_DestroyCertificate>> | |
27 ScopedCERTCertificate; | |
28 | |
29 class CertVerificationContextNSS : public CertVerificationContext { | |
30 public: | |
31 explicit CertVerificationContextNSS(CERTCertificate* x509) : x509_(x509) {} | |
Ryan Sleevi
2014/12/15 21:43:00
NAMING: s/x509/certificate/ (throughout)
sheretov
2014/12/16 08:44:21
Done.
| |
32 | |
33 VerificationResult VerifySignatureOverData( | |
34 const base::StringPiece& signature, | |
35 const base::StringPiece& data) const override { | |
36 // Verify that the |signature| matches |peer_cert|. | |
37 crypto::ScopedSECKEYPublicKey public_key( | |
38 CERT_ExtractPublicKey(x509_.get())); | |
39 if (!public_key.get()) { | |
40 return VerificationResult( | |
41 "Unable to extract public key from certificate", | |
42 VerificationResult::ERROR_CANNOT_EXTRACT_PUBLIC_KEY, PORT_GetError()); | |
43 } | |
44 SECItem signature_item; | |
45 signature_item.type = siBuffer; | |
46 signature_item.data = | |
47 reinterpret_cast<unsigned char*>(const_cast<char*>(signature.data())); | |
48 signature_item.len = signature.length(); | |
49 SECStatus verified = VFY_VerifyDataDirect( | |
50 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())), | |
51 data.size(), public_key.get(), &signature_item, | |
52 SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA1, NULL, NULL); | |
53 | |
54 if (verified != SECSuccess) { | |
55 return VerificationResult("Signature verification failed.", | |
56 VerificationResult::ERROR_SIGNATURE_INVALID, | |
57 PORT_GetError()); | |
58 } | |
59 VLOG(1) << "Signature verification succeeded"; | |
60 return VerificationResult(); | |
61 } | |
62 | |
63 std::string getCommonName() const override { | |
64 char* common_name = CERT_GetCommonName(&x509_->subject); | |
65 if (!common_name) { | |
66 LOG(ERROR) << "Certificate does not have common name."; | |
67 return ""; | |
Ryan Sleevi
2014/12/15 21:43:00
s/""/std::string()
sheretov
2014/12/16 08:44:21
Done.
| |
68 } else { | |
69 std::string result(common_name); | |
70 PORT_Free(common_name); | |
71 return result; | |
72 } | |
73 } | |
74 | |
75 private: | |
76 ScopedCERTCertificate x509_; | |
77 }; | |
78 | |
79 } // namespace | |
80 | |
81 VerificationResult VerifyCert(const base::StringPiece& device_cert, | |
82 const std::vector<std::string>& ica_certs, | |
83 CertVerificationContext** out_context) { | |
84 const std::string kErrorPrefix("Failed to verify credentials: "); | |
85 | |
86 // If the list of intermediates is empty then use kPublicKeyICA1 as | |
87 // the trusted CA (legacy case). | |
88 // Otherwise, use the first intermediate in the list as long as it | |
89 // is in the allowed list of intermediates. | |
90 int num_intermediates = ica_certs.size(); | |
91 | |
92 VLOG(1) << "Response has " << num_intermediates << " intermediates"; | |
Ryan Sleevi
2014/12/15 21:43:00
SPAM: This is spammy. Don't log like this. It does
sheretov
2014/12/16 08:44:21
Done.
| |
93 | |
94 base::StringPiece ica; | |
95 if (num_intermediates <= 0) { | |
96 ica = cast_channel::GetDefaultTrustedICAPublicKey(); | |
97 } else { | |
98 ica = cast_channel::GetTrustedICAPublicKey(ica_certs[0]); | |
99 } | |
100 if (ica.empty()) { | |
101 return VerificationResult( | |
102 "Disallowed intermediate cert", | |
103 VerificationResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA); | |
104 } | |
105 | |
106 SECItem trusted_ca_key_der; | |
107 trusted_ca_key_der.type = SECItemType::siDERCertBuffer; | |
108 trusted_ca_key_der.data = | |
109 const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(ica.data())); | |
110 trusted_ca_key_der.len = ica.size(); | |
111 | |
112 crypto::EnsureNSSInit(); | |
113 SECItem der_cert; | |
114 der_cert.type = siDERCertBuffer; | |
115 // Make a copy of certificate string so it is safe to type cast. | |
116 der_cert.data = | |
117 reinterpret_cast<unsigned char*>(const_cast<char*>(device_cert.data())); | |
118 der_cert.len = device_cert.length(); | |
119 | |
120 // Parse into a certificate structure. | |
121 ScopedCERTCertificate cert(CERT_NewTempCertificate( | |
122 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE)); | |
123 if (!cert.get()) { | |
124 return VerificationResult("Failed to parse certificate.", | |
125 VerificationResult::ERROR_CERT_PARSING_FAILED, | |
126 PORT_GetError()); | |
127 } | |
128 | |
129 // Check that the certificate is signed by trusted CA. | |
130 // NOTE: We const_cast trusted_ca_key_der since on some platforms | |
131 // SECKEY_ImportDERPublicKey API takes in SECItem* and not const | |
132 // SECItem*. | |
133 crypto::ScopedSECKEYPublicKey ca_public_key( | |
134 SECKEY_ImportDERPublicKey(&trusted_ca_key_der, CKK_RSA)); | |
135 if (!ca_public_key) { | |
136 return VerificationResult( | |
137 "Failed to import public key from CA certificate.", | |
138 VerificationResult::ERROR_CERT_PARSING_FAILED, PORT_GetError()); | |
139 } | |
140 SECStatus verified = CERT_VerifySignedDataWithPublicKey( | |
141 &cert->signatureWrap, ca_public_key.get(), NULL); | |
142 if (verified != SECSuccess) { | |
143 return VerificationResult( | |
144 "Cert not signed by trusted CA", | |
145 VerificationResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, | |
146 PORT_GetError()); | |
147 } | |
148 | |
149 VLOG(1) << "Cert signed by trusted CA"; | |
Ryan Sleevi
2014/12/15 21:43:00
SPAM: This is also spammy. Considering you don't l
sheretov
2014/12/16 08:44:21
Done.
| |
150 | |
151 if (out_context) | |
152 *out_context = new CertVerificationContextNSS(cert.release()); | |
153 | |
154 return VerificationResult(); | |
155 } | |
156 | |
157 } // namespace cast_crypto | |
158 } // namespace core_api | |
159 } // namespace extensions | |
OLD | NEW |