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

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

Issue 2289933002: Optimize X509 DER cache for memory. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 3 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/x509_util_openssl.h" 5 #include "net/cert/x509_util_openssl.h"
6 6
7 #include <limits.h> 7 #include <limits.h>
8 #include <openssl/asn1.h> 8 #include <openssl/asn1.h>
9 #include <openssl/digest.h> 9 #include <openssl/digest.h>
10 #include <openssl/mem.h> 10 #include <openssl/mem.h>
11 11
12 #include <algorithm> 12 #include <algorithm>
13 #include <map>
13 #include <memory> 14 #include <memory>
14 15
15 #include "base/lazy_instance.h" 16 #include "base/lazy_instance.h"
16 #include "base/logging.h" 17 #include "base/logging.h"
17 #include "base/macros.h" 18 #include "base/macros.h"
18 #include "base/strings/string_piece.h" 19 #include "base/strings/string_piece.h"
19 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
21 #include "base/synchronization/lock.h"
20 #include "crypto/ec_private_key.h" 22 #include "crypto/ec_private_key.h"
21 #include "crypto/openssl_util.h" 23 #include "crypto/openssl_util.h"
22 #include "crypto/rsa_private_key.h" 24 #include "crypto/rsa_private_key.h"
23 #include "crypto/scoped_openssl_types.h" 25 #include "crypto/scoped_openssl_types.h"
24 #include "net/cert/internal/parse_certificate.h" 26 #include "net/cert/internal/parse_certificate.h"
25 #include "net/cert/internal/signature_algorithm.h" 27 #include "net/cert/internal/signature_algorithm.h"
26 #include "net/cert/x509_cert_types.h" 28 #include "net/cert/x509_cert_types.h"
27 #include "net/cert/x509_certificate.h" 29 #include "net/cert/x509_certificate.h"
28 #include "net/cert/x509_util.h" 30 #include "net/cert/x509_util.h"
29 #include "net/ssl/scoped_openssl_types.h" 31 #include "net/ssl/scoped_openssl_types.h"
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 // Sign it with the private key. 167 // Sign it with the private key.
166 if (!X509_sign(cert, key, md)) { 168 if (!X509_sign(cert, key, md)) {
167 LOG(ERROR) << "Could not sign certificate with key."; 169 LOG(ERROR) << "Could not sign certificate with key.";
168 return false; 170 return false;
169 } 171 }
170 172
171 // Convert it into a DER-encoded string copied to |der_encoded|. 173 // Convert it into a DER-encoded string copied to |der_encoded|.
172 return DerEncodeCert(cert, der_encoded); 174 return DerEncodeCert(cert, der_encoded);
173 } 175 }
174 176
175 struct DERCache { 177 // Re-encoding the DER data via i2d_X509 is an expensive operation, but
176 std::string data; 178 // it's necessary for comparing two certificates. Re-encode at most once
177 }; 179 // per certificate and cache the pointer to the data within the X509 cert
178 180 // using X509_set_ex_data. For memory efficiency the DER data itself is
179 void DERCache_free(void* parent, void* ptr, CRYPTO_EX_DATA* ad, int idx, 181 // stored globally, shared between certificates and is deleted once all
180 long argl, void* argp) { 182 // certificates that reference it are deleted.
181 DERCache* der_cache = static_cast<DERCache*>(ptr); 183 class DerCache {
182 delete der_cache;
183 }
184
185 class DERCacheInitSingleton {
186 public: 184 public:
187 DERCacheInitSingleton() { 185 // Returns nullptr only if DerEncodeCert() has failed.
188 crypto::EnsureOpenSSLInit(); 186 static const std::string* Get(X509* cert) {
189 der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free); 187 const std::string* cached_der = GetInstance().Retrieve(cert);
190 DCHECK_NE(-1, der_cache_ex_index_); 188 if (cached_der) {
189 return cached_der;
190 }
191 std::string der;
192 if (!DerEncodeCert(cert, &der)) {
193 return nullptr;
194 }
195 return GetInstance().Put(cert, std::move(der));
191 } 196 }
192 197
193 int der_cache_ex_index() const { return der_cache_ex_index_; } 198 private:
199 friend struct base::DefaultLazyInstanceTraits<DerCache>;
194 200
195 private: 201 DerCache() {
196 int der_cache_ex_index_; 202 crypto::EnsureOpenSSLInit();
203 x509_value_index_ =
204 X509_get_ex_new_index(0, nullptr, nullptr, nullptr, &free_x509_value);
205 DCHECK_NE(-1, x509_value_index_);
206 }
197 207
198 DISALLOW_COPY_AND_ASSIGN(DERCacheInitSingleton); 208 const std::string* Retrieve(X509* cert) const {
209 return static_cast<const std::string*>(
210 X509_get_ex_data(cert, x509_value_index_));
211 }
212
213 const std::string* Put(X509* cert, std::string der) {
214 base::AutoLock lock(lock_);
215 auto der_iter = count_by_der_.emplace(std::move(der), 0).first;
216 der_iter->second++; // add a reference
217 const std::string* cached_der = &der_iter->first;
218 X509_set_ex_data(cert, x509_value_index_,
219 const_cast<std::string*>(cached_der));
220 return cached_der;
221 }
222
223 void Remove(const std::string& der) {
224 base::AutoLock lock(lock_);
225 auto der_iter = count_by_der_.find(der);
226 DCHECK(count_by_der_.end() != der_iter);
227 der_iter->second--; // remove a reference
228 if (der_iter->second == 0) {
229 count_by_der_.erase(der_iter);
230 }
231 }
232
233 static DerCache& GetInstance() {
234 static base::LazyInstance<DerCache>::Leaky instance =
235 LAZY_INSTANCE_INITIALIZER;
236 return instance.Get();
237 }
238
239 static void free_x509_value(void* parent,
240 void* value,
241 CRYPTO_EX_DATA* ad,
242 int idx,
243 long argl,
244 void* argp) {
245 const std::string* cached_der = static_cast<const std::string*>(value);
246 DCHECK(cached_der);
247 GetInstance().Remove(*cached_der);
248 }
249
250 int x509_value_index_;
251 std::map<std::string, size_t> count_by_der_;
252 base::Lock lock_;
199 }; 253 };
200 254
201 base::LazyInstance<DERCacheInitSingleton>::Leaky g_der_cache_singleton =
202 LAZY_INSTANCE_INITIALIZER;
203
204 } // namespace 255 } // namespace
205 256
206 bool CreateSelfSignedCert(crypto::RSAPrivateKey* key, 257 bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
207 DigestAlgorithm alg, 258 DigestAlgorithm alg,
208 const std::string& common_name, 259 const std::string& common_name,
209 uint32_t serial_number, 260 uint32_t serial_number,
210 base::Time not_valid_before, 261 base::Time not_valid_before,
211 base::Time not_valid_after, 262 base::Time not_valid_after,
212 std::string* der_encoded) { 263 std::string* der_encoded) {
213 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 264 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 return false; 328 return false;
278 329
279 base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data), 330 base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data),
280 x509_time->length); 331 x509_time->length);
281 332
282 CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ? 333 CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ?
283 CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; 334 CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
284 return ParseCertificateDate(str_date, format, time); 335 return ParseCertificateDate(str_date, format, time);
285 } 336 }
286 337
287 // Returns true if |der_cache| points to valid data, false otherwise. 338 // Returns true if |out_der| points to valid data, false otherwise.
288 // (note: the DER-encoded data in |der_cache| is owned by |cert|, callers should 339 // (note: the DER-encoded data in |out_der| is owned by |cert|, callers should
289 // not free it). 340 // not free it).
290 bool GetDER(X509* x509, base::StringPiece* der_cache) { 341 bool GetDER(X509* x509, base::StringPiece* out_der) {
291 int x509_der_cache_index = 342 const std::string* cached_der = DerCache::Get(x509);
292 g_der_cache_singleton.Get().der_cache_ex_index(); 343 if (!cached_der) {
293 344 return false;
294 // Re-encoding the DER data via i2d_X509 is an expensive operation,
295 // but it's necessary for comparing two certificates. Re-encode at
296 // most once per certificate and cache the data within the X509 cert
297 // using X509_set_ex_data.
298 DERCache* internal_cache = static_cast<DERCache*>(
299 X509_get_ex_data(x509, x509_der_cache_index));
300 if (!internal_cache) {
301 std::unique_ptr<DERCache> new_cache(new DERCache);
302 if (!DerEncodeCert(x509, &new_cache->data))
303 return false;
304 internal_cache = new_cache.get();
305 X509_set_ex_data(x509, x509_der_cache_index, new_cache.release());
306 } 345 }
307 *der_cache = base::StringPiece(internal_cache->data); 346 *out_der = base::StringPiece(*cached_der);
308 return true; 347 return true;
309 } 348 }
310 349
311 bool GetTLSServerEndPointChannelBinding(const X509Certificate& certificate, 350 bool GetTLSServerEndPointChannelBinding(const X509Certificate& certificate,
312 std::string* token) { 351 std::string* token) {
313 static const char kChannelBindingPrefix[] = "tls-server-end-point:"; 352 static const char kChannelBindingPrefix[] = "tls-server-end-point:";
314 353
315 std::string der_encoded_certificate; 354 std::string der_encoded_certificate;
316 if (!X509Certificate::GetDEREncoded(certificate.os_cert_handle(), 355 if (!X509Certificate::GetDEREncoded(certificate.os_cert_handle(),
317 &der_encoded_certificate)) 356 &der_encoded_certificate))
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 396
358 digest.resize(out_size); 397 digest.resize(out_size);
359 token->assign(kChannelBindingPrefix); 398 token->assign(kChannelBindingPrefix);
360 token->append(digest.begin(), digest.end()); 399 token->append(digest.begin(), digest.end());
361 return true; 400 return true;
362 } 401 }
363 402
364 } // namespace x509_util 403 } // namespace x509_util
365 404
366 } // namespace net 405 } // namespace net
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698