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

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

Issue 27026002: CT: Adding preliminary Certificate Transparency support to Chromium. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Distinguish between SCTs from unknown logs and unverified ones Created 7 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.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) 2013 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/ct_objects_extractor.h"
6
7 #include <cert.h>
8 #include <secasn1.h>
9 #include <secitem.h>
10 #include <secoid.h>
11
12 #include "base/lazy_instance.h"
13 #include "crypto/scoped_nss_types.h"
14 #include "crypto/sha2.h"
15 #include "net/cert/asn1_util.h"
16 #include "net/cert/signed_certificate_timestamp.h"
17
18 namespace net {
19
20 namespace ct {
21
22 namespace {
23
24 struct FreeCERTCertificate {
25 public:
26 inline void operator()(CERTCertificate* x) const {
27 CERT_DestroyCertificate(x);
28 }
29 };
30
31 typedef scoped_ptr_malloc<CERTCertificate, FreeCERTCertificate>
32 ScopedCERTCertificate;
33
34 // Wrapper class to convert a X509Certificate::OSCertHandle directly
35 // into a CERTCertificate* usable with other NSS functions. This is used for
36 // systems where X509Certificate::OSCertHandle refers to a different type
37 // than a CERTCertificate*.
38 struct NSSCertWrapper {
39 explicit NSSCertWrapper(X509Certificate::OSCertHandle cert_handle);
40 ~NSSCertWrapper() {}
41
42 ScopedCERTCertificate cert;
43 };
44
45 NSSCertWrapper::NSSCertWrapper(X509Certificate::OSCertHandle cert_handle) {
46 #if defined(USE_NSS)
47 cert.reset(CERT_DupCertificate(cert_handle));
48 #else
49 SECItem der_cert;
50 std::string der_data;
51 if (!X509Certificate::GetDEREncoded(cert_handle, &der_data))
52 return;
53 der_cert.data = reinterpret_cast<unsigned char*>(
54 const_cast<char*>(der_data.data()));
55 der_cert.len = der_data.size();
56
57 // Note: CERT_NewTempCertificate may return NULL if the certificate
58 // shares a serial number with another cert, which is not supposed
59 // to happen.
60 cert.reset(CERT_NewTempCertificate(
61 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE));
62 #endif
63 DCHECK(cert.get() != NULL);
64 }
65
66 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
67 // RFC6962.
68 const unsigned char kEmbeddedSCTOid[] =
69 { 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x04, 0x02 };
70 const char kEmbeddedSCTDescription[] =
71 "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp "
72 "List";
73
74 // Initializes the necessary NSS internals for use with Certificate
75 // Transparency.
76 class CTInitSingleton {
77 public:
78 SECOidTag embedded_oid() const { return embedded_oid_; }
79
80 private:
81 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>;
82
83 CTInitSingleton()
84 : embedded_oid_(SEC_OID_UNKNOWN) {
85 embedded_oid_ = RegisterOid(kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid),
86 kEmbeddedSCTDescription);
87 }
88
89 ~CTInitSingleton() {}
90
91 SECOidTag RegisterOid(const unsigned char* oid,
92 unsigned int oid_len,
93 const char* description) {
94 SECOidData oid_data;
95 oid_data.oid.len = oid_len;
96 oid_data.oid.data = const_cast<unsigned char*>(oid);
97 oid_data.offset = SEC_OID_UNKNOWN;
98 oid_data.desc = description;
99 oid_data.mechanism = CKM_INVALID_MECHANISM;
100 // Setting this to SUPPORTED_CERT_EXTENSION ensures that if a certificate
101 // contains this extension with the critical bit set, NSS will not reject
102 // it. However, because verification of this extension happens after NSS,
103 // it is currently left as INVALID_CERT_EXTENSION.
104 oid_data.supportedExtension = INVALID_CERT_EXTENSION;
105
106 SECOidTag result = SECOID_AddEntry(&oid_data);
107 CHECK_NE(SEC_OID_UNKNOWN, result);
108
109 return result;
110 }
111
112 SECOidTag embedded_oid_;
113
114 DISALLOW_COPY_AND_ASSIGN(CTInitSingleton);
115 };
116
117 base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton =
118 LAZY_INSTANCE_INITIALIZER;
119
120 // Obtains the data for an X.509v3 certificate extension identified by |oid|
121 // and encoded as an OCTET STRING. Returns true if the extension was found,
122 // updating |ext_data| to be the extension data after removing the DER
123 // encoding.
124 bool GetOctetStringExtension(CERTCertificate* cert,
125 SECOidTag oid,
126 std::string* extension_data) {
127 SECItem extension;
128 SECStatus rv = CERT_FindCertExtension(cert, oid, &extension);
129 if (rv != SECSuccess)
130 return false;
131
132 base::StringPiece raw_data(reinterpret_cast<char*>(extension.data),
133 extension.len);
134 base::StringPiece parsed_data;
135 if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data)) {
136 rv = SECFailure;
137 } else {
138 parsed_data.CopyToString(extension_data);
139 }
140
141 SECITEM_FreeItem(&extension, PR_FALSE);
142 return rv == SECSuccess;
143 }
144
145 bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert,
146 std::string* to_be_signed) {
147 SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid());
148 if (!oid)
149 return false;
150
151 // This is a giant hack, due to the fact that NSS does not expose a good API
152 // for simply removing certificate fields from existing certificates.
153 CERTCertificateStr temp_cert;
154 temp_cert = *cert;
155 temp_cert.extensions = NULL;
156
157 // Strip out the embedded SCT OID from the new certificate by directly
158 // mutating the extensions in place.
159 std::vector<CERTCertExtension*> new_extensions;
160 if (cert->extensions) {
161 for (CERTCertExtension** exts = cert->extensions; *exts; ++exts) {
162 CERTCertExtension* ext = *exts;
163 SECComparison result = SECITEM_CompareItem(&oid->oid, &ext->id);
164 if (result != SECEqual)
165 new_extensions.push_back(ext);
166 }
167 }
168 if (!new_extensions.empty()) {
169 new_extensions.push_back(NULL);
170 // XXX(eranm): Should probabry convert to a proper array here.
171 temp_cert.extensions = &new_extensions[0];
172 }
173
174 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
175
176 SECItem tbs_data;
177 tbs_data.len = 0;
178 tbs_data.data = NULL;
179 void* result = SEC_ASN1EncodeItem(arena.get(),
180 &tbs_data,
181 &temp_cert,
182 SEC_ASN1_GET(CERT_CertificateTemplate));
183 if (!result || tbs_data.len == 0)
184 return false;
185
186 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len);
187 return true;
188 }
189
190 } // namespace
191
192 bool ExtractEmbeddedSCTs(X509Certificate::OSCertHandle cert,
193 std::string* sct_list) {
194 DCHECK(cert);
195
196 NSSCertWrapper leaf_cert(cert);
197 if (!leaf_cert.cert)
198 return false;
199
200 return GetOctetStringExtension(
201 leaf_cert.cert.get(), g_ct_singleton.Get().embedded_oid(), sct_list);
202 }
203
204 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf,
205 X509Certificate::OSCertHandle issuer,
206 LogEntry* result) {
207 DCHECK(leaf);
208 DCHECK(issuer);
209
210 NSSCertWrapper leaf_cert(leaf);
211 NSSCertWrapper issuer_cert(issuer);
212
213 // XXX(rsleevi): This check may be overkill, since we should be able to
214 // generate precerts for certs without the extension. For now, just a sanity
215 // check to match the reference implementation.
216 SECItem extension;
217 SECStatus rv = CERT_FindCertExtension(
218 leaf_cert.cert.get(), g_ct_singleton.Get().embedded_oid(), &extension);
219 if (rv != SECSuccess)
220 return false;
221 SECITEM_FreeItem(&extension, PR_FALSE);
222
223 std::string to_be_signed;
224 if (!ExtractTBSCertWithoutSCTs(leaf_cert.cert.get(), &to_be_signed))
225 return false;
226
227 result->Reset();
228 result->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT;
229 result->tbs_certificate.swap(to_be_signed);
230 if (!issuer_cert.cert) {
231 // This can happen when the issuer and leaf certs share the same serial
232 // number, which should never be the case (but happened with bad test
233 // certs).
234 VLOG(1) << "Issuer cert is null, failing SCT verification.";
235 return false;
236 }
237
238 // Hash the entire SPKI, not just the public key.
239 // See https://codereview.appspot.com/8269046/
240 SECKEYPublicKey *issuer_pub_key =
241 SECKEY_ExtractPublicKey(&(issuer_cert.cert->subjectPublicKeyInfo));
242 SECItem *encoded_issuer_pubKey =
243 SECKEY_EncodeDERSubjectPublicKeyInfo(issuer_pub_key);
244
245 crypto::SHA256HashString(
246 base::StringPiece(
247 reinterpret_cast<char*>(encoded_issuer_pubKey->data),
248 encoded_issuer_pubKey->len),
249 result->issuer_key_hash.data,
250 sizeof(result->issuer_key_hash.data));
251
252 SECITEM_FreeItem(encoded_issuer_pubKey, PR_TRUE);
253 encoded_issuer_pubKey = NULL;
254 SECKEY_DestroyPublicKey(issuer_pub_key);
255 issuer_pub_key = NULL;
256
257 return true;
258 }
259
260 bool GetAsn1CertLogEntry(X509Certificate::OSCertHandle leaf,
261 LogEntry* result) {
262 DCHECK(leaf);
263
264 std::string encoded;
265 if (!X509Certificate::GetDEREncoded(leaf, &encoded))
266 return false;
267
268 result->Reset();
269 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509;
270 result->leaf_certificate.swap(encoded);
271 return true;
272 }
273
274 } // namespace ct
275
276 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698