blob: 82556b44128093b5eeebb68d498f94a6de6309db [file] [log] [blame]
[email protected]9fc44162012-01-23 22:56:411// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]bc1e07c72008-09-16 14:32:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]4b559b4d2011-04-14 17:37:145#include "crypto/nss_util.h"
6#include "crypto/nss_util_internal.h"
[email protected]bc1e07c72008-09-16 14:32:447
[email protected]bc1e07c72008-09-16 14:32:448#include <nss.h>
[email protected]ea1a3f62012-11-16 20:34:239#include <pk11pub.h>
[email protected]c72f16a2009-03-19 16:02:3110#include <plarena.h>
[email protected]6e7e8062009-04-13 17:35:0911#include <prerror.h>
[email protected]c72f16a2009-03-19 16:02:3112#include <prinit.h>
[email protected]1b1a264a2010-01-14 22:36:3513#include <prtime.h>
[email protected]ea224582008-12-07 20:25:4614#include <secmod.h>
[email protected]b43c97c2008-10-22 19:50:5815
[email protected]a8b58f42010-07-14 20:21:3516#if defined(OS_LINUX)
[email protected]1f59edcb2010-11-04 00:32:2517#include <linux/nfs_fs.h>
[email protected]a8b58f42010-07-14 20:21:3518#include <sys/vfs.h>
[email protected]d816516f2011-10-25 19:11:5919#elif defined(OS_OPENBSD)
20#include <sys/mount.h>
21#include <sys/param.h>
[email protected]a8b58f42010-07-14 20:21:3522#endif
23
[email protected]f6a67b42011-03-17 23:49:2124#include <vector>
25
[email protected]f3d445e2013-11-22 18:35:0326#include "base/cpu.h"
[email protected]716fb112012-11-15 05:41:2527#include "base/debug/alias.h"
[email protected]0f8f69c2013-11-12 02:56:3128#include "base/debug/stack_trace.h"
[email protected]ed450f32011-03-16 01:26:4929#include "base/environment.h"
[email protected]ea224582008-12-07 20:25:4630#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:5231#include "base/files/file_path.h"
[email protected]ea1a3f62012-11-16 20:34:2332#include "base/files/scoped_temp_dir.h"
[email protected]f615bda32010-11-21 01:04:5633#include "base/lazy_instance.h"
[email protected]c1444fe2008-09-17 09:42:5134#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1535#include "base/memory/scoped_ptr.h"
[email protected]68bf4482013-01-22 16:39:3436#include "base/metrics/histogram.h"
[email protected]f6a67b42011-03-17 23:49:2137#include "base/native_library.h"
[email protected]0d8db082013-06-11 07:27:0138#include "base/strings/stringprintf.h"
[email protected]0f8f69c2013-11-12 02:56:3139#include "base/threading/thread_checker.h"
[email protected]34b99632011-01-01 01:01:0640#include "base/threading/thread_restrictions.h"
[email protected]26661c22011-10-07 01:33:5841#include "build/build_config.h"
[email protected]bc1e07c72008-09-16 14:32:4442
[email protected]a8b58f42010-07-14 20:21:3543// USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not
44// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
45// use NSS for crypto or certificate verification, and we don't use the NSS
46// certificate and key databases.
[email protected]69138472010-06-25 22:44:4847#if defined(USE_NSS)
[email protected]20305ec2011-01-21 04:55:5248#include "base/synchronization/lock.h"
[email protected]4b559b4d2011-04-14 17:37:1449#include "crypto/crypto_module_blocking_password_delegate.h"
[email protected]a8b58f42010-07-14 20:21:3550#endif // defined(USE_NSS)
[email protected]abd4aba2010-01-27 19:36:2251
[email protected]4b559b4d2011-04-14 17:37:1452namespace crypto {
[email protected]f1633932010-08-17 23:05:2853
[email protected]bc1e07c72008-09-16 14:32:4454namespace {
55
[email protected]6a89ef22011-04-07 17:34:2156#if defined(OS_CHROMEOS)
57const char kNSSDatabaseName[] = "Real NSS database";
58
[email protected]84e47722011-11-17 05:12:0259// Constants for loading the Chrome OS TPM-backed PKCS #11 library.
60const char kChapsModuleName[] = "Chaps";
61const char kChapsPath[] = "libchaps.so";
[email protected]6a89ef22011-04-07 17:34:2162
[email protected]6a89ef22011-04-07 17:34:2163// Fake certificate authority database used for testing.
[email protected]9e275712013-02-10 19:20:1464static const base::FilePath::CharType kReadOnlyCertDB[] =
[email protected]6a89ef22011-04-07 17:34:2165 FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
66#endif // defined(OS_CHROMEOS)
67
68std::string GetNSSErrorMessage() {
69 std::string result;
70 if (PR_GetErrorTextLength()) {
[email protected]e8328952013-04-09 17:35:4271 scoped_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]);
[email protected]6a89ef22011-04-07 17:34:2172 PRInt32 copied = PR_GetErrorText(error_text.get());
73 result = std::string(error_text.get(), copied);
74 } else {
[email protected]7d3cbc92013-03-18 22:33:0475 result = base::StringPrintf("NSS error code: %d", PR_GetError());
[email protected]6a89ef22011-04-07 17:34:2176 }
77 return result;
78}
79
[email protected]a8b58f42010-07-14 20:21:3580#if defined(USE_NSS)
[email protected]9e275712013-02-10 19:20:1481base::FilePath GetDefaultConfigDirectory() {
[email protected]fb4bcfa32013-12-02 18:55:4982 base::FilePath dir = base::GetHomeDir();
[email protected]a8b58f42010-07-14 20:21:3583 if (dir.empty()) {
84 LOG(ERROR) << "Failed to get home directory.";
85 return dir;
[email protected]86913342009-05-25 02:14:3486 }
[email protected]86913342009-05-25 02:14:3487 dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
[email protected]426d1c92013-12-03 20:08:5488 if (!base::CreateDirectory(dir)) {
[email protected]948a707b2011-06-07 22:51:4489 LOG(ERROR) << "Failed to create " << dir.value() << " directory.";
[email protected]a8b58f42010-07-14 20:21:3590 dir.clear();
[email protected]86913342009-05-25 02:14:3491 }
[email protected]a8b58f42010-07-14 20:21:3592 return dir;
[email protected]86913342009-05-25 02:14:3493}
94
[email protected]259c42f2013-09-12 20:32:2295// On non-Chrome OS platforms, return the default config directory. On Chrome OS
96// test images, return a read-only directory with fake root CA certs (which are
97// used by the local Google Accounts server mock we use when testing our login
98// code). On Chrome OS non-test images (where the read-only directory doesn't
99// exist), return an empty path.
[email protected]9e275712013-02-10 19:20:14100base::FilePath GetInitialConfigDirectory() {
[email protected]dcce6cf2010-04-29 17:50:06101#if defined(OS_CHROMEOS)
[email protected]259c42f2013-09-12 20:32:22102 base::FilePath database_dir = base::FilePath(kReadOnlyCertDB);
103 if (!base::PathExists(database_dir))
104 database_dir.clear();
105 return database_dir;
[email protected]dcce6cf2010-04-29 17:50:06106#else
107 return GetDefaultConfigDirectory();
108#endif // defined(OS_CHROMEOS)
109}
110
[email protected]88b9db72011-01-13 01:48:43111// This callback for NSS forwards all requests to a caller-specified
[email protected]3f1f8412011-01-19 03:01:23112// CryptoModuleBlockingPasswordDelegate object.
113char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
[email protected]4b559b4d2011-04-14 17:37:14114 crypto::CryptoModuleBlockingPasswordDelegate* delegate =
115 reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg);
[email protected]88b9db72011-01-13 01:48:43116 if (delegate) {
117 bool cancelled = false;
118 std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
119 retry != PR_FALSE,
120 &cancelled);
121 if (cancelled)
122 return NULL;
123 char* result = PORT_Strdup(password.c_str());
124 password.replace(0, password.size(), password.size(), 0);
125 return result;
126 }
127 DLOG(ERROR) << "PK11 password requested with NULL arg";
128 return NULL;
129}
130
[email protected]a8b58f42010-07-14 20:21:35131// NSS creates a local cache of the sqlite database if it detects that the
132// filesystem the database is on is much slower than the local disk. The
133// detection doesn't work with the latest versions of sqlite, such as 3.6.22
134// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set
135// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
136// detection when database_dir is on NFS. See http://crbug.com/48585.
137//
138// TODO(wtc): port this function to other USE_NSS platforms. It is defined
[email protected]d816516f2011-10-25 19:11:59139// only for OS_LINUX and OS_OPENBSD simply because the statfs structure
140// is OS-specific.
[email protected]ac3d5972011-01-13 20:33:45141//
142// Because this function sets an environment variable it must be run before we
143// go multi-threaded.
[email protected]9e275712013-02-10 19:20:14144void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) {
[email protected]d816516f2011-10-25 19:11:59145#if defined(OS_LINUX) || defined(OS_OPENBSD)
[email protected]a8b58f42010-07-14 20:21:35146 struct statfs buf;
147 if (statfs(database_dir.value().c_str(), &buf) == 0) {
[email protected]d816516f2011-10-25 19:11:59148#if defined(OS_LINUX)
[email protected]a8b58f42010-07-14 20:21:35149 if (buf.f_type == NFS_SUPER_MAGIC) {
[email protected]d816516f2011-10-25 19:11:59150#elif defined(OS_OPENBSD)
151 if (strcmp(buf.f_fstypename, MOUNT_NFS) == 0) {
152#endif
[email protected]4b559b4d2011-04-14 17:37:14153 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]a8b58f42010-07-14 20:21:35154 const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
[email protected]9432ade2010-08-04 23:43:20155 if (!env->HasVar(use_cache_env_var))
[email protected]c87bcf002010-08-06 01:03:37156 env->SetVar(use_cache_env_var, "yes");
[email protected]a8b58f42010-07-14 20:21:35157 }
158 }
[email protected]d816516f2011-10-25 19:11:59159#endif // defined(OS_LINUX) || defined(OS_OPENBSD)
[email protected]a8b58f42010-07-14 20:21:35160}
161
[email protected]a8b58f42010-07-14 20:21:35162#endif // defined(USE_NSS)
[email protected]ea224582008-12-07 20:25:46163
[email protected]730fb132009-09-02 22:50:25164// A singleton to initialize/deinitialize NSPR.
165// Separate from the NSS singleton because we initialize NSPR on the UI thread.
[email protected]f615bda32010-11-21 01:04:56166// Now that we're leaking the singleton, we could merge back with the NSS
167// singleton.
[email protected]730fb132009-09-02 22:50:25168class NSPRInitSingleton {
[email protected]f615bda32010-11-21 01:04:56169 private:
[email protected]4b559b4d2011-04-14 17:37:14170 friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
[email protected]f615bda32010-11-21 01:04:56171
[email protected]730fb132009-09-02 22:50:25172 NSPRInitSingleton() {
173 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
174 }
175
[email protected]f615bda32010-11-21 01:04:56176 // NOTE(willchan): We don't actually execute this code since we leak NSS to
177 // prevent non-joinable threads from using NSS after it's already been shut
178 // down.
[email protected]730fb132009-09-02 22:50:25179 ~NSPRInitSingleton() {
[email protected]829296f2010-01-27 02:58:03180 PL_ArenaFinish();
[email protected]730fb132009-09-02 22:50:25181 PRStatus prstatus = PR_Cleanup();
[email protected]450b4ad72012-05-17 10:04:17182 if (prstatus != PR_SUCCESS)
[email protected]730fb132009-09-02 22:50:25183 LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
[email protected]730fb132009-09-02 22:50:25184 }
185};
186
[email protected]9fc44162012-01-23 22:56:41187base::LazyInstance<NSPRInitSingleton>::Leaky
[email protected]6de0fd1d2011-11-15 13:31:49188 g_nspr_singleton = LAZY_INSTANCE_INITIALIZER;
[email protected]f615bda32010-11-21 01:04:56189
[email protected]02746292012-01-25 04:37:51190// This is a LazyInstance so that it will be deleted automatically when the
191// unittest exits. NSSInitSingleton is a LeakySingleton, so it would not be
192// deleted if it were a regular member.
[email protected]ea1a3f62012-11-16 20:34:23193base::LazyInstance<base::ScopedTempDir> g_test_nss_db_dir =
194 LAZY_INSTANCE_INITIALIZER;
[email protected]02746292012-01-25 04:37:51195
[email protected]007f5122012-11-21 16:00:21196// Force a crash with error info on NSS_NoDB_Init failure.
197void CrashOnNSSInitFailure() {
198 int nss_error = PR_GetError();
199 int os_error = PR_GetOSError();
[email protected]716fb112012-11-15 05:41:25200 base::debug::Alias(&nss_error);
201 base::debug::Alias(&os_error);
[email protected]007f5122012-11-21 16:00:21202 LOG(ERROR) << "Error initializing NSS without a persistent database: "
203 << GetNSSErrorMessage();
[email protected]2b12459a2012-11-16 03:45:32204 LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error;
[email protected]716fb112012-11-15 05:41:25205}
206
[email protected]bc1e07c72008-09-16 14:32:44207class NSSInitSingleton {
208 public:
[email protected]f615bda32010-11-21 01:04:56209#if defined(OS_CHROMEOS)
210 void OpenPersistentNSSDB() {
[email protected]0f8f69c2013-11-12 02:56:31211 DCHECK(thread_checker_.CalledOnValidThread());
212
[email protected]f615bda32010-11-21 01:04:56213 if (!chromeos_user_logged_in_) {
[email protected]61f93212011-01-24 14:37:08214 // GetDefaultConfigDirectory causes us to do blocking IO on UI thread.
[email protected]6a89ef22011-04-07 17:34:21215 // Temporarily allow it until we fix http://crbug.com/70119
[email protected]4b559b4d2011-04-14 17:37:14216 base::ThreadRestrictions::ScopedAllowIO allow_io;
[email protected]f615bda32010-11-21 01:04:56217 chromeos_user_logged_in_ = true;
[email protected]6a89ef22011-04-07 17:34:21218
219 // This creates another DB slot in NSS that is read/write, unlike
220 // the fake root CA cert DB and the "default" crypto key
221 // provider, which are still read-only (because we initialized
222 // NSS before we had a cryptohome mounted).
223 software_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
[email protected]74beead2011-04-12 20:40:12224 kNSSDatabaseName);
[email protected]f615bda32010-11-21 01:04:56225 }
226 }
[email protected]6a89ef22011-04-07 17:34:21227
[email protected]450b4ad72012-05-17 10:04:17228 void EnableTPMTokenForNSS() {
[email protected]0f8f69c2013-11-12 02:56:31229 DCHECK(thread_checker_.CalledOnValidThread());
230
[email protected]3f3b9b12013-10-25 22:03:26231 // If this gets set, then we'll use the TPM for certs with
232 // private keys, otherwise we'll fall back to the software
233 // implementation.
[email protected]450b4ad72012-05-17 10:04:17234 tpm_token_enabled_for_nss_ = true;
[email protected]6a89ef22011-04-07 17:34:21235 }
236
[email protected]bee9b542013-11-25 21:45:04237 bool InitializeTPMToken(int token_slot_id) {
[email protected]0f8f69c2013-11-12 02:56:31238 DCHECK(thread_checker_.CalledOnValidThread());
239
[email protected]450b4ad72012-05-17 10:04:17240 // If EnableTPMTokenForNSS hasn't been called, return false.
241 if (!tpm_token_enabled_for_nss_)
242 return false;
[email protected]14172c82012-02-28 10:34:21243
[email protected]450b4ad72012-05-17 10:04:17244 // If everything is already initialized, then return true.
245 if (chaps_module_ && tpm_slot_)
246 return true;
247
[email protected]450b4ad72012-05-17 10:04:17248 // This tries to load the Chaps module so NSS can talk to the hardware
249 // TPM.
250 if (!chaps_module_) {
251 chaps_module_ = LoadModule(
252 kChapsModuleName,
253 kChapsPath,
254 // For more details on these parameters, see:
255 // https://developer.mozilla.org/en/PKCS11_Module_Specs
256 // slotFlags=[PublicCerts] -- Certificates and public keys can be
257 // read from this slot without requiring a call to C_Login.
258 // askpw=only -- Only authenticate to the token when necessary.
259 "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
[email protected]3f3b9b12013-10-25 22:03:26260 if (!chaps_module_ && test_slot_) {
261 // chromeos_unittests try to test the TPM initialization process. If we
262 // have a test DB open, pretend that it is the TPM slot.
263 tpm_slot_ = PK11_ReferenceSlot(test_slot_);
264 return true;
265 }
[email protected]14172c82012-02-28 10:34:21266 }
[email protected]450b4ad72012-05-17 10:04:17267 if (chaps_module_){
[email protected]3f3b9b12013-10-25 22:03:26268 tpm_slot_ = GetTPMSlotForId(token_slot_id);
[email protected]450b4ad72012-05-17 10:04:17269
270 return tpm_slot_ != NULL;
271 }
272 return false;
[email protected]c175cdb2011-06-28 20:41:55273 }
274
[email protected]c64b9142011-04-19 18:49:54275 bool IsTPMTokenReady() {
[email protected]0f8f69c2013-11-12 02:56:31276 // TODO(mattm): Change to DCHECK when callers have been fixed.
277 if (!thread_checker_.CalledOnValidThread()) {
278 DVLOG(1) << "Called on wrong thread.\n"
279 << base::debug::StackTrace().ToString();
280 }
281
[email protected]c64b9142011-04-19 18:49:54282 return tpm_slot_ != NULL;
[email protected]74beead2011-04-12 20:40:12283 }
284
[email protected]3f3b9b12013-10-25 22:03:26285 // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot
286 // id as an int. This should be safe since this is only used with chaps, which
287 // we also control.
288 PK11SlotInfo* GetTPMSlotForId(CK_SLOT_ID slot_id) {
[email protected]0f8f69c2013-11-12 02:56:31289 DCHECK(thread_checker_.CalledOnValidThread());
290
[email protected]3f3b9b12013-10-25 22:03:26291 if (!chaps_module_)
292 return NULL;
293
294 VLOG(1) << "Poking chaps module.";
295 SECStatus rv = SECMOD_UpdateSlotList(chaps_module_);
296 if (rv != SECSuccess)
297 PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError();
298
299 PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module_->moduleID, slot_id);
300 if (!slot)
301 LOG(ERROR) << "TPM slot " << slot_id << " not found.";
302 return slot;
[email protected]6a89ef22011-04-07 17:34:21303 }
[email protected]f615bda32010-11-21 01:04:56304#endif // defined(OS_CHROMEOS)
305
[email protected]6a89ef22011-04-07 17:34:21306
[email protected]02746292012-01-25 04:37:51307 bool OpenTestNSSDB() {
[email protected]0f8f69c2013-11-12 02:56:31308 DCHECK(thread_checker_.CalledOnValidThread());
309
[email protected]02746292012-01-25 04:37:51310 if (test_slot_)
311 return true;
312 if (!g_test_nss_db_dir.Get().CreateUniqueTempDir())
313 return false;
[email protected]b17bc3662013-08-12 14:09:19314 test_slot_ = OpenUserDB(g_test_nss_db_dir.Get().path(), kTestTPMTokenName);
[email protected]6a89ef22011-04-07 17:34:21315 return !!test_slot_;
[email protected]f615bda32010-11-21 01:04:56316 }
317
318 void CloseTestNSSDB() {
[email protected]0f8f69c2013-11-12 02:56:31319 DCHECK(thread_checker_.CalledOnValidThread());
320
[email protected]166fac0d2013-06-29 21:18:16321 if (!test_slot_)
322 return;
323 SECStatus status = SECMOD_CloseUserDB(test_slot_);
324 if (status != SECSuccess)
325 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
326 PK11_FreeSlot(test_slot_);
327 test_slot_ = NULL;
328 ignore_result(g_test_nss_db_dir.Get().Delete());
[email protected]f615bda32010-11-21 01:04:56329 }
330
[email protected]6a89ef22011-04-07 17:34:21331 PK11SlotInfo* GetPublicNSSKeySlot() {
[email protected]0f8f69c2013-11-12 02:56:31332 // TODO(mattm): Change to DCHECK when callers have been fixed.
333 if (!thread_checker_.CalledOnValidThread()) {
334 DVLOG(1) << "Called on wrong thread.\n"
335 << base::debug::StackTrace().ToString();
336 }
337
[email protected]6a89ef22011-04-07 17:34:21338 if (test_slot_)
339 return PK11_ReferenceSlot(test_slot_);
340 if (software_slot_)
341 return PK11_ReferenceSlot(software_slot_);
342 return PK11_GetInternalKeySlot();
343 }
344
345 PK11SlotInfo* GetPrivateNSSKeySlot() {
[email protected]0f8f69c2013-11-12 02:56:31346 // TODO(mattm): Change to DCHECK when callers have been fixed.
347 if (!thread_checker_.CalledOnValidThread()) {
348 DVLOG(1) << "Called on wrong thread.\n"
349 << base::debug::StackTrace().ToString();
350 }
351
[email protected]6a89ef22011-04-07 17:34:21352 if (test_slot_)
353 return PK11_ReferenceSlot(test_slot_);
[email protected]c64b9142011-04-19 18:49:54354
355#if defined(OS_CHROMEOS)
[email protected]450b4ad72012-05-17 10:04:17356 if (tpm_token_enabled_for_nss_) {
[email protected]20ba9d82012-02-15 04:17:00357 if (IsTPMTokenReady()) {
[email protected]c64b9142011-04-19 18:49:54358 return PK11_ReferenceSlot(tpm_slot_);
359 } else {
360 // If we were supposed to get the hardware token, but were
361 // unable to, return NULL rather than fall back to sofware.
362 return NULL;
363 }
364 }
365#endif
366 // If we weren't supposed to enable the TPM for NSS, then return
367 // the software slot.
[email protected]6a89ef22011-04-07 17:34:21368 if (software_slot_)
369 return PK11_ReferenceSlot(software_slot_);
[email protected]f615bda32010-11-21 01:04:56370 return PK11_GetInternalKeySlot();
371 }
372
373#if defined(USE_NSS)
[email protected]4b559b4d2011-04-14 17:37:14374 base::Lock* write_lock() {
[email protected]f615bda32010-11-21 01:04:56375 return &write_lock_;
376 }
377#endif // defined(USE_NSS)
378
[email protected]ed450f32011-03-16 01:26:49379 // This method is used to force NSS to be initialized without a DB.
380 // Call this method before NSSInitSingleton() is constructed.
381 static void ForceNoDBInit() {
382 force_nodb_init_ = true;
383 }
384
[email protected]f615bda32010-11-21 01:04:56385 private:
[email protected]4b559b4d2011-04-14 17:37:14386 friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
[email protected]f615bda32010-11-21 01:04:56387
[email protected]dcce6cf2010-04-29 17:50:06388 NSSInitSingleton()
[email protected]450b4ad72012-05-17 10:04:17389 : tpm_token_enabled_for_nss_(false),
390 chaps_module_(NULL),
[email protected]6a89ef22011-04-07 17:34:21391 software_slot_(NULL),
392 test_slot_(NULL),
393 tpm_slot_(NULL),
[email protected]dcce6cf2010-04-29 17:50:06394 root_(NULL),
[email protected]450b4ad72012-05-17 10:04:17395 chromeos_user_logged_in_(false) {
[email protected]68bf4482013-01-22 16:39:34396 base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]0f8f69c2013-11-12 02:56:31397
398 // It's safe to construct on any thread, since LazyInstance will prevent any
399 // other threads from accessing until the constructor is done.
400 thread_checker_.DetachFromThread();
401
[email protected]f3d445e2013-11-22 18:35:03402 DisableAESNIIfNeeded();
403
[email protected]f615bda32010-11-21 01:04:56404 EnsureNSPRInit();
[email protected]730fb132009-09-02 22:50:25405
[email protected]805acdc2013-08-07 22:57:00406 // We *must* have NSS >= 3.14.3.
[email protected]dc36c9c2010-01-20 20:45:00407 COMPILE_ASSERT(
[email protected]805acdc2013-08-07 22:57:00408 (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) ||
409 (NSS_VMAJOR == 3 && NSS_VMINOR > 14) ||
[email protected]dc36c9c2010-01-20 20:45:00410 (NSS_VMAJOR > 3),
411 nss_version_check_failed);
412 // Also check the run-time NSS version.
413 // NSS_VersionCheck is a >= check, not strict equality.
[email protected]805acdc2013-08-07 22:57:00414 if (!NSS_VersionCheck("3.14.3")) {
415 LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is "
416 "required. Please upgrade to the latest NSS, and if you "
[email protected]1b8082d2010-02-19 12:21:48417 "still get this error, contact your distribution "
418 "maintainer.";
419 }
[email protected]dc36c9c2010-01-20 20:45:00420
[email protected]897f5202009-09-08 17:40:27421 SECStatus status = SECFailure;
[email protected]ed450f32011-03-16 01:26:49422 bool nodb_init = force_nodb_init_;
423
[email protected]a8b58f42010-07-14 20:21:35424#if !defined(USE_NSS)
[email protected]abd4aba2010-01-27 19:36:22425 // Use the system certificate store, so initialize NSS without database.
[email protected]ed450f32011-03-16 01:26:49426 nodb_init = true;
[email protected]dcce6cf2010-04-29 17:50:06427#endif
[email protected]ed450f32011-03-16 01:26:49428
429 if (nodb_init) {
[email protected]897f5202009-09-08 17:40:27430 status = NSS_NoDB_Init(NULL);
431 if (status != SECSuccess) {
[email protected]007f5122012-11-21 16:00:21432 CrashOnNSSInitFailure();
[email protected]716fb112012-11-15 05:41:25433 return;
[email protected]897f5202009-09-08 17:40:27434 }
[email protected]a88f87e2012-08-14 19:46:28435#if defined(OS_IOS)
436 root_ = InitDefaultRootCerts();
437#endif // defined(OS_IOS)
[email protected]ed450f32011-03-16 01:26:49438 } else {
439#if defined(USE_NSS)
[email protected]9e275712013-02-10 19:20:14440 base::FilePath database_dir = GetInitialConfigDirectory();
[email protected]ed450f32011-03-16 01:26:49441 if (!database_dir.empty()) {
442 // This duplicates the work which should have been done in
443 // EarlySetupForNSSInit. However, this function is idempotent so
444 // there's no harm done.
445 UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
446
447 // Initialize with a persistent database (likely, ~/.pki/nssdb).
448 // Use "sql:" which can be shared by multiple processes safely.
449 std::string nss_config_dir =
[email protected]7d3cbc92013-03-18 22:33:04450 base::StringPrintf("sql:%s", database_dir.value().c_str());
[email protected]ed450f32011-03-16 01:26:49451#if defined(OS_CHROMEOS)
452 status = NSS_Init(nss_config_dir.c_str());
453#else
454 status = NSS_InitReadWrite(nss_config_dir.c_str());
455#endif
456 if (status != SECSuccess) {
457 LOG(ERROR) << "Error initializing NSS with a persistent "
458 "database (" << nss_config_dir
[email protected]6a89ef22011-04-07 17:34:21459 << "): " << GetNSSErrorMessage();
[email protected]ed450f32011-03-16 01:26:49460 }
461 }
462 if (status != SECSuccess) {
[email protected]6a89ef22011-04-07 17:34:21463 VLOG(1) << "Initializing NSS without a persistent database.";
[email protected]ed450f32011-03-16 01:26:49464 status = NSS_NoDB_Init(NULL);
465 if (status != SECSuccess) {
[email protected]007f5122012-11-21 16:00:21466 CrashOnNSSInitFailure();
[email protected]ed450f32011-03-16 01:26:49467 return;
468 }
469 }
470
471 PK11_SetPasswordFunc(PKCS11PasswordFunc);
472
473 // If we haven't initialized the password for the NSS databases,
474 // initialize an empty-string password so that we don't need to
475 // log in.
476 PK11SlotInfo* slot = PK11_GetInternalKeySlot();
477 if (slot) {
478 // PK11_InitPin may write to the keyDB, but no other thread can use NSS
479 // yet, so we don't need to lock.
480 if (PK11_NeedUserInit(slot))
481 PK11_InitPin(slot, NULL, NULL);
482 PK11_FreeSlot(slot);
483 }
484
485 root_ = InitDefaultRootCerts();
486#endif // defined(USE_NSS)
[email protected]6e7e8062009-04-13 17:35:09487 }
[email protected]fa2d3dc2012-11-20 07:58:44488
[email protected]fa2d3dc2012-11-20 07:58:44489 // Disable MD5 certificate signatures. (They are disabled by default in
490 // NSS 3.14.)
491 NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
492 NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
493 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
[email protected]68bf4482013-01-22 16:39:34494
495 // The UMA bit is conditionally set for this histogram in
496 // chrome/common/startup_metric_utils.cc .
497 HISTOGRAM_CUSTOM_TIMES("Startup.SlowStartupNSSInit",
498 base::TimeTicks::Now() - start_time,
499 base::TimeDelta::FromMilliseconds(10),
500 base::TimeDelta::FromHours(1),
501 50);
[email protected]bc1e07c72008-09-16 14:32:44502 }
503
[email protected]f615bda32010-11-21 01:04:56504 // NOTE(willchan): We don't actually execute this code since we leak NSS to
505 // prevent non-joinable threads from using NSS after it's already been shut
506 // down.
[email protected]bc1e07c72008-09-16 14:32:44507 ~NSSInitSingleton() {
[email protected]6a89ef22011-04-07 17:34:21508 if (tpm_slot_) {
509 PK11_FreeSlot(tpm_slot_);
510 tpm_slot_ = NULL;
511 }
512 if (software_slot_) {
513 SECMOD_CloseUserDB(software_slot_);
514 PK11_FreeSlot(software_slot_);
515 software_slot_ = NULL;
[email protected]dcce6cf2010-04-29 17:50:06516 }
[email protected]bb6390382010-08-12 19:49:40517 CloseTestNSSDB();
[email protected]ea224582008-12-07 20:25:46518 if (root_) {
519 SECMOD_UnloadUserModule(root_);
520 SECMOD_DestroyModule(root_);
521 root_ = NULL;
522 }
[email protected]84e47722011-11-17 05:12:02523 if (chaps_module_) {
524 SECMOD_UnloadUserModule(chaps_module_);
525 SECMOD_DestroyModule(chaps_module_);
526 chaps_module_ = NULL;
[email protected]6a89ef22011-04-07 17:34:21527 }
[email protected]ea224582008-12-07 20:25:46528
[email protected]c1444fe2008-09-17 09:42:51529 SECStatus status = NSS_Shutdown();
[email protected]f8adef52009-08-04 17:52:06530 if (status != SECSuccess) {
[email protected]b026e35d2010-10-19 02:31:03531 // We VLOG(1) because this failure is relatively harmless (leaking, but
532 // we're shutting down anyway).
[email protected]6a89ef22011-04-07 17:34:21533 VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609";
[email protected]f8adef52009-08-04 17:52:06534 }
[email protected]bc1e07c72008-09-16 14:32:44535 }
[email protected]c72f16a2009-03-19 16:02:31536
[email protected]a88f87e2012-08-14 19:46:28537#if defined(USE_NSS) || defined(OS_IOS)
[email protected]6a89ef22011-04-07 17:34:21538 // Load nss's built-in root certs.
539 SECMODModule* InitDefaultRootCerts() {
540 SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
541 if (root)
542 return root;
543
544 // Aw, snap. Can't find/load root cert shared library.
545 // This will make it hard to talk to anybody via https.
[email protected]3f3b9b12013-10-25 22:03:26546 // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed.
[email protected]6a89ef22011-04-07 17:34:21547 return NULL;
548 }
549
550 // Load the given module for this NSS session.
551 SECMODModule* LoadModule(const char* name,
552 const char* library_path,
553 const char* params) {
[email protected]7d3cbc92013-03-18 22:33:04554 std::string modparams = base::StringPrintf(
[email protected]6a89ef22011-04-07 17:34:21555 "name=\"%s\" library=\"%s\" %s",
556 name, library_path, params ? params : "");
557
558 // Shouldn't need to const_cast here, but SECMOD doesn't properly
559 // declare input string arguments as const. Bug
560 // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
561 // on NSS codebase to address this.
562 SECMODModule* module = SECMOD_LoadUserModule(
563 const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
564 if (!module) {
565 LOG(ERROR) << "Error loading " << name << " module into NSS: "
566 << GetNSSErrorMessage();
567 return NULL;
568 }
[email protected]3f3b9b12013-10-25 22:03:26569 if (!module->loaded) {
570 LOG(ERROR) << "After loading " << name << ", loaded==false: "
571 << GetNSSErrorMessage();
572 SECMOD_DestroyModule(module);
573 return NULL;
574 }
[email protected]6a89ef22011-04-07 17:34:21575 return module;
576 }
577#endif
578
[email protected]9e275712013-02-10 19:20:14579 static PK11SlotInfo* OpenUserDB(const base::FilePath& path,
[email protected]bb6390382010-08-12 19:49:40580 const char* description) {
581 const std::string modspec =
[email protected]7d3cbc92013-03-18 22:33:04582 base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
583 path.value().c_str(), description);
[email protected]bb6390382010-08-12 19:49:40584 PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
585 if (db_slot) {
586 if (PK11_NeedUserInit(db_slot))
587 PK11_InitPin(db_slot, NULL, NULL);
588 }
589 else {
590 LOG(ERROR) << "Error opening persistent database (" << modspec
[email protected]6a89ef22011-04-07 17:34:21591 << "): " << GetNSSErrorMessage();
[email protected]bb6390382010-08-12 19:49:40592 }
593 return db_slot;
594 }
595
[email protected]f3d445e2013-11-22 18:35:03596 static void DisableAESNIIfNeeded() {
597 if (NSS_VersionCheck("3.15") && !NSS_VersionCheck("3.15.4")) {
598 // Some versions of NSS have a bug that causes AVX instructions to be
599 // used without testing whether XSAVE is enabled by the operating system.
600 // In order to work around this, we disable AES-NI in NSS when we find
601 // that |has_avx()| is false (which includes the XSAVE test). See
602 // https://bugzilla.mozilla.org/show_bug.cgi?id=940794
603 base::CPU cpu;
604
605 if (cpu.has_avx_hardware() && !cpu.has_avx()) {
606 base::Environment::Create()->SetVar("NSS_DISABLE_HW_AES", "1");
607 }
608 }
609 }
610
[email protected]ed450f32011-03-16 01:26:49611 // If this is set to true NSS is forced to be initialized without a DB.
612 static bool force_nodb_init_;
613
[email protected]450b4ad72012-05-17 10:04:17614 bool tpm_token_enabled_for_nss_;
[email protected]84e47722011-11-17 05:12:02615 SECMODModule* chaps_module_;
[email protected]6a89ef22011-04-07 17:34:21616 PK11SlotInfo* software_slot_;
617 PK11SlotInfo* test_slot_;
618 PK11SlotInfo* tpm_slot_;
619 SECMODModule* root_;
[email protected]dcce6cf2010-04-29 17:50:06620 bool chromeos_user_logged_in_;
[email protected]69138472010-06-25 22:44:48621#if defined(USE_NSS)
[email protected]f615bda32010-11-21 01:04:56622 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
623 // is fixed, we will no longer need the lock.
[email protected]4b559b4d2011-04-14 17:37:14624 base::Lock write_lock_;
[email protected]a8b58f42010-07-14 20:21:35625#endif // defined(USE_NSS)
[email protected]0f8f69c2013-11-12 02:56:31626
627 base::ThreadChecker thread_checker_;
[email protected]bc1e07c72008-09-16 14:32:44628};
629
[email protected]ed450f32011-03-16 01:26:49630// static
631bool NSSInitSingleton::force_nodb_init_ = false;
632
[email protected]9fc44162012-01-23 22:56:41633base::LazyInstance<NSSInitSingleton>::Leaky
[email protected]6de0fd1d2011-11-15 13:31:49634 g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
[email protected]bc1e07c72008-09-16 14:32:44635} // namespace
636
[email protected]b17bc3662013-08-12 14:09:19637const char kTestTPMTokenName[] = "Test DB";
638
[email protected]ac3d5972011-01-13 20:33:45639#if defined(USE_NSS)
640void EarlySetupForNSSInit() {
[email protected]9e275712013-02-10 19:20:14641 base::FilePath database_dir = GetInitialConfigDirectory();
[email protected]ac3d5972011-01-13 20:33:45642 if (!database_dir.empty())
643 UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
644}
645#endif
646
[email protected]730fb132009-09-02 22:50:25647void EnsureNSPRInit() {
[email protected]f615bda32010-11-21 01:04:56648 g_nspr_singleton.Get();
[email protected]730fb132009-09-02 22:50:25649}
650
[email protected]2fdd99e82012-11-29 04:46:41651void InitNSSSafely() {
[email protected]5fe0c7692012-11-17 03:57:56652 // We might fork, but we haven't loaded any security modules.
[email protected]2fdd99e82012-11-29 04:46:41653 DisableNSSForkCheck();
[email protected]5fe0c7692012-11-17 03:57:56654 // If we're sandboxed, we shouldn't be able to open user security modules,
655 // but it's more correct to tell NSS to not even try.
656 // Loading user security modules would have security implications.
[email protected]2fdd99e82012-11-29 04:46:41657 ForceNSSNoDBInit();
[email protected]5fe0c7692012-11-17 03:57:56658 // Initialize NSS.
[email protected]2fdd99e82012-11-29 04:46:41659 EnsureNSSInit();
[email protected]5fe0c7692012-11-17 03:57:56660}
661
[email protected]bc1e07c72008-09-16 14:32:44662void EnsureNSSInit() {
[email protected]a8e4b5a82010-10-27 00:05:47663 // Initializing SSL causes us to do blocking IO.
664 // Temporarily allow it until we fix
665 // http://code.google.com/p/chromium/issues/detail?id=59847
[email protected]4b559b4d2011-04-14 17:37:14666 base::ThreadRestrictions::ScopedAllowIO allow_io;
[email protected]f615bda32010-11-21 01:04:56667 g_nss_singleton.Get();
[email protected]bc1e07c72008-09-16 14:32:44668}
669
[email protected]ed450f32011-03-16 01:26:49670void ForceNSSNoDBInit() {
671 NSSInitSingleton::ForceNoDBInit();
672}
673
674void DisableNSSForkCheck() {
[email protected]4b559b4d2011-04-14 17:37:14675 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]ed450f32011-03-16 01:26:49676 env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
677}
678
[email protected]f6a67b42011-03-17 23:49:21679void LoadNSSLibraries() {
680 // Some NSS libraries are linked dynamically so load them here.
681#if defined(USE_NSS)
682 // Try to search for multiple directories to load the libraries.
[email protected]9e275712013-02-10 19:20:14683 std::vector<base::FilePath> paths;
[email protected]f6a67b42011-03-17 23:49:21684
685 // Use relative path to Search PATH for the library files.
[email protected]9e275712013-02-10 19:20:14686 paths.push_back(base::FilePath());
[email protected]f6a67b42011-03-17 23:49:21687
[email protected]26661c22011-10-07 01:33:58688 // For Debian derivatives NSS libraries are located here.
[email protected]9e275712013-02-10 19:20:14689 paths.push_back(base::FilePath("/usr/lib/nss"));
[email protected]f6a67b42011-03-17 23:49:21690
[email protected]532983bb2013-08-01 02:46:46691 // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here.
[email protected]26661c22011-10-07 01:33:58692#if defined(ARCH_CPU_X86_64)
[email protected]9e275712013-02-10 19:20:14693 paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss"));
[email protected]26661c22011-10-07 01:33:58694#elif defined(ARCH_CPU_X86)
[email protected]9e275712013-02-10 19:20:14695 paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss"));
[email protected]26661c22011-10-07 01:33:58696#elif defined(ARCH_CPU_ARMEL)
[email protected]9e275712013-02-10 19:20:14697 paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss"));
[email protected]532983bb2013-08-01 02:46:46698#elif defined(ARCH_CPU_MIPSEL)
699 paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss"));
[email protected]26661c22011-10-07 01:33:58700#endif
701
[email protected]f6a67b42011-03-17 23:49:21702 // A list of library files to load.
703 std::vector<std::string> libs;
704 libs.push_back("libsoftokn3.so");
705 libs.push_back("libfreebl3.so");
706
707 // For each combination of library file and path, check for existence and
708 // then load.
709 size_t loaded = 0;
710 for (size_t i = 0; i < libs.size(); ++i) {
711 for (size_t j = 0; j < paths.size(); ++j) {
[email protected]9e275712013-02-10 19:20:14712 base::FilePath path = paths[j].Append(libs[i]);
[email protected]84479322011-04-18 22:06:22713 base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL);
[email protected]9f9b4f22011-04-08 21:57:46714 if (lib) {
715 ++loaded;
716 break;
[email protected]f6a67b42011-03-17 23:49:21717 }
718 }
719 }
720
721 if (loaded == libs.size()) {
722 VLOG(3) << "NSS libraries loaded.";
723 } else {
[email protected]26661c22011-10-07 01:33:58724 LOG(ERROR) << "Failed to load NSS libraries.";
[email protected]f6a67b42011-03-17 23:49:21725 }
726#endif
727}
728
[email protected]f61c3972010-12-23 09:54:15729bool CheckNSSVersion(const char* version) {
730 return !!NSS_VersionCheck(version);
731}
732
[email protected]69138472010-06-25 22:44:48733#if defined(USE_NSS)
[email protected]7025e932012-10-18 07:02:54734ScopedTestNSSDB::ScopedTestNSSDB()
735 : is_open_(g_nss_singleton.Get().OpenTestNSSDB()) {
736}
737
738ScopedTestNSSDB::~ScopedTestNSSDB() {
[email protected]166fac0d2013-06-29 21:18:16739 // Don't close when NSS is < 3.15.1, because it would require an additional
740 // sleep for 1 second after closing the database, due to
741 // http://bugzil.la/875601.
742 if (NSS_VersionCheck("3.15.1")) {
743 g_nss_singleton.Get().CloseTestNSSDB();
744 }
[email protected]bb6390382010-08-12 19:49:40745}
746
[email protected]4b559b4d2011-04-14 17:37:14747base::Lock* GetNSSWriteLock() {
[email protected]f615bda32010-11-21 01:04:56748 return g_nss_singleton.Get().write_lock();
[email protected]69138472010-06-25 22:44:48749}
750
751AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
752 // May be NULL if the lock is not needed in our version of NSS.
753 if (lock_)
754 lock_->Acquire();
755}
756
757AutoNSSWriteLock::~AutoNSSWriteLock() {
758 if (lock_) {
759 lock_->AssertAcquired();
760 lock_->Release();
761 }
762}
[email protected]dd24ffc2011-06-08 19:46:42763
764AutoSECMODListReadLock::AutoSECMODListReadLock()
765 : lock_(SECMOD_GetDefaultModuleListLock()) {
766 SECMOD_GetReadLock(lock_);
767 }
768
769AutoSECMODListReadLock::~AutoSECMODListReadLock() {
770 SECMOD_ReleaseReadLock(lock_);
771}
772
[email protected]69138472010-06-25 22:44:48773#endif // defined(USE_NSS)
774
[email protected]dcce6cf2010-04-29 17:50:06775#if defined(OS_CHROMEOS)
776void OpenPersistentNSSDB() {
[email protected]f615bda32010-11-21 01:04:56777 g_nss_singleton.Get().OpenPersistentNSSDB();
[email protected]dcce6cf2010-04-29 17:50:06778}
[email protected]6a89ef22011-04-07 17:34:21779
[email protected]450b4ad72012-05-17 10:04:17780void EnableTPMTokenForNSS() {
781 g_nss_singleton.Get().EnableTPMTokenForNSS();
[email protected]6a89ef22011-04-07 17:34:21782}
783
[email protected]c64b9142011-04-19 18:49:54784bool IsTPMTokenReady() {
785 return g_nss_singleton.Get().IsTPMTokenReady();
[email protected]74beead2011-04-12 20:40:12786}
[email protected]c64b9142011-04-19 18:49:54787
[email protected]bee9b542013-11-25 21:45:04788bool InitializeTPMToken(int token_slot_id) {
789 return g_nss_singleton.Get().InitializeTPMToken(token_slot_id);
[email protected]c175cdb2011-06-28 20:41:55790}
[email protected]6a89ef22011-04-07 17:34:21791#endif // defined(OS_CHROMEOS)
[email protected]dcce6cf2010-04-29 17:50:06792
[email protected]4b559b4d2011-04-14 17:37:14793base::Time PRTimeToBaseTime(PRTime prtime) {
[email protected]ca929ed32011-12-15 20:37:28794 return base::Time::FromInternalValue(
795 prtime + base::Time::UnixEpoch().ToInternalValue());
796}
[email protected]1b1a264a2010-01-14 22:36:35797
[email protected]ca929ed32011-12-15 20:37:28798PRTime BaseTimeToPRTime(base::Time time) {
799 return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue();
[email protected]1b1a264a2010-01-14 22:36:35800}
801
[email protected]6a89ef22011-04-07 17:34:21802PK11SlotInfo* GetPublicNSSKeySlot() {
803 return g_nss_singleton.Get().GetPublicNSSKeySlot();
804}
805
806PK11SlotInfo* GetPrivateNSSKeySlot() {
807 return g_nss_singleton.Get().GetPrivateNSSKeySlot();
[email protected]dcce6cf2010-04-29 17:50:06808}
809
[email protected]4b559b4d2011-04-14 17:37:14810} // namespace crypto