blob: 15263cb00612af1b58e29418f176336ec9f5fef4 [file] [log] [blame]
// 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, &params, &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