0

Changed OAuth token+secret encryption to use supplemental user key from NSS DB.

BUG=chromium-os:18633
TEST=none
Review URL: http://codereview.chromium.org/7756025

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99912 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
zelidrag@chromium.org
2011-09-07 04:01:03 +00:00
parent 3f7af10d3e
commit 6e3d9a9cf7
14 changed files with 176 additions and 28 deletions

@ -176,6 +176,10 @@ class CertLibraryImpl
return server_ca_certs_;
}
virtual crypto::SymmetricKey* GetSupplementalUserKey() const {
return crypto::GetSupplementalUserKey();
}
// net::CertDatabase::Observer implementation. Observer added on UI thread.
virtual void OnCertTrustChanged(const net::X509Certificate* cert) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

@ -12,6 +12,10 @@
#include "net/base/cert_database.h"
#include "net/base/x509_certificate.h"
namespace crypto {
class SymmetricKey;
}
namespace chromeos {
class CertLibrary {
@ -96,6 +100,9 @@ class CertLibrary {
// Returns the current list of server CA certificates.
virtual const CertList& GetCACertificates() const = 0;
// Returns the supplemental user key.
virtual crypto::SymmetricKey* GetSupplementalUserKey() const = 0;
};
} // namespace chromeos

@ -100,6 +100,9 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> {
// OAuth token encryption helpers.
virtual std::string EncryptToken(const std::string& token) = 0;
virtual std::string DecryptToken(const std::string& encrypted_token) = 0;
// TODO(zelidrag): Remove legacy encryption support in R16.
virtual std::string DecryptLegacyToken(
const std::string& encrypted_token) = 0;
// Profile (usually off the record ) that was used to perform the last
// authentication process.

@ -422,6 +422,11 @@ std::string GoogleAuthenticator::DecryptToken(const std::string& unused) {
return std::string();
}
std::string GoogleAuthenticator::DecryptLegacyToken(const std::string& unused) {
NOTIMPLEMENTED();
return std::string();
}
void GoogleAuthenticator::LoadSystemSalt() {
if (!system_salt_.empty())

@ -120,6 +120,8 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer {
const std::string& oauth1_secret) OVERRIDE;
virtual std::string EncryptToken(const std::string& token) OVERRIDE;
virtual std::string DecryptToken(const std::string& encrypted_token) OVERRIDE;
virtual std::string DecryptLegacyToken(
const std::string& encrypted_token) OVERRIDE;
// Callbacks from GaiaAuthFetcher
virtual void OnClientLoginFailure(

@ -935,8 +935,19 @@ bool LoginUtilsImpl::ReadOAuth1AccessToken(Profile* user_profile,
std::string decoded_token = authenticator_->DecryptToken(encoded_token);
std::string decoded_secret = authenticator_->DecryptToken(encoded_secret);
if (!decoded_token.length() || !decoded_secret.length())
return false;
if (!decoded_token.length() || !decoded_secret.length()) {
// TODO(zelidrag): Remove legacy encryption support in R16.
// Check if tokens were encoded with the legacy encryption instead.
decoded_token = authenticator_->DecryptLegacyToken(encoded_token);
decoded_secret = authenticator_->DecryptLegacyToken(encoded_secret);
if (!decoded_token.length() || !decoded_secret.length())
return false;
pref_service->SetString(prefs::kOAuth1Token,
authenticator_->EncryptToken(decoded_token));
pref_service->SetString(prefs::kOAuth1Secret,
authenticator_->EncryptToken(decoded_secret));
}
*token = decoded_token;
*secret = decoded_secret;

@ -69,6 +69,11 @@ std::string MockAuthenticator::DecryptToken(
return std::string();
}
std::string MockAuthenticator::DecryptLegacyToken(
const std::string& encrypted_token) {
return std::string();
}
////////////////////////////////////////////////////////////////////////////////
// MockLoginUtils

@ -71,6 +71,8 @@ class MockAuthenticator : public Authenticator {
virtual std::string DecryptToken(const std::string& encrypted_token);
virtual std::string DecryptLegacyToken(const std::string& encrypted_token);
virtual void VerifyOAuth1AccessToken(const std::string& oauth1_access_token,
const std::string& oauth1_secret) {}

@ -16,6 +16,7 @@
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/chromeos/cros/cert_library.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#include "chrome/browser/chromeos/login/auth_response_handler.h"
#include "chrome/browser/chromeos/login/authentication_notification_details.h"
@ -46,6 +47,37 @@ using file_util::PathExists;
using file_util::ReadFile;
using file_util::ReadFileToString;
namespace {
const int kPassHashLen = 32;
const size_t kKeySize = 16;
// Decrypts (AES) hex encoded encrypted token given |key| and |salt|.
std::string DecryptTokenWithKey(
crypto::SymmetricKey* key,
const std::string& salt,
const std::string& encrypted_token_hex) {
std::vector<uint8> encrypted_token_bytes;
if (!base::HexStringToBytes(encrypted_token_hex, &encrypted_token_bytes))
return std::string();
std::string encrypted_token(
reinterpret_cast<char*>(encrypted_token_bytes.data()),
encrypted_token_bytes.size());
crypto::Encryptor encryptor;
if (!encryptor.Init(key, crypto::Encryptor::CTR, std::string()))
return std::string();
std::string nonce = salt.substr(0, kKeySize);
std::string token;
CHECK(encryptor.SetCounter(nonce));
if (!encryptor.Decrypt(encrypted_token, &token))
return std::string();
return token;
}
} // namespace
namespace chromeos {
// static
@ -56,9 +88,6 @@ const int ParallelAuthenticator::kClientLoginTimeoutMs = 10000;
// static
const int ParallelAuthenticator::kLocalaccountRetryIntervalMs = 20;
const int kPassHashLen = 32;
const size_t kKeySize = 16;
ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer)
: Authenticator(consumer),
already_reported_success_(false),
@ -673,6 +702,15 @@ void ParallelAuthenticator::LoadSystemSalt() {
CHECK_EQ(system_salt_.size() % 2, 0U);
}
bool ParallelAuthenticator::LoadSupplementalUserKey() {
if (!supplemental_user_key_.get()) {
supplemental_user_key_.reset(
CrosLibrary::Get()->GetCertLibrary()->GetSupplementalUserKey());
}
return supplemental_user_key_.get() != NULL;
}
void ParallelAuthenticator::LoadLocalaccount(const std::string& filename) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
{
@ -704,13 +742,11 @@ void ParallelAuthenticator::SetLocalaccount(const std::string& new_name) {
}
std::string ParallelAuthenticator::EncryptToken(const std::string& token) {
// TODO(zelidrag): Replace salt with
scoped_ptr<crypto::SymmetricKey> key(
crypto::SymmetricKey::DeriveKeyFromPassword(
crypto::SymmetricKey::AES, UserSupplementalKeyAsAscii(),
SaltAsAscii(), 1000, 256));
if (!LoadSupplementalUserKey())
return std::string();
crypto::Encryptor encryptor;
if (!encryptor.Init(key.get(), crypto::Encryptor::CTR, std::string()))
if (!encryptor.Init(supplemental_user_key_.get(), crypto::Encryptor::CTR,
std::string()))
return std::string();
std::string nonce = SaltAsAscii().substr(0, kKeySize);
@ -723,33 +759,24 @@ std::string ParallelAuthenticator::EncryptToken(const std::string& token) {
reinterpret_cast<const void*>(encoded_token.data()),
encoded_token.size()));
}
std::string ParallelAuthenticator::DecryptToken(
const std::string& encrypted_token_hex) {
std::vector<uint8> encrypted_token_bytes;
if (!base::HexStringToBytes(encrypted_token_hex, &encrypted_token_bytes))
if (!LoadSupplementalUserKey())
return std::string();
return DecryptTokenWithKey(supplemental_user_key_.get(),
SaltAsAscii(),
encrypted_token_hex);
}
std::string encrypted_token(
reinterpret_cast<char*>(encrypted_token_bytes.data()),
encrypted_token_bytes.size());
std::string ParallelAuthenticator::DecryptLegacyToken(
const std::string& encrypted_token_hex) {
scoped_ptr<crypto::SymmetricKey> key(
crypto::SymmetricKey::DeriveKeyFromPassword(
crypto::SymmetricKey::AES, UserSupplementalKeyAsAscii(),
SaltAsAscii(), 1000, 256));
crypto::Encryptor encryptor;
if (!encryptor.Init(key.get(), crypto::Encryptor::CTR, std::string()))
return std::string();
std::string nonce = SaltAsAscii().substr(0, kKeySize);
std::string token;
CHECK(encryptor.SetCounter(nonce));
if (!encryptor.Decrypt(encrypted_token, &token))
return std::string();
return token;
return DecryptTokenWithKey(key.get(), SaltAsAscii(), encrypted_token_hex);
}
std::string ParallelAuthenticator::HashPassword(const std::string& password) {
// Get salt, ascii encode, update sha with that, then update with ascii
// of password, then end.

@ -32,6 +32,10 @@ namespace base {
class Lock;
}
namespace crypto {
class SymmetricKey;
}
namespace chromeos {
class LoginStatusConsumer;
@ -149,6 +153,8 @@ class ParallelAuthenticator : public Authenticator,
const std::string& oauth1_secret) OVERRIDE;
virtual std::string EncryptToken(const std::string& token) OVERRIDE;
virtual std::string DecryptToken(const std::string& encrypted_token) OVERRIDE;
virtual std::string DecryptLegacyToken(
const std::string& encrypted_token) OVERRIDE;
// AuthAttemptStateResolver overrides.
// Attempts to make a decision and call back |consumer_| based on
@ -220,6 +226,9 @@ class ParallelAuthenticator : public Authenticator,
// If we don't have the system salt yet, loads it from the CryptohomeLibrary.
void LoadSystemSalt();
// If we don't have supplemental_user_key_ yet, loads it from the NSS DB.
// Returns false if the key can not be loaded/created.
bool LoadSupplementalUserKey();
// If we haven't already, looks in a file called |filename| next to
// the browser executable for a "localaccount" name, and retrieves it
@ -275,6 +284,7 @@ class ParallelAuthenticator : public Authenticator,
std::string ascii_hash_;
chromeos::CryptohomeBlob system_salt_;
scoped_ptr<crypto::SymmetricKey> supplemental_user_key_;
// When the user has changed her password, but gives us the old one, we will
// be able to mount her cryptohome, but online authentication will fail.

@ -31,6 +31,10 @@
#include "base/threading/thread_restrictions.h"
#include "crypto/scoped_nss_types.h"
#if defined(OS_CHROMEOS)
#include "crypto/symmetric_key.h"
#endif
// USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not
// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
// use NSS for crypto or certificate verification, and we don't use the NSS
@ -83,6 +87,15 @@ FilePath GetDefaultConfigDirectory() {
return dir;
}
#if defined(OS_CHROMEOS)
// Supplemental user key id.
unsigned char kSupplementalUserKeyId[] = {
0xCC, 0x13, 0x19, 0xDE, 0x75, 0x5E, 0xFE, 0xFA,
0x5E, 0x71, 0xD4, 0xA6, 0xFB, 0x00, 0x00, 0xCC
};
#endif // defined(OS_CHROMEOS)
// On non-chromeos platforms, return the default config directory.
// On chromeos, return a read-only directory with fake root CA certs for testing
// (which will not exist on non-testing images). These root CA certs are used
@ -288,6 +301,40 @@ class NSSInitSingleton {
return FindSlotWithTokenName(token_name);
}
SymmetricKey* GetSupplementalUserKey() {
DCHECK(chromeos_user_logged_in_);
PK11SlotInfo* slot = NULL;
PK11SymKey* key = NULL;
SECItem keyID;
CK_MECHANISM_TYPE type = CKM_AES_ECB;
slot = GetPublicNSSKeySlot();
if (!slot)
goto done;
if (PK11_Authenticate(slot, PR_TRUE, NULL) != SECSuccess)
goto done;
keyID.type = siBuffer;
keyID.data = kSupplementalUserKeyId;
keyID.len = static_cast<int>(sizeof(kSupplementalUserKeyId));
// Find/generate AES key.
key = PK11_FindFixedKey(slot, type, &keyID, NULL);
if (!key) {
const int kKeySizeInBytes = 32;
key = PK11_TokenKeyGen(slot, type, NULL,
kKeySizeInBytes,
&keyID, PR_TRUE, NULL);
}
done:
if (slot)
PK11_FreeSlot(slot);
return key ? SymmetricKey::CreateFromKey(key) : NULL;
}
#endif // defined(OS_CHROMEOS)
@ -702,6 +749,9 @@ bool EnsureTPMTokenReady() {
return g_nss_singleton.Get().EnsureTPMTokenReady();
}
SymmetricKey* GetSupplementalUserKey() {
return g_nss_singleton.Get().GetSupplementalUserKey();
}
#endif // defined(OS_CHROMEOS)
// TODO(port): Implement this more simply. We can convert by subtracting an

@ -24,6 +24,8 @@ class Time;
// initialization functions.
namespace crypto {
class SymmetricKey;
#if defined(USE_NSS)
// EarlySetupForNSSInit performs lightweight setup which must occur before the
// process goes multithreaded. This does not initialise NSS. For test, see
@ -133,6 +135,14 @@ CRYPTO_EXPORT bool IsTPMTokenReady();
// Same as IsTPMTokenReady() except this attempts to initialize the token
// if necessary.
CRYPTO_EXPORT bool EnsureTPMTokenReady();
// Gets supplemental user key. Creates one in NSS database if it does not exist.
// The supplemental user key is used for AES encryption of user data that is
// stored and protected by cryptohome. This additional layer of encryption of
// provided to ensure that sensitive data wouldn't be exposed in plain text in
// case when an attacker would somehow gain access to all content within
// cryptohome.
CRYPTO_EXPORT SymmetricKey* GetSupplementalUserKey();
#endif
// Convert a NSS PRTime value into a base::Time object.

@ -71,6 +71,11 @@ class CRYPTO_EXPORT SymmetricKey {
// carefully.
bool GetRawKey(std::string* raw_key);
#if defined(OS_CHROMEOS)
// Creates symmetric key from NSS key. Takes over the ownership of |key|.
static SymmetricKey* CreateFromKey(PK11SymKey* key);
#endif
private:
#if defined(USE_OPENSSL)
SymmetricKey() {}

@ -120,6 +120,13 @@ bool SymmetricKey::GetRawKey(std::string* raw_key) {
return true;
}
#if defined(OS_CHROMEOS)
// static
SymmetricKey* SymmetricKey::CreateFromKey(PK11SymKey* key) {
return new SymmetricKey(key);
}
#endif
SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) {
DCHECK(key);
}