| // Copyright 2020 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/libscrypt_compat.h" |
| |
| #include <openssl/aes.h> |
| #include <openssl/evp.h> |
| |
| #include <base/bits.h> |
| #include <base/sys_byteorder.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/scoped_openssl_types.h> |
| |
| #include "cryptohome/cryptolib.h" |
| |
| namespace cryptohome { |
| |
| // Callers of this library need to allocate the salt and key, so the sizes are |
| // exposed. |
| constexpr size_t kLibScryptSaltSize = 32; |
| |
| constexpr size_t kLibScryptDerivedKeySize = 64; |
| |
| namespace { |
| |
| constexpr size_t kLibScryptHeaderSize = 96; |
| |
| constexpr size_t kLibScryptSubHeaderSize = 48; |
| |
| constexpr size_t kLibScryptHeaderBytesToHMAC = 64; |
| |
| constexpr char kLibScryptHeaderMagic[] = "scrypt"; |
| |
| // Bytes 33-64 of the derived key are used for the HMAC key. |
| constexpr size_t kLibScryptHMACKeyOffset = 32; |
| |
| constexpr size_t kLibScryptHMACSize = 32; |
| |
| constexpr size_t kLibScryptIVSize = 16; |
| |
| // libscrypt places data into a uint8_t[96] array in C style. This lays it out |
| // as a more readable struct, but it must be tightly packed to be compatible |
| // with the array. |
| #pragma pack(push, 1) |
| struct LibScryptHeader { |
| // This is always "scrypt". |
| char magic[6]; |
| // This is set to 0. |
| uint8_t header_reserved_byte; |
| // The log base 2 of the N-factor (i.e. 10 for 1024). |
| uint8_t log_n; |
| // The r and p params used to generate this key. |
| uint32_t r_factor; |
| uint32_t p_factor; |
| // A salt which is unique to each encryption. Note that this is a bit odd and |
| // in new scrypt code it's better to use a unique *nonce* in the AES |
| // encryption. |
| uint8_t salt[32]; |
| // This is a checksum of the first 48 bytes of the header (all fields up to |
| // and including the salt). |
| uint8_t check_sum[16]; |
| // This is an HMAC over the first 64 bytes of the header (all fields up to and |
| // including the check_sum). Why there is a check_sum and an HMAC is |
| // confusing, since they cover the same data. But the key given to the HMAC is |
| // the last 32 bytes of the |derived_key|, and so it verifies that the |
| // password is the proper passsord for this encrypted blob. |
| uint8_t signature[kLibScryptHMACSize]; |
| }; |
| #pragma pack(pop) |
| |
| static_assert(sizeof(LibScryptHeader) == kLibScryptHeaderSize, |
| "LibScryptHeader struct is packed wrong and will not be byte " |
| "compatible with existing data"); |
| |
| // This generates the header which is specific to libscrypt. It's inserted at |
| // the beginning |output|. |
| void GenerateHeader(const brillo::SecureBlob& salt, |
| const brillo::SecureBlob& derived_key, |
| const ScryptParameters& params, |
| LibScryptHeader* header_struct) { |
| DCHECK_EQ(kLibScryptSaltSize, salt.size()); |
| |
| *header_struct = { |
| {'s', 'c', 'r', 'y', 'p', 't'}, |
| 0, |
| static_cast<uint8_t>(base::bits::Log2Ceiling(params.n_factor)), |
| base::ByteSwap(params.r_factor), |
| base::ByteSwap(params.p_factor)}; |
| |
| memcpy(&header_struct->salt, salt.data(), sizeof(header_struct->salt)); |
| |
| // Add the header check sum. |
| uint8_t* header_ptr = reinterpret_cast<uint8_t*>(header_struct); |
| brillo::Blob header_blob_to_hash(header_ptr, |
| header_ptr + kLibScryptSubHeaderSize); |
| brillo::Blob sha = CryptoLib::Sha256(header_blob_to_hash); |
| memcpy(&header_struct->check_sum[0], sha.data(), |
| sizeof(header_struct->check_sum)); |
| |
| // Add the header signature (used for verifying the passsword). |
| brillo::SecureBlob key_hmac(derived_key.begin() + kLibScryptHMACKeyOffset, |
| derived_key.end()); |
| brillo::Blob data_to_hmac(header_ptr, |
| header_ptr + kLibScryptHeaderBytesToHMAC); |
| brillo::SecureBlob hmac = CryptoLib::HmacSha256(key_hmac, data_to_hmac); |
| memcpy(&header_struct->signature[0], hmac.data(), |
| sizeof(header_struct->signature)); |
| } |
| |
| bool VerifyDerivedKey(const brillo::SecureBlob& encrypted_blob, |
| const brillo::SecureBlob& derived_key) { |
| const LibScryptHeader* header = |
| reinterpret_cast<const LibScryptHeader*>(encrypted_blob.data()); |
| const uint8_t* header_ptr = reinterpret_cast<const uint8_t*>(header); |
| |
| // Verify the password. |
| brillo::SecureBlob key_hmac(derived_key.begin() + kLibScryptHMACKeyOffset, |
| derived_key.end()); |
| brillo::Blob data_to_hmac(header_ptr, |
| header_ptr + kLibScryptHeaderBytesToHMAC); |
| brillo::SecureBlob hmac = CryptoLib::HmacSha256(key_hmac, data_to_hmac); |
| if (brillo::SecureMemcmp(header->signature, hmac.data(), |
| kLibScryptHMACSize) != 0) { |
| LOG(ERROR) << "hmac verification failed."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| // static |
| bool LibScryptCompat::Encrypt(const brillo::SecureBlob& derived_key, |
| const brillo::SecureBlob& salt, |
| const brillo::SecureBlob& data_to_encrypt, |
| const ScryptParameters& params, |
| brillo::SecureBlob* encrypted_data) { |
| encrypted_data->resize(data_to_encrypt.size() + kLibScryptHeaderSize + |
| kLibScryptHMACSize); |
| |
| LibScryptHeader header_struct; |
| GenerateHeader(salt, derived_key, params, &header_struct); |
| memcpy(encrypted_data->data(), &header_struct, sizeof(header_struct)); |
| |
| brillo::SecureBlob aes_key(derived_key.begin(), |
| derived_key.end() - kLibScryptHMACKeyOffset); |
| // libscrypt uses a 0 IV for every message. This is safe _ONLY_ because |
| // libscrypt mixes the passphrase with a new salt, generating a new derived |
| // key, FOR EACH ENCRYPTION. DO NOT CALL THIS ENCRYPTION method multiple times |
| // with the same key, it is only safe under this limited circumstances. |
| brillo::SecureBlob iv(kLibScryptIVSize, 0); |
| brillo::SecureBlob aes_ciphertext; |
| |
| if (!CryptoLib::AesEncryptSpecifyBlockMode( |
| data_to_encrypt, 0, data_to_encrypt.size(), aes_key, iv, |
| CryptoLib::kPaddingStandard, CryptoLib::kCtr, &aes_ciphertext)) { |
| LOG(ERROR) << "AesEncryptSpecifyBlockMode failed."; |
| return false; |
| } |
| memcpy(encrypted_data->data() + sizeof(header_struct), aes_ciphertext.data(), |
| aes_ciphertext.size()); |
| |
| brillo::SecureBlob key_hmac(derived_key.begin() + kLibScryptHMACKeyOffset, |
| derived_key.end()); |
| brillo::Blob data_to_hmac( |
| encrypted_data->begin(), |
| encrypted_data->begin() + aes_ciphertext.size() + kLibScryptHeaderSize); |
| brillo::SecureBlob hmac = CryptoLib::HmacSha256(key_hmac, data_to_hmac); |
| |
| memcpy(encrypted_data->data() + sizeof(header_struct) + aes_ciphertext.size(), |
| hmac.data(), kLibScryptHMACSize); |
| |
| return true; |
| } |
| |
| // static |
| bool LibScryptCompat::ParseHeader(const brillo::SecureBlob& encrypted_blob, |
| ScryptParameters* out_params, |
| brillo::SecureBlob* salt) { |
| if (encrypted_blob.size() < kLibScryptHeaderSize + kLibScryptHMACSize) { |
| LOG(ERROR) << "Incomplete header present."; |
| return false; |
| } |
| |
| const LibScryptHeader* header = |
| reinterpret_cast<const LibScryptHeader*>(encrypted_blob.data()); |
| if (brillo::SecureMemcmp(header->magic, kLibScryptHeaderMagic, |
| strlen(kLibScryptHeaderMagic)) != 0) { |
| LOG(ERROR) << "wrong header text present"; |
| return false; |
| } |
| |
| if (header->header_reserved_byte != 0) { |
| LOG(ERROR) << "Wrong reserved byte present"; |
| return false; |
| } |
| |
| // Verify the header checksum before returning any information. |
| const uint8_t* header_ptr = reinterpret_cast<const uint8_t*>(header); |
| brillo::Blob header_blob_to_hash(header_ptr, |
| header_ptr + kLibScryptSubHeaderSize); |
| brillo::Blob sha = CryptoLib::Sha256(header_blob_to_hash); |
| if (brillo::SecureMemcmp(sha.data(), header->check_sum, |
| sizeof(header->check_sum)) != 0) { |
| LOG(ERROR) << "Wrong checksum present."; |
| return false; |
| } |
| |
| // Now parse the parameters. |
| if (header->log_n < 1 || header->log_n > 63) { |
| LOG(ERROR) << "Invalid logN present in header."; |
| return false; |
| } |
| |
| out_params->n_factor = static_cast<uint64_t>(1) << header->log_n; |
| out_params->r_factor = base::ByteSwap(header->r_factor); |
| out_params->p_factor = base::ByteSwap(header->p_factor); |
| salt->assign(header->salt, header->salt + kLibScryptSaltSize); |
| |
| return true; |
| } |
| |
| // static |
| bool LibScryptCompat::Decrypt( |
| const brillo::SecureBlob& encrypted_data, |
| const brillo::SecureBlob& derived_key, |
| brillo::SecureBlob* decrypted_data) { |
| if (!VerifyDerivedKey(encrypted_data, derived_key)) |
| return false; |
| |
| // lib scrypt appends an HMAC. |
| brillo::SecureBlob key_hmac(derived_key.begin() + kLibScryptHMACKeyOffset, |
| derived_key.end()); |
| brillo::Blob data_to_hmac(encrypted_data.begin(), |
| encrypted_data.end() - kLibScryptHMACSize); |
| brillo::SecureBlob hmac = CryptoLib::HmacSha256(key_hmac, data_to_hmac); |
| brillo::SecureBlob hmac_from_blob(encrypted_data.end() - kLibScryptHMACSize, |
| encrypted_data.end()); |
| if (brillo::SecureMemcmp(hmac.data(), hmac_from_blob.data(), |
| kLibScryptHMACSize) != 0) { |
| return false; |
| } |
| |
| brillo::SecureBlob aes_key(derived_key.begin(), |
| derived_key.end() - kLibScryptHMACKeyOffset); |
| // libscrypt uses a 0 IV for every message. This is safe _ONLY_ because |
| // libscrypt mixes the passphrase with a new salt, generating a new derived |
| // key, FOR EACH ENCRYPTION. |
| brillo::SecureBlob iv(kLibScryptIVSize, 0); |
| brillo::SecureBlob data_to_decrypt( |
| encrypted_data.begin() + kLibScryptHeaderSize, |
| encrypted_data.end() - kLibScryptHMACSize); |
| |
| if (!CryptoLib::AesDecryptSpecifyBlockMode( |
| data_to_decrypt, 0, data_to_decrypt.size(), aes_key, iv, |
| CryptoLib::kPaddingStandard, CryptoLib::kCtr, decrypted_data)) { |
| LOG(ERROR) << "AesDecryptSpecifyBlockMode failed."; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace cryptohome |