| // Copyright (c) 2012 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. |
| |
| #include "cryptohome/crypto/scrypt.h" |
| |
| #include <limits> |
| #include <utility> |
| #include <vector> |
| |
| #include <malloc.h> |
| #include <openssl/err.h> |
| #include <openssl/kdf.h> |
| #include <openssl/sha.h> |
| #include <unistd.h> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/notreached.h> |
| #include <base/numerics/safe_conversions.h> |
| #include <base/stl_util.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/libcrypto-compat.h> |
| #include <crypto/scoped_openssl_types.h> |
| |
| #include "cryptohome/crypto/aes.h" |
| #include "cryptohome/crypto/secure_blob_util.h" |
| #include "cryptohome/libscrypt_compat.h" |
| #include "cryptohome/platform.h" |
| |
| using brillo::SecureBlob; |
| |
| namespace cryptohome { |
| |
| namespace { |
| |
| // Global override-able for testing. |
| ScryptParameters gScryptParams = kDefaultScryptParams; |
| |
| } // namespace |
| |
| // The current number of hash rounds we use. Large enough to be a measurable |
| // amount of time, but not add too much overhead to login (around 10ms). |
| const unsigned int kDefaultPasswordRounds = 1337; |
| |
| // The number of hash rounds we originally used when converting a password to a |
| // key. This is used when converting older cryptohome vault keysets. |
| const unsigned int kDefaultLegacyPasswordRounds = 1; |
| |
| // The maximum number of times to try decryption with the TPM. |
| constexpr int kTpmDecryptMaxRetries = 2; |
| |
| // The size in bytes of password blob to be generated by Scrypt. Should be the |
| // same size as the modulus of cryptohome key, since we need to be able to |
| // decrypt it. |
| constexpr unsigned int kDefaultPassBlobSize = 256; |
| |
| // Scrypt creates a header in the cipher text that we need to account for in |
| // buffer sizing. This accounts for both the header and the HMAC. |
| constexpr unsigned int kScryptMetadataSize = 128; |
| |
| // An upper bound on the amount of memory that we allow Scrypt to use when |
| // performing key strengthening (32MB). A large size is okay since we only use |
| // Scrypt during the login process, before the user is logged in. This memory |
| // is managed (and freed) by the scrypt library. |
| const unsigned int kScryptMaxMem = 32 * 1024 * 1024; |
| |
| // An upper bound on the amount of time we allow Scrypt to use when performing |
| // key strenthening (1/3s) for encryption. |
| constexpr double kScryptMaxEncryptTime = 0.333; |
| |
| // These are the params to use for production code. |
| constexpr ScryptParameters kDefaultScryptParams; |
| |
| // scrypt, with the default params, is too slow for unit testing so here are |
| // some fast parameters only for test code. |
| constexpr ScryptParameters kTestScryptParams = {1024, 8, 1}; |
| |
| bool DeriveSecretsScrypt(const brillo::SecureBlob& passkey, |
| const brillo::SecureBlob& salt, |
| std::vector<brillo::SecureBlob*> gen_secrets) { |
| if (gen_secrets.empty()) { |
| LOG(ERROR) << "No secrets requested from scrypt derivation."; |
| return false; |
| } |
| size_t total_len = 0; |
| for (auto& secret : gen_secrets) { |
| if (secret->empty()) { |
| LOG(ERROR) << "Empty secret requested from scrypt derivation."; |
| return false; |
| } |
| total_len += secret->size(); |
| } |
| |
| SecureBlob generated(total_len); |
| if (!Scrypt(passkey, salt, kDefaultScryptParams.n_factor, |
| kDefaultScryptParams.r_factor, kDefaultScryptParams.p_factor, |
| &generated)) { |
| LOG(ERROR) << "Failed to derive scrypt keys from passkey."; |
| return false; |
| } |
| |
| uint8_t* data = generated.data(); |
| for (auto& value : gen_secrets) { |
| value->assign(data, data + value->size()); |
| data += value->size(); |
| } |
| |
| return true; |
| } |
| |
| bool Scrypt(const brillo::SecureBlob& input, |
| const brillo::SecureBlob& salt, |
| int work_factor, |
| int block_size, |
| int parallel_factor, |
| brillo::SecureBlob* result) { |
| crypto::ScopedEVP_PKEY_CTX pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_SCRYPT, NULL)); |
| if (EVP_PKEY_derive_init(pctx.get()) <= 0) |
| return false; |
| if (EVP_PKEY_CTX_set1_pbe_pass(pctx.get(), input.data(), input.size()) <= 0) |
| return false; |
| if (EVP_PKEY_CTX_set1_scrypt_salt(pctx.get(), salt.data(), salt.size()) <= 0) |
| return false; |
| if (EVP_PKEY_CTX_set_scrypt_N(pctx.get(), work_factor) <= 0) |
| return false; |
| if (EVP_PKEY_CTX_set_scrypt_r(pctx.get(), block_size) <= 0) |
| return false; |
| if (EVP_PKEY_CTX_set_scrypt_p(pctx.get(), parallel_factor) <= 0) |
| return false; |
| |
| size_t outlen = result->size(); |
| int rc = EVP_PKEY_derive(pctx.get(), result->data(), &outlen); |
| |
| return rc > 0 && outlen == result->size(); |
| } |
| |
| bool DeprecatedEncryptScryptBlob(const brillo::SecureBlob& blob, |
| const brillo::SecureBlob& key_source, |
| brillo::SecureBlob* wrapped_blob) { |
| wrapped_blob->resize(blob.size() + kScryptMetadataSize); |
| |
| brillo::SecureBlob salt = CreateSecureRandomBlob(kLibScryptSaltSize); |
| brillo::SecureBlob derived_key(kLibScryptDerivedKeySize, '0'); |
| if (!Scrypt(key_source, salt, gScryptParams.n_factor, gScryptParams.r_factor, |
| gScryptParams.p_factor, &derived_key) != 0) { |
| LOG(ERROR) << "Failed to derive key with scrypt."; |
| return false; |
| } |
| |
| if (!LibScryptCompat::Encrypt(derived_key, salt, blob, gScryptParams, |
| wrapped_blob)) { |
| LOG(ERROR) << "Failed to generate encrypted data."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DeprecatedDecryptScryptBlob(const brillo::SecureBlob& wrapped_blob, |
| const brillo::SecureBlob& key, |
| brillo::SecureBlob* blob, |
| CryptoError* error) { |
| DCHECK(blob->size() >= wrapped_blob.size()); |
| |
| ScryptParameters params; |
| brillo::SecureBlob salt; |
| if (!LibScryptCompat::ParseHeader(wrapped_blob, ¶ms, &salt)) { |
| LOG(ERROR) << "Failed to parse header."; |
| PopulateError(error, CryptoError::CE_SCRYPT_CRYPTO); |
| return false; |
| } |
| |
| // Generate the derived key. |
| brillo::SecureBlob derived_key(kLibScryptDerivedKeySize, 0); |
| if (!Scrypt(key, salt, params.n_factor, params.r_factor, params.p_factor, |
| &derived_key)) { |
| LOG(ERROR) << "scrypt failed"; |
| return false; |
| } |
| |
| if (!LibScryptCompat::Decrypt(wrapped_blob, derived_key, blob)) { |
| LOG(ERROR) << "Failed to decrypt output."; |
| PopulateError(error, CryptoError::CE_SCRYPT_CRYPTO); |
| return false; |
| } |
| |
| // Check if the plaintext is the right length. |
| if ((wrapped_blob.size() < kScryptMetadataSize) || |
| (blob->size() != (wrapped_blob.size() - kScryptMetadataSize))) { |
| LOG(ERROR) << "Blob Scrypt decryption output was the wrong length"; |
| PopulateError(error, CryptoError::CE_SCRYPT_CRYPTO); |
| return false; |
| } |
| return true; |
| } |
| |
| void AssertProductionScryptParams() { |
| // Always perform the check just in case. |
| CHECK_EQ(kDefaultScryptParams.n_factor, gScryptParams.n_factor); |
| CHECK_EQ(kDefaultScryptParams.r_factor, gScryptParams.r_factor); |
| CHECK_EQ(kDefaultScryptParams.p_factor, gScryptParams.p_factor); |
| } |
| |
| void SetScryptTestingParams() { |
| gScryptParams = kTestScryptParams; |
| } |
| |
| } // namespace cryptohome |