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

Unified Diff: crypto.cc

Issue 2645008: Update on feedback, update dbus API, add unit tests. TEST=manual,unit,BVT BUG=3628 323 (Closed) Base URL: ssh://git@chromiumos-git/cryptohome.git
Patch Set: Address second round of feedback. Created 10 years, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « crypto.h ('k') | crypto_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: crypto.cc
diff --git a/crypto.cc b/crypto.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4ada2a015e929ba516be65abbb2619a4d83a8b85
--- /dev/null
+++ b/crypto.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Contains the implementation of class Crypto
+
+#include "crypto.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <base/file_util.h>
+#include <base/logging.h>
+#include <chromeos/utility.h>
+
+#include "cryptohome_common.h"
+#include "platform.h"
+#include "username_passkey.h"
+
+// Included last because it has conflicting defines
+extern "C" {
+#include <ecryptfs.h>
+}
+
+using std::string;
+
+namespace cryptohome {
+
+const std::string kDefaultEntropySource = "/dev/urandom";
+const std::string kOpenSSLMagic = "Salted__";
+
+Crypto::Crypto()
+ : entropy_source_(kDefaultEntropySource) {
+}
+
+Crypto::~Crypto() {
+}
+
+void Crypto::GetSecureRandom(unsigned char *rand, int length) const {
+ // TODO(fes): Get assistance from the TPM when it is available
+ // Seed the OpenSSL random number generator until it is happy
+ while (!RAND_status()) {
+ char buffer[256];
+ file_util::ReadFile(FilePath(entropy_source_), buffer, sizeof(buffer));
+ RAND_add(buffer, sizeof(buffer), sizeof(buffer));
+ }
+
+ // Have OpenSSL generate the random bytes
+ RAND_bytes(rand, length);
+}
+
+bool Crypto::UnwrapVaultKeyset(const chromeos::Blob& wrapped_keyset,
+ const chromeos::Blob& vault_wrapper,
+ VaultKeyset* vault_keyset) const {
+ unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
+
+ if (wrapped_keyset.size() < header_size) {
+ LOG(ERROR) << "Master key file too short";
+ return false;
+ }
+
+ // Grab the salt used in converting the passkey to a key (OpenSSL
+ // passkey-encrypted files have the format:
+ // Salted__<8-byte-salt><ciphertext>)
+ unsigned char salt[PKCS5_SALT_LEN];
+ memcpy(salt, &wrapped_keyset[kOpenSSLMagic.length()], PKCS5_SALT_LEN);
+
+ unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
+ unsigned char iv[EVP_MAX_IV_LENGTH];
+
+ // Convert the passkey to a key encryption key
+ if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, &vault_wrapper[0],
+ vault_wrapper.size(), 1, wrapper_key, iv)) {
+ LOG(ERROR) << "Failure converting bytes to key";
+ return false;
+ }
+
+ int cipher_text_size = wrapped_keyset.size() - header_size;
+ SecureBlob plain_text(cipher_text_size);
+
+ int final_size = 0;
+ int decrypt_size = plain_text.size();
+
+ // Do the actual decryption
+ EVP_CIPHER_CTX d_ctx;
+ EVP_CIPHER_CTX_init(&d_ctx);
+ EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
+ if (!EVP_DecryptUpdate(&d_ctx, &plain_text[0], &decrypt_size,
+ &wrapped_keyset[header_size],
+ cipher_text_size)) {
+ LOG(ERROR) << "DecryptUpdate failed";
+ return false;
+ }
+ if (!EVP_DecryptFinal_ex(&d_ctx, &plain_text[decrypt_size], &final_size)) {
+ unsigned long err = ERR_get_error();
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+
+ LOG(ERROR) << "DecryptFinal Error: " << err
+ << ": " << ERR_lib_error_string(err)
+ << ", " << ERR_func_error_string(err)
+ << ", " << ERR_reason_error_string(err);
+
+ return false;
+ }
+ final_size += decrypt_size;
+
+ plain_text.resize(final_size);
+
+ if (plain_text.size() != VaultKeyset::SerializedSize()) {
+ LOG(ERROR) << "Plain text was not the correct size: " << plain_text.size()
+ << ", expected: " << VaultKeyset::SerializedSize();
+ return false;
+ }
+
+ (*vault_keyset).AssignBuffer(plain_text);
+ return true;
+}
+
+bool Crypto::WrapVaultKeyset(const VaultKeyset& vault_keyset,
+ const SecureBlob& vault_wrapper,
+ const SecureBlob& vault_wrapper_salt,
+ SecureBlob* wrapped_keyset) const {
+ unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
+ unsigned char iv[EVP_MAX_IV_LENGTH];
+
+ if (vault_wrapper_salt.size() < PKCS5_SALT_LEN) {
+ LOG(ERROR) << "Vault wrapper salt was not the correct length";
+ return false;
+ }
+
+ // Convert the passkey wrapper to a key encryption key
+ if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), &vault_wrapper_salt[0],
+ &vault_wrapper[0], vault_wrapper.size(), 1,
+ wrapper_key, iv)) {
+ LOG(ERROR) << "Failure converting bytes to key";
+ return false;
+ }
+
+ SecureBlob keyset_blob;
+ if (!vault_keyset.ToBuffer(&keyset_blob)) {
+ LOG(ERROR) << "Failure serializing keyset to buffer";
+ return false;
+ }
+
+ // Store the salt and encrypt the master key
+ unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
+ SecureBlob cipher_text(header_size
+ + keyset_blob.size()
+ + EVP_CIPHER_block_size(EVP_aes_256_ecb()));
+ memcpy(&cipher_text[0], kOpenSSLMagic.c_str(), kOpenSSLMagic.length());
+ memcpy(&cipher_text[kOpenSSLMagic.length()], vault_wrapper_salt.const_data(),
+ PKCS5_SALT_LEN);
+
+ int current_size = header_size;
+ int encrypt_size = 0;
+ EVP_CIPHER_CTX e_ctx;
+ EVP_CIPHER_CTX_init(&e_ctx);
+
+ // Encrypt the keyset
+ EVP_EncryptInit_ex(&e_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
+ if (!EVP_EncryptUpdate(&e_ctx, &cipher_text[current_size], &encrypt_size,
+ &keyset_blob[0],
+ keyset_blob.size())) {
+ LOG(ERROR) << "EncryptUpdate failed";
+ chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
+ return false;
+ }
+ current_size += encrypt_size;
+ encrypt_size = 0;
+
+ // Finish the encryption
+ if (!EVP_EncryptFinal_ex(&e_ctx, &cipher_text[current_size], &encrypt_size)) {
+ LOG(ERROR) << "EncryptFinal failed";
+ chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
+ return false;
+ }
+ current_size += encrypt_size;
+ cipher_text.resize(current_size);
+
+ chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
+
+ wrapped_keyset->swap(cipher_text);
+ return true;
+}
+
+void Crypto::PasskeyToWrapper(const chromeos::Blob& passkey,
+ const chromeos::Blob& salt, int iters,
+ SecureBlob* wrapper) const {
+ // TODO(fes): Update this when TPM support is available, or use a memory-
+ // bound strengthening algorithm.
+ int update_length = passkey.size();
+ int holder_size = SHA_DIGEST_LENGTH;
+ if (update_length > SHA_DIGEST_LENGTH) {
+ holder_size = update_length;
+ }
+ SecureBlob holder(holder_size);
+ memcpy(&holder[0], &passkey[0], update_length);
+
+ // Repeatedly hash the user passkey and salt to generate the wrapper
+ for (int i = 0; i < iters; ++i) {
+ SHA_CTX ctx;
+ unsigned char md_value[SHA_DIGEST_LENGTH];
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, &salt[0], salt.size());
+ SHA1_Update(&ctx, &holder[0], update_length);
+ SHA1_Final(md_value, &ctx);
+
+ memcpy(&holder[0], md_value, SHA_DIGEST_LENGTH);
+ update_length = SHA_DIGEST_LENGTH;
+ }
+
+ holder.resize(update_length);
+ SecureBlob local_wrapper(update_length * 2);
+ AsciiEncodeToBuffer(holder, static_cast<char*>(local_wrapper.data()),
+ local_wrapper.size());
+ wrapper->swap(local_wrapper);
+}
+
+bool Crypto::GetOrCreateSalt(const FilePath& path, int length, bool force,
+ SecureBlob* salt) const {
+ SecureBlob local_salt;
+ if (force || !file_util::PathExists(path)) {
+ // If this salt doesn't exist, automatically create it
+ local_salt.resize(length);
+ GetSecureRandom(static_cast<unsigned char*>(local_salt.data()),
+ local_salt.size());
+ int data_written = file_util::WriteFile(path,
+ static_cast<const char*>(local_salt.const_data()),
+ length);
+ if (data_written != length) {
+ LOG(ERROR) << "Could not write user salt";
+ return false;
+ }
+ } else {
+ // Otherwise just load the contents of the salt
+ int64 file_size;
+ if (!file_util::GetFileSize(path, &file_size)) {
+ LOG(ERROR) << "Could not get size of " << path.value();
+ return false;
+ }
+ if (file_size > INT_MAX) {
+ LOG(ERROR) << "File " << path.value() << " is too large: " << file_size;
+ return false;
+ }
+ local_salt.resize(file_size);
+ int data_read = file_util::ReadFile(path,
+ static_cast<char*>(local_salt.data()),
+ local_salt.size());
+ if (data_read != file_size) {
+ LOG(ERROR) << "Could not read entire file " << file_size;
+ return false;
+ }
+ }
+ salt->swap(local_salt);
+ return true;
+}
+
+bool Crypto::AddKeyset(const VaultKeyset& vault_keyset,
+ std::string* key_signature,
+ std::string* fnek_signature) const {
+ // Add the FEK
+ *key_signature = chromeos::AsciiEncode(vault_keyset.FEK_SIG());
+ if (!PushVaultKey(vault_keyset.FEK(), *key_signature,
+ vault_keyset.FEK_SALT())) {
+ LOG(ERROR) << "Couldn't add ecryptfs key to keyring";
+ return false;
+ }
+
+ // Add the FNEK
+ *fnek_signature = chromeos::AsciiEncode(vault_keyset.FNEK_SIG());
+ if (!PushVaultKey(vault_keyset.FNEK(), *fnek_signature,
+ vault_keyset.FNEK_SALT())) {
+ LOG(ERROR) << "Couldn't add ecryptfs fnek key to keyring";
+ return false;
+ }
+
+ return true;
+}
+
+void Crypto::ClearKeyset() const {
+ Platform::ClearUserKeyring();
+}
+
+bool Crypto::PushVaultKey(const SecureBlob& key, const std::string& key_sig,
+ const SecureBlob& salt) const {
+ DCHECK(key.size() == ECRYPTFS_MAX_KEY_BYTES);
+ DCHECK(key_sig.length() == (ECRYPTFS_SIG_SIZE * 2));
+ DCHECK(salt.size() == ECRYPTFS_SALT_SIZE);
+
+ struct ecryptfs_auth_tok auth_token;
+
+ generate_payload(&auth_token, const_cast<char*>(key_sig.c_str()),
+ const_cast<char*>(reinterpret_cast<const char*>(&salt[0])),
+ const_cast<char*>(reinterpret_cast<const char*>(&key[0])));
+
+ if (ecryptfs_add_auth_tok_to_keyring(&auth_token,
+ const_cast<char*>(key_sig.c_str())) < 0) {
+ LOG(ERROR) << "PushVaultKey failed";
+ }
+
+ return true;
+}
+
+void Crypto::PasswordToPasskey(const char *password,
+ const chromeos::Blob& salt,
+ SecureBlob* passkey) {
+ CHECK(password);
+
+ std::string ascii_salt = chromeos::AsciiEncode(salt);
+ // Convert a raw password to a password hash
+ SHA256_CTX sha_ctx;
+ SecureBlob md_value(SHA256_DIGEST_LENGTH);
+
+ SHA256_Init(&sha_ctx);
+ SHA256_Update(&sha_ctx,
+ reinterpret_cast<const unsigned char*>(ascii_salt.data()),
+ static_cast<unsigned int>(ascii_salt.length()));
+ SHA256_Update(&sha_ctx, password, strlen(password));
+ SHA256_Final(static_cast<unsigned char*>(md_value.data()), &sha_ctx);
+
+ md_value.resize(SHA256_DIGEST_LENGTH / 2);
+ SecureBlob local_passkey(SHA256_DIGEST_LENGTH);
+ AsciiEncodeToBuffer(md_value, static_cast<char*>(local_passkey.data()),
+ local_passkey.size());
+ passkey->swap(local_passkey);
+}
+
+void Crypto::AsciiEncodeToBuffer(const chromeos::Blob& blob, char* buffer,
+ int buffer_length) {
+ const char hex_chars[] = "0123456789abcdef";
+ int i = 0;
+ for (chromeos::Blob::const_iterator it = blob.begin();
+ it < blob.end() && (i + 1) < buffer_length; ++it) {
+ buffer[i++] = hex_chars[((*it) >> 4) & 0x0f];
+ buffer[i++] = hex_chars[(*it) & 0x0f];
+ }
+ if (i < buffer_length) {
+ buffer[i] = '\0';
+ }
+}
+
+} // namespace cryptohome
« no previous file with comments | « crypto.h ('k') | crypto_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698