Merge branch 'secg' into 'master'
luks: Integrate support for a TKey See merge request u-boot/u-boot!211
This commit is contained in:
94
cmd/luks.c
94
cmd/luks.c
@@ -8,8 +8,11 @@
|
|||||||
#include <blk.h>
|
#include <blk.h>
|
||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
|
#include <hexdump.h>
|
||||||
#include <luks.h>
|
#include <luks.h>
|
||||||
#include <part.h>
|
#include <part.h>
|
||||||
|
#include <tkey.h>
|
||||||
|
#include <u-boot/sha256.h>
|
||||||
|
|
||||||
static int do_luks_detect(struct cmd_tbl *cmdtp, int flag, int argc,
|
static int do_luks_detect(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *const argv[])
|
char *const argv[])
|
||||||
@@ -57,18 +60,86 @@ static int do_luks_info(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||||||
return CMD_RET_SUCCESS;
|
return CMD_RET_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unlock_with_tkey() - Unlock LUKS partition using TKey-derived key
|
||||||
|
*
|
||||||
|
* This function uses TKey to derive a disk encryption key from the
|
||||||
|
* provided passphrase (used as USS) and uses it to unlock the LUKS partition.
|
||||||
|
*
|
||||||
|
* @dev_desc: Block device descriptor
|
||||||
|
* @info: Partition information
|
||||||
|
* @passphrase: Passphrase to use as USS for TKey
|
||||||
|
* @master_key: Buffer to receive unlocked master key
|
||||||
|
* @key_size: Pointer to receive key size
|
||||||
|
* Return: 0 on success, -ve on error
|
||||||
|
*/
|
||||||
|
static int unlock_with_tkey(struct blk_desc *dev_desc,
|
||||||
|
struct disk_partition *info, const char *passphrase,
|
||||||
|
u8 *master_key, u32 *key_size)
|
||||||
|
{
|
||||||
|
u8 tkey_disk_key[TKEY_DISK_KEY_SIZE];
|
||||||
|
u8 pubkey[TKEY_PUBKEY_SIZE];
|
||||||
|
struct udevice *tkey_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
printf("Using TKey for disk encryption key\n");
|
||||||
|
|
||||||
|
/* Find TKey device */
|
||||||
|
ret = uclass_first_device_err(UCLASS_TKEY, &tkey_dev);
|
||||||
|
if (ret) {
|
||||||
|
printf("Failed to find TKey device (err %dE)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derive disk key using TKey with passphrase as USS */
|
||||||
|
printf("Loading TKey signer app (%lx bytes) with USS...\n",
|
||||||
|
TKEY_SIGNER_SIZE);
|
||||||
|
ret = tkey_derive_disk_key(tkey_dev, (const u8 *)__signer_1_0_0_begin,
|
||||||
|
TKEY_SIGNER_SIZE, (const u8 *)passphrase,
|
||||||
|
strlen(passphrase), tkey_disk_key, pubkey,
|
||||||
|
NULL);
|
||||||
|
if (ret) {
|
||||||
|
printf("Failed to derive TKey disk key (err %dE)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("TKey public key: ");
|
||||||
|
print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1, pubkey,
|
||||||
|
TKEY_PUBKEY_SIZE, false);
|
||||||
|
|
||||||
|
printf("TKey disk key derived successfully\n");
|
||||||
|
printf("TKey derived disk key: ");
|
||||||
|
print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1, tkey_disk_key,
|
||||||
|
TKEY_DISK_KEY_SIZE, false);
|
||||||
|
|
||||||
|
ret = luks_unlock(dev_desc->bdev, info, tkey_disk_key,
|
||||||
|
TKEY_DISK_KEY_SIZE, true, master_key, key_size);
|
||||||
|
|
||||||
|
/* Wipe TKey disk key */
|
||||||
|
memset(tkey_disk_key, '\0', sizeof(tkey_disk_key));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
|
static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *const argv[])
|
char *const argv[])
|
||||||
{
|
{
|
||||||
struct blk_desc *dev_desc;
|
struct blk_desc *dev_desc;
|
||||||
struct disk_partition info;
|
struct disk_partition info;
|
||||||
struct udevice *blkmap_dev;
|
struct udevice *blkmap_dev;
|
||||||
const char *passphrase;
|
const char *passphrase = NULL;
|
||||||
|
bool use_tkey = false;
|
||||||
int part, ret, version;
|
int part, ret, version;
|
||||||
u8 master_key[128];
|
u8 master_key[128];
|
||||||
char label[64];
|
char label[64];
|
||||||
u32 key_size;
|
u32 key_size;
|
||||||
|
|
||||||
|
/* Check for -t flag */
|
||||||
|
if (!strcmp(argv[1], "-t")) {
|
||||||
|
use_tkey = true;
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
if (argc != 4)
|
if (argc != 4)
|
||||||
return CMD_RET_USAGE;
|
return CMD_RET_USAGE;
|
||||||
|
|
||||||
@@ -78,6 +149,10 @@ static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||||||
|
|
||||||
passphrase = argv[3];
|
passphrase = argv[3];
|
||||||
|
|
||||||
|
log_debug("Partition start %llx blks %llx blksz%lx\n",
|
||||||
|
(unsigned long long)info.start, (unsigned long long)info.size,
|
||||||
|
(ulong)dev_desc->blksz);
|
||||||
|
|
||||||
/* Verify it's a LUKS partition */
|
/* Verify it's a LUKS partition */
|
||||||
version = luks_get_version(dev_desc->bdev, &info);
|
version = luks_get_version(dev_desc->bdev, &info);
|
||||||
if (version < 0) {
|
if (version < 0) {
|
||||||
@@ -87,9 +162,15 @@ static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||||||
|
|
||||||
printf("Unlocking LUKS%d partition...\n", version);
|
printf("Unlocking LUKS%d partition...\n", version);
|
||||||
|
|
||||||
/* Unlock the partition to get the master key */
|
if (use_tkey) {
|
||||||
ret = luks_unlock(dev_desc->bdev, &info, passphrase, master_key,
|
ret = unlock_with_tkey(dev_desc, &info, passphrase, master_key,
|
||||||
&key_size);
|
&key_size);
|
||||||
|
} else {
|
||||||
|
/* Unlock with passphrase */
|
||||||
|
ret = luks_unlock(dev_desc->bdev, &info,(const u8 *)passphrase,
|
||||||
|
strlen(passphrase), false, master_key,
|
||||||
|
&key_size);
|
||||||
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printf("Failed to unlock LUKS partition (err %dE)\n", ret);
|
printf("Failed to unlock LUKS partition (err %dE)\n", ret);
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
@@ -121,10 +202,11 @@ cleanup:
|
|||||||
static char luks_help_text[] =
|
static char luks_help_text[] =
|
||||||
"detect <interface> <dev[:part]> - detect if partition is LUKS encrypted\n"
|
"detect <interface> <dev[:part]> - detect if partition is LUKS encrypted\n"
|
||||||
"luks info <interface> <dev[:part]> - show LUKS header information\n"
|
"luks info <interface> <dev[:part]> - show LUKS header information\n"
|
||||||
"luks unlock <interface> <dev[:part]> <passphrase> - unlock LUKS partition\n";
|
"luks unlock [-t] <interface> <dev[:part]> <passphrase> - unlock LUKS partition\n"
|
||||||
|
" -t: Use TKey hardware security token with passphrase as USS\n";
|
||||||
|
|
||||||
U_BOOT_CMD_WITH_SUBCMDS(luks, "LUKS (Linux Unified Key Setup) operations",
|
U_BOOT_CMD_WITH_SUBCMDS(luks, "LUKS (Linux Unified Key Setup) operations",
|
||||||
luks_help_text,
|
luks_help_text,
|
||||||
U_BOOT_SUBCMD_MKENT(detect, 3, 1, do_luks_detect),
|
U_BOOT_SUBCMD_MKENT(detect, 3, 1, do_luks_detect),
|
||||||
U_BOOT_SUBCMD_MKENT(info, 3, 1, do_luks_info),
|
U_BOOT_SUBCMD_MKENT(info, 3, 1, do_luks_info),
|
||||||
U_BOOT_SUBCMD_MKENT(unlock, 4, 1, do_luks_unlock));
|
U_BOOT_SUBCMD_MKENT(unlock, 5, 1, do_luks_unlock));
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Synopsis
|
|||||||
|
|
||||||
luks detect <interface> <dev[:part]>
|
luks detect <interface> <dev[:part]>
|
||||||
luks info <interface> <dev[:part]>
|
luks info <interface> <dev[:part]>
|
||||||
luks unlock <interface> <dev[:part]> <passphrase>
|
luks unlock [-t] <interface> <dev[:part]> <passphrase>
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
@@ -88,12 +88,17 @@ dev[:part]
|
|||||||
luks unlock
|
luks unlock
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
Unlock a LUKS encrypted partition using a passphrase. This command:
|
Unlock a LUKS encrypted partition using a passphrase or TKey hardware token.
|
||||||
|
This command:
|
||||||
|
|
||||||
1. Verifies the partition is LUKS encrypted (LUKS1 or LUKS2)
|
1. Verifies the partition is LUKS encrypted (LUKS1 or LUKS2)
|
||||||
2. Parses LUKS2 JSON metadata (if LUKS2) using FDT conversion
|
2. Parses LUKS2 JSON metadata (if LUKS2) using FDT conversion
|
||||||
3. Derives the encryption key using PBKDF2 or Argon2id with the provided
|
3. Derives the encryption key:
|
||||||
passphrase
|
|
||||||
|
- **Without -t**: Uses PBKDF2 or Argon2id with the provided passphrase
|
||||||
|
- **With -t**: Uses TKey hardware token with passphrase as USS (User-Supplied
|
||||||
|
Secret) to derive a disk encryption key
|
||||||
|
|
||||||
4. Attempts to unlock each active key slot
|
4. Attempts to unlock each active key slot
|
||||||
5. Verifies the master key against the stored digest
|
5. Verifies the master key against the stored digest
|
||||||
6. Creates a blkmap device providing on-the-fly decryption
|
6. Creates a blkmap device providing on-the-fly decryption
|
||||||
@@ -118,6 +123,11 @@ be used to access files on the unlocked partition.
|
|||||||
* **Argon2id**: Memory-hard KDF resistant to GPU attacks (LUKS2 only, requires
|
* **Argon2id**: Memory-hard KDF resistant to GPU attacks (LUKS2 only, requires
|
||||||
CONFIG_ARGON2)
|
CONFIG_ARGON2)
|
||||||
|
|
||||||
|
-t
|
||||||
|
Optional flag to use TKey hardware security token. When specified, the
|
||||||
|
passphrase is used as the USS (User-Supplied Secret) to derive a disk
|
||||||
|
encryption key from the TKey's public key.
|
||||||
|
|
||||||
interface
|
interface
|
||||||
The storage interface type (e.g., mmc, usb, scsi)
|
The storage interface type (e.g., mmc, usb, scsi)
|
||||||
|
|
||||||
@@ -125,7 +135,8 @@ dev[:part]
|
|||||||
The device number and optional partition number
|
The device number and optional partition number
|
||||||
|
|
||||||
passphrase
|
passphrase
|
||||||
The passphrase to unlock the LUKS partition. Note that the passphrase is
|
The passphrase to unlock the LUKS partition. When using -t flag, this is
|
||||||
|
used as the USS for TKey key derivation. Note that the passphrase is
|
||||||
passed as a command-line argument and may be visible in command history.
|
passed as a command-line argument and may be visible in command history.
|
||||||
Consider using environment variables to minimize exposure.
|
Consider using environment variables to minimize exposure.
|
||||||
|
|
||||||
@@ -228,6 +239,17 @@ Unlock and load a kernel from encrypted partition::
|
|||||||
|
|
||||||
=> bootz ${kernel_addr_r} - ${fdt_addr_r}
|
=> bootz ${kernel_addr_r} - ${fdt_addr_r}
|
||||||
|
|
||||||
|
Unlock using TKey hardware token::
|
||||||
|
|
||||||
|
=> luks unlock -t mmc 0:2 mypassword
|
||||||
|
Using TKey for disk encryption key
|
||||||
|
Loading TKey signer app (7168 bytes) with USS...
|
||||||
|
TKey public key: 3a b2 c4 ... (32 bytes)
|
||||||
|
TKey disk key derived successfully
|
||||||
|
Unlocking LUKS2 partition...
|
||||||
|
Successfully unlocked with key slot 0!
|
||||||
|
Unlocked LUKS partition as blkmap device 'luks-mmc-0:2'
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@@ -254,6 +276,10 @@ For Argon2id support (modern LUKS2 KDF)::
|
|||||||
|
|
||||||
CONFIG_ARGON2=y # Argon2 password hashing (adds ~50KB to binary)
|
CONFIG_ARGON2=y # Argon2 password hashing (adds ~50KB to binary)
|
||||||
|
|
||||||
|
For TKey hardware token support (requires -t flag)::
|
||||||
|
|
||||||
|
CONFIG_TKEY=y # TKey hardware security token support
|
||||||
|
|
||||||
Return value
|
Return value
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|||||||
@@ -195,20 +195,6 @@ static int af_hash(struct hash_algo *algo, size_t key_size, u8 *block_buf)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* af_merge() - Merge anti-forensic split key into original key
|
|
||||||
*
|
|
||||||
* This performs the LUKS AF-merge operation to recover the original key from
|
|
||||||
* its AF-split representation. The algorithm XORs all stripes together,
|
|
||||||
* applying diffusion between each stripe.
|
|
||||||
*
|
|
||||||
* @src: AF-split key material (key_size * stripes bytes)
|
|
||||||
* @dst: Output buffer for merged key (key_size bytes)
|
|
||||||
* @key_size: Size of the original key
|
|
||||||
* @stripes: Number of anti-forensic stripes
|
|
||||||
* @hash_spec: Hash algorithm name (e.g., "sha256")
|
|
||||||
* Return: 0 on success, -ve on error
|
|
||||||
*/
|
|
||||||
int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
|
int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
|
||||||
const char *hash_spec)
|
const char *hash_spec)
|
||||||
{
|
{
|
||||||
@@ -250,23 +236,8 @@ int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void essiv_decrypt(const u8 *derived_key, uint key_size, u8 *expkey,
|
||||||
* essiv_decrypt() - Decrypt key material using ESSIV mode
|
u8 *km, u8 *split_key, uint km_blocks, uint blksz)
|
||||||
*
|
|
||||||
* ESSIV (Encrypted Salt-Sector Initialization Vector) mode generates a unique
|
|
||||||
* IV for each sector by encrypting the sector number with a key derived from
|
|
||||||
* hashing the encryption key.
|
|
||||||
*
|
|
||||||
* @derived_key: Key derived from passphrase
|
|
||||||
* @key_size: Size of the encryption key in bytes
|
|
||||||
* @expkey: Expanded AES key for decryption
|
|
||||||
* @km: Encrypted key material buffer
|
|
||||||
* @split_key: Output buffer for decrypted key material
|
|
||||||
* @km_blocks: Number of blocks of key material
|
|
||||||
* @blksz: Block size in bytes
|
|
||||||
*/
|
|
||||||
static void essiv_decrypt(u8 *derived_key, uint key_size, u8 *expkey, u8 *km,
|
|
||||||
u8 *split_key, uint km_blocks, uint blksz)
|
|
||||||
{
|
{
|
||||||
u8 essiv_expkey[AES256_EXPAND_KEY_LENGTH];
|
u8 essiv_expkey[AES256_EXPAND_KEY_LENGTH];
|
||||||
u8 essiv_key_material[SHA256_SUM_LEN];
|
u8 essiv_key_material[SHA256_SUM_LEN];
|
||||||
@@ -317,58 +288,31 @@ static void essiv_decrypt(u8 *derived_key, uint key_size, u8 *expkey, u8 *km,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* try_keyslot() - Unlock a LUKS key slot with a passphrase
|
* derive_key_pbkdf2() - Derive key from passphrase using PBKDF2
|
||||||
*
|
*
|
||||||
* @blk: Block device
|
* @slot: LUKS keyslot containing salt and iteration count
|
||||||
* @pinfo: Partition information
|
* @pass: Passphrase
|
||||||
* @hdr: LUKS header
|
* @pass_len: Length of passphrase
|
||||||
* @slot_idx: Key slot index to try
|
|
||||||
* @pass: Passphrase to try
|
|
||||||
* @md_type: Hash algorithm type
|
* @md_type: Hash algorithm type
|
||||||
* @key_size: Size of the key
|
* @key_size: Size of the key to derive
|
||||||
* @derived_key: Buffer for derived key (key_size bytes)
|
* @derived_key: Buffer for derived key (key_size bytes)
|
||||||
* @km: Buffer for encrypted key material
|
* Return: 0 on success, -EPROTO on error
|
||||||
* @km_blocks: Size of km buffer in blocks
|
|
||||||
* @split_key: Buffer for AF-split key
|
|
||||||
* @candidate_key: Buffer to receive decrypted master key
|
|
||||||
*
|
|
||||||
* Return: 0 on success (correct passphrase), -EPROTO on mbedtls error, -ve on
|
|
||||||
* other error
|
|
||||||
*/
|
*/
|
||||||
static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
static int derive_key_pbkdf2(struct luks1_keyslot *slot, const u8 *pass,
|
||||||
struct luks1_phdr *hdr, int slot_idx,
|
size_t pass_len, mbedtls_md_type_t md_type,
|
||||||
const char *pass, mbedtls_md_type_t md_type,
|
uint key_size, u8 *derived_key)
|
||||||
uint key_size, u8 *derived_key, u8 *km, uint km_blocks,
|
|
||||||
u8 *split_key, u8 *candidate_key)
|
|
||||||
{
|
{
|
||||||
struct luks1_keyslot *slot = &hdr->key_slot[slot_idx];
|
uint iters = be32_to_cpu(slot->iterations);
|
||||||
uint iters, km_offset, stripes, split_key_size;
|
|
||||||
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
|
||||||
u8 expkey[AES256_EXPAND_KEY_LENGTH];
|
|
||||||
u8 key_digest[LUKS_DIGESTSIZE];
|
|
||||||
u8 iv[AES_BLOCK_LENGTH];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Check if slot is active */
|
|
||||||
if (be32_to_cpu(slot->active) != LUKS_KEY_ENABLED)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
log_debug("trying key slot %d...\n", slot_idx);
|
|
||||||
|
|
||||||
iters = be32_to_cpu(slot->iterations);
|
|
||||||
km_offset = be32_to_cpu(slot->key_material_offset);
|
|
||||||
stripes = be32_to_cpu(slot->stripes);
|
|
||||||
split_key_size = key_size * stripes;
|
|
||||||
|
|
||||||
/* Derive key from passphrase using PBKDF2 */
|
/* Derive key from passphrase using PBKDF2 */
|
||||||
log_debug("PBKDF2(pass '%s'[len %zu], ", pass, strlen(pass));
|
log_debug("PBKDF2(pass len=%zu, ", pass_len);
|
||||||
log_debug_hex("salt[0-7]", (u8 *)slot->salt, 8);
|
log_debug_hex("salt[0-7]", (u8 *)slot->salt, 8);
|
||||||
log_debug("iter %u, keylen %u)\n", iters, key_size);
|
log_debug("iter %u, keylen %u)\n", iters, key_size);
|
||||||
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, (const u8 *)pass,
|
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, pass, pass_len,
|
||||||
strlen(pass),
|
|
||||||
(const u8 *)slot->salt,
|
(const u8 *)slot->salt,
|
||||||
LUKS_SALTSIZE, iters, key_size,
|
LUKS_SALTSIZE, iters,
|
||||||
derived_key);
|
key_size, derived_key);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
log_debug("PBKDF2 failed: %d\n", ret);
|
log_debug("PBKDF2 failed: %d\n", ret);
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
@@ -376,6 +320,64 @@ static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
|
|
||||||
log_debug_hex("derived_key[0-7]", derived_key, 8);
|
log_debug_hex("derived_key[0-7]", derived_key, 8);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unlock_luks1() - Unlock a LUKSv1 partition
|
||||||
|
*
|
||||||
|
* @blk: Block device
|
||||||
|
* @pinfo: Partition information
|
||||||
|
* @hdr: LUKS1 header (already read)
|
||||||
|
* @pass: Passphrase or pre-derived key
|
||||||
|
* @pass_len: Length of passphrase
|
||||||
|
* @pre_derived: True if pass is a pre-derived key, false for passphrase
|
||||||
|
* @master_key: Buffer to receive master key
|
||||||
|
* @key_size: Output for key size
|
||||||
|
*
|
||||||
|
* Return: 0 on success, -ve on error
|
||||||
|
*/
|
||||||
|
static int unlock_luks1(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
|
struct luks1_phdr *hdr, const u8 *pass, size_t pass_len,
|
||||||
|
bool pre_derived, u8 *master_key, u32 *key_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try_keyslot() - Try to unlock a LUKS key slot with a derived key
|
||||||
|
*
|
||||||
|
* @blk: Block device
|
||||||
|
* @pinfo: Partition information
|
||||||
|
* @hdr: LUKS header
|
||||||
|
* @slot_idx: Key slot index to try
|
||||||
|
* @md_type: Hash algorithm type for master key verification
|
||||||
|
* @key_size: Size of the key
|
||||||
|
* @derived_key: Pre-derived key from PBKDF2 (key_size bytes)
|
||||||
|
* @km: Buffer for encrypted key material
|
||||||
|
* @km_blocks: Size of km buffer in blocks
|
||||||
|
* @split_key: Buffer for AF-split key
|
||||||
|
* @candidate_key: Buffer to receive decrypted master key
|
||||||
|
*
|
||||||
|
* Return: 0 on success (correct key), -ve on error
|
||||||
|
*/
|
||||||
|
static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
|
struct luks1_phdr *hdr, int slot_idx,
|
||||||
|
mbedtls_md_type_t md_type, uint key_size,
|
||||||
|
const u8 *derived_key, u8 *km, uint km_blocks,
|
||||||
|
u8 *split_key, u8 *candidate_key)
|
||||||
|
{
|
||||||
|
struct luks1_keyslot *slot = &hdr->key_slot[slot_idx];
|
||||||
|
uint km_offset, stripes, split_key_size;
|
||||||
|
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
||||||
|
u8 expkey[AES256_EXPAND_KEY_LENGTH];
|
||||||
|
u8 key_digest[LUKS_DIGESTSIZE];
|
||||||
|
u8 iv[AES_BLOCK_LENGTH];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
log_debug("trying key slot %d with derived key\n", slot_idx);
|
||||||
|
|
||||||
|
km_offset = be32_to_cpu(slot->key_material_offset);
|
||||||
|
stripes = be32_to_cpu(slot->stripes);
|
||||||
|
split_key_size = key_size * stripes;
|
||||||
|
|
||||||
/* Read encrypted key material */
|
/* Read encrypted key material */
|
||||||
ret = blk_read(blk, pinfo->start + km_offset, km_blocks, km);
|
ret = blk_read(blk, pinfo->start + km_offset, km_blocks, km);
|
||||||
if (ret != km_blocks) {
|
if (ret != km_blocks) {
|
||||||
@@ -387,9 +389,11 @@ static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
|
|
||||||
/* Decrypt key material using derived key */
|
/* Decrypt key material using derived key */
|
||||||
log_debug("expand key with key_size*8 %u bits\n", key_size * 8);
|
log_debug("expand key with key_size*8 %u bits\n", key_size * 8);
|
||||||
log_debug_hex("input key (derived_key) full:", derived_key, key_size);
|
log_debug_hex("derived_key", derived_key, key_size);
|
||||||
|
|
||||||
|
/* Decrypt key material */
|
||||||
aes_expand_key(derived_key, key_size * 8, expkey);
|
aes_expand_key(derived_key, key_size * 8, expkey);
|
||||||
|
|
||||||
log_debug_hex("expanded key [0-15]:", expkey, 16);
|
log_debug_hex("expanded key [0-15]:", expkey, 16);
|
||||||
|
|
||||||
/* Decrypt with CBC mode: first check if ESSIV is used */
|
/* Decrypt with CBC mode: first check if ESSIV is used */
|
||||||
@@ -398,10 +402,8 @@ static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
km_blocks, desc->blksz);
|
km_blocks, desc->blksz);
|
||||||
} else {
|
} else {
|
||||||
/* Plain CBC with zero IV */
|
/* Plain CBC with zero IV */
|
||||||
|
log_debug("using plain CBC mode\n");
|
||||||
memset(iv, '\0', sizeof(iv));
|
memset(iv, '\0', sizeof(iv));
|
||||||
log_debug("using plain CBC with zero IV\n");
|
|
||||||
log_debug("decrypting %u blocks\n",
|
|
||||||
split_key_size / AES_BLOCK_LENGTH);
|
|
||||||
aes_cbc_decrypt_blocks(key_size * 8, expkey, iv, km, split_key,
|
aes_cbc_decrypt_blocks(key_size * 8, expkey, iv, km, split_key,
|
||||||
split_key_size / AES_BLOCK_LENGTH);
|
split_key_size / AES_BLOCK_LENGTH);
|
||||||
}
|
}
|
||||||
@@ -434,7 +436,7 @@ static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
|
|
||||||
/* Check if the digest matches */
|
/* Check if the digest matches */
|
||||||
if (!memcmp(key_digest, hdr->mk_digest, LUKS_DIGESTSIZE)) {
|
if (!memcmp(key_digest, hdr->mk_digest, LUKS_DIGESTSIZE)) {
|
||||||
log_debug("Uunlocked with key slot %d\n", slot_idx);
|
log_debug("Unlocked with key slot %d\n", slot_idx);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
log_debug("key slot %d: wrong passphrase\n", slot_idx);
|
log_debug("key slot %d: wrong passphrase\n", slot_idx);
|
||||||
@@ -442,19 +444,161 @@ static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
int luks_unlock(struct udevice *blk, struct disk_partition *pinfo,
|
/**
|
||||||
const char *pass, u8 *master_key, u32 *key_size)
|
* unlock_luks1() - Unlock a LUKSv1 partition
|
||||||
|
*
|
||||||
|
* Attempts to unlock a LUKSv1 encrypted partition by trying each active
|
||||||
|
* key slot with the provided passphrase or pre-derived key. When pre_derived
|
||||||
|
* is false, uses PBKDF2 for key derivation. When true, uses the pass data
|
||||||
|
* directly as the derived key. Supports CBC cipher mode with optional ESSIV.
|
||||||
|
*
|
||||||
|
* @blk: Block device containing the partition
|
||||||
|
* @pinfo: Partition information
|
||||||
|
* @hdr: LUKSv1 header (already read and validated)
|
||||||
|
* @pass: Passphrase (binary data) or pre-derived key
|
||||||
|
* @pass_len: Length of passphrase in bytes
|
||||||
|
* @pre_derived: True if pass is a pre-derived key, false for passphrase
|
||||||
|
* @master_key: Buffer to receive unlocked master key (min 128 bytes)
|
||||||
|
* @key_sizep: Output for master key size in bytes (set only on success)
|
||||||
|
*
|
||||||
|
* Return: 0 on success, -ve on error
|
||||||
|
*/
|
||||||
|
static int unlock_luks1(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
|
struct luks1_phdr *hdr, const u8 *pass, size_t pass_len,
|
||||||
|
bool pre_derived, u8 *master_key, u32 *key_sizep)
|
||||||
{
|
{
|
||||||
uint version, split_key_size, km_blocks, hdr_blocks;
|
uint split_key_size, km_blocks, key_size;
|
||||||
u8 *split_key, *derived_key;
|
u8 *split_key, *derived_key;
|
||||||
struct hash_algo *hash_algo;
|
struct hash_algo *hash_algo;
|
||||||
u8 candidate_key[128], *km;
|
u8 candidate_key[128], *km;
|
||||||
mbedtls_md_type_t md_type;
|
mbedtls_md_type_t md_type;
|
||||||
struct luks1_phdr *hdr;
|
|
||||||
struct blk_desc *desc;
|
struct blk_desc *desc;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (!blk || !pinfo || !pass || !master_key || !key_size)
|
desc = dev_get_uclass_plat(blk);
|
||||||
|
|
||||||
|
/* Debug: show what we read from header */
|
||||||
|
log_debug("Read header at sector %llu, mk_digest[0-7] ",
|
||||||
|
(unsigned long long)pinfo->start);
|
||||||
|
log_debug_hex("", (u8 *)hdr->mk_digest, 8);
|
||||||
|
|
||||||
|
/* Verify cipher mode - only CBC supported */
|
||||||
|
if (strncmp(hdr->cipher_mode, "cbc", 3)) {
|
||||||
|
log_debug("only CBC mode is currently supported (got: %.32s)\n",
|
||||||
|
hdr->cipher_mode);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look up hash algorithm */
|
||||||
|
ret = hash_lookup_algo(hdr->hash_spec, &hash_algo);
|
||||||
|
if (ret) {
|
||||||
|
log_debug("unsupported hash: %.32s\n", hdr->hash_spec);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
md_type = hash_mbedtls_type(hash_algo);
|
||||||
|
key_size = be32_to_cpu(hdr->key_bytes);
|
||||||
|
|
||||||
|
/* Find the first active slot to get the stripes value */
|
||||||
|
u32 stripes = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
||||||
|
if (be32_to_cpu(hdr->key_slot[i].active) == LUKS_KEY_ENABLED) {
|
||||||
|
stripes = be32_to_cpu(hdr->key_slot[i].stripes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!stripes) {
|
||||||
|
log_debug("no active key slots found\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
split_key_size = key_size * stripes;
|
||||||
|
log_debug("Unlocking LUKS partition: key size: %u bytes\n", key_size);
|
||||||
|
|
||||||
|
/* Allocate buffers */
|
||||||
|
derived_key = malloc(key_size);
|
||||||
|
split_key = malloc(split_key_size);
|
||||||
|
km_blocks = (split_key_size + desc->blksz - 1) / desc->blksz;
|
||||||
|
km = malloc_cache_aligned(km_blocks * desc->blksz);
|
||||||
|
|
||||||
|
if (!derived_key || !split_key || !km) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If using pre-derived key, use it directly */
|
||||||
|
if (pre_derived) {
|
||||||
|
if (pass_len != key_size) {
|
||||||
|
log_debug("Pre-derived key size mismatch: got %zu, need %u\n",
|
||||||
|
pass_len, key_size);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(derived_key, pass, key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try each key slot */
|
||||||
|
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
||||||
|
struct luks1_keyslot *slot = &hdr->key_slot[i];
|
||||||
|
|
||||||
|
/* Skip inactive slots */
|
||||||
|
if (be32_to_cpu(slot->active) != LUKS_KEY_ENABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Derive key for this slot if not pre-derived */
|
||||||
|
if (!pre_derived) {
|
||||||
|
ret = derive_key_pbkdf2(slot, pass, pass_len, md_type,
|
||||||
|
key_size, derived_key);
|
||||||
|
if (ret)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to unlock with the derived key */
|
||||||
|
ret = try_keyslot(blk, pinfo, hdr, i, md_type, key_size,
|
||||||
|
derived_key, km, km_blocks, split_key,
|
||||||
|
candidate_key);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
/* Successfully unlocked */
|
||||||
|
memcpy(master_key, candidate_key, key_size);
|
||||||
|
*key_sizep = key_size;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* Continue trying other slots on failure */
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("Failed to unlock: wrong passphrase or no active key slots\n");
|
||||||
|
ret = -EACCES;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (derived_key) {
|
||||||
|
memset(derived_key, '\0', key_size);
|
||||||
|
free(derived_key);
|
||||||
|
}
|
||||||
|
if (split_key) {
|
||||||
|
memset(split_key, '\0', split_key_size);
|
||||||
|
free(split_key);
|
||||||
|
}
|
||||||
|
if (km) {
|
||||||
|
memset(km, '\0', km_blocks * desc->blksz);
|
||||||
|
free(km);
|
||||||
|
}
|
||||||
|
memset(candidate_key, '\0', sizeof(candidate_key));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int luks_unlock(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
|
const u8 *pass, size_t pass_len, bool pre_derived,
|
||||||
|
u8 *master_key, u32 *key_sizep)
|
||||||
|
{
|
||||||
|
uint version, hdr_blocks;
|
||||||
|
struct luks1_phdr *hdr;
|
||||||
|
struct blk_desc *desc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!blk || !pinfo || !pass || !master_key || !key_sizep)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
desc = dev_get_uclass_plat(blk);
|
desc = dev_get_uclass_plat(blk);
|
||||||
@@ -479,118 +623,26 @@ int luks_unlock(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
version = be16_to_cpu(*(__be16 *)(buffer + LUKS_MAGIC_LEN));
|
version = be16_to_cpu(*(__be16 *)(buffer + LUKS_MAGIC_LEN));
|
||||||
if (version == LUKS_VERSION_2)
|
switch (version) {
|
||||||
return unlock_luks2(blk, pinfo, pass, master_key, key_size);
|
case LUKS_VERSION_1:
|
||||||
|
hdr = (struct luks1_phdr *)buffer;
|
||||||
if (version != LUKS_VERSION_1) {
|
ret = unlock_luks1(blk, pinfo, hdr, pass, pass_len,
|
||||||
|
pre_derived, master_key, key_sizep);
|
||||||
|
break;
|
||||||
|
case LUKS_VERSION_2:
|
||||||
|
ret = unlock_luks2(blk, pinfo, pass, pass_len, pre_derived,
|
||||||
|
master_key, key_sizep);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
log_debug("unsupported LUKS version %d\n", version);
|
log_debug("unsupported LUKS version %d\n", version);
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
hdr = (struct luks1_phdr *)buffer;
|
return 0;
|
||||||
|
|
||||||
/* Debug: show what we read from header */
|
|
||||||
log_debug("Read header at sector %llu, mk_digest[0-7] ",
|
|
||||||
(unsigned long long)pinfo->start);
|
|
||||||
log_debug_hex("", (u8 *)hdr->mk_digest, 8);
|
|
||||||
|
|
||||||
/* Verify cipher mode - only CBC supported */
|
|
||||||
if (strncmp(hdr->cipher_mode, "cbc", 3) != 0) {
|
|
||||||
log_debug("only CBC mode is currently supported (got: %.32s)\n",
|
|
||||||
hdr->cipher_mode);
|
|
||||||
return -ENOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look up hash algorithm */
|
|
||||||
ret = hash_lookup_algo(hdr->hash_spec, &hash_algo);
|
|
||||||
if (ret) {
|
|
||||||
log_debug("unsupported hash: %.32s\n", hdr->hash_spec);
|
|
||||||
return -ENOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
md_type = hash_mbedtls_type(hash_algo);
|
|
||||||
|
|
||||||
*key_size = be32_to_cpu(hdr->key_bytes);
|
|
||||||
|
|
||||||
/* Find the first active slot to get the stripes value */
|
|
||||||
u32 stripes = 0;
|
|
||||||
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
|
||||||
if (be32_to_cpu(hdr->key_slot[i].active) == LUKS_KEY_ENABLED) {
|
|
||||||
stripes = be32_to_cpu(hdr->key_slot[i].stripes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!stripes) {
|
|
||||||
log_debug("no active key slots found\n");
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
split_key_size = *key_size * stripes;
|
|
||||||
|
|
||||||
log_debug("Trying to unlock LUKS partition: key size: %u bytes\n",
|
|
||||||
*key_size);
|
|
||||||
|
|
||||||
/* Allocate buffers */
|
|
||||||
derived_key = malloc(*key_size);
|
|
||||||
split_key = malloc(split_key_size);
|
|
||||||
km_blocks = (split_key_size + desc->blksz - 1) / desc->blksz;
|
|
||||||
km = malloc_cache_aligned(km_blocks * desc->blksz);
|
|
||||||
|
|
||||||
if (!derived_key || !split_key || !km) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try each key slot */
|
|
||||||
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
|
||||||
ret = try_keyslot(blk, pinfo, hdr, i, pass, md_type,
|
|
||||||
*key_size, derived_key, km, km_blocks,
|
|
||||||
split_key, candidate_key);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
/* Successfully unlocked */
|
|
||||||
memcpy(master_key, candidate_key, *key_size);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* Continue trying other slots on failure */
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("Failed to unlock: wrong passphrase or no active key slots\n");
|
|
||||||
ret = -EACCES;
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (derived_key) {
|
|
||||||
memset(derived_key, '\0', *key_size);
|
|
||||||
free(derived_key);
|
|
||||||
}
|
|
||||||
if (split_key) {
|
|
||||||
memset(split_key, '\0', split_key_size);
|
|
||||||
free(split_key);
|
|
||||||
}
|
|
||||||
if (km) {
|
|
||||||
memset(km, '\0', km_blocks * desc->blksz);
|
|
||||||
free(km);
|
|
||||||
}
|
|
||||||
memset(candidate_key, '\0', sizeof(candidate_key));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* luks_create_blkmap() - Create a blkmap device for a LUKS partition
|
|
||||||
*
|
|
||||||
* This creates and configures a blkmap device to provide access to the
|
|
||||||
* decrypted contents of a LUKS partition. The master key must already be
|
|
||||||
* unlocked using luks_unlock().
|
|
||||||
*
|
|
||||||
* @blk: Block device containing the LUKS partition
|
|
||||||
* @pinfo: Partition information
|
|
||||||
* @master_key: Unlocked master key
|
|
||||||
* @key_size: Size of the master key in bytes
|
|
||||||
* @label: Label for the blkmap device
|
|
||||||
* @blkmapp: Output pointer for created blkmap device
|
|
||||||
* Return: 0 on success, -ve on error
|
|
||||||
*/
|
|
||||||
int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
|
int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const u8 *master_key, u32 key_size, const char *label,
|
const u8 *master_key, u32 key_size, const char *label,
|
||||||
struct udevice **blkmapp)
|
struct udevice **blkmapp)
|
||||||
|
|||||||
@@ -454,69 +454,6 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* essiv_decrypt() - Decrypt key material using ESSIV mode
|
|
||||||
*
|
|
||||||
* ESSIV (Encrypted Salt-Sector Initialization Vector) mode generates a unique
|
|
||||||
* IV for each sector by encrypting the sector number with a key derived from
|
|
||||||
* hashing the encryption key.
|
|
||||||
*
|
|
||||||
* @derived_key: Key derived from passphrase
|
|
||||||
* @key_size: Size of the encryption key in bytes
|
|
||||||
* @expkey: Expanded AES key for decryption
|
|
||||||
* @km: Encrypted key material buffer
|
|
||||||
* @split_key: Output buffer for decrypted key material
|
|
||||||
* @km_blocks: Number of blocks of key material
|
|
||||||
* @blksz: Block size in bytes
|
|
||||||
*/
|
|
||||||
static void essiv_decrypt(u8 *derived_key, uint key_size, u8 *expkey,
|
|
||||||
u8 *km, u8 *split_key, uint km_blocks, uint blksz)
|
|
||||||
{
|
|
||||||
u8 essiv_expkey[AES256_EXPAND_KEY_LENGTH];
|
|
||||||
u8 essiv_key_material[SHA256_SUM_LEN];
|
|
||||||
u32 num_sectors = km_blocks;
|
|
||||||
u8 iv[AES_BLOCK_LENGTH];
|
|
||||||
uint rel_sect;
|
|
||||||
|
|
||||||
/* Generate ESSIV key by hashing the encryption key */
|
|
||||||
log_debug("using ESSIV mode\n");
|
|
||||||
sha256_csum_wd(derived_key, key_size, essiv_key_material,
|
|
||||||
CHUNKSZ_SHA256);
|
|
||||||
|
|
||||||
log_debug_hex("ESSIV key[0-7]:", essiv_key_material, 8);
|
|
||||||
|
|
||||||
/* Expand ESSIV key for AES */
|
|
||||||
aes_expand_key(essiv_key_material, 256, essiv_expkey);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decrypt each sector with its own IV
|
|
||||||
* NOTE: sector number is relative to the key material buffer,
|
|
||||||
* not an absolute disk sector
|
|
||||||
*/
|
|
||||||
for (rel_sect = 0; rel_sect < num_sectors; rel_sect++) {
|
|
||||||
u8 sector_iv[AES_BLOCK_LENGTH];
|
|
||||||
|
|
||||||
/* Create IV: little-endian sector number padded to 16 bytes */
|
|
||||||
memset(sector_iv, '\0', AES_BLOCK_LENGTH);
|
|
||||||
put_unaligned_le32(rel_sect, sector_iv);
|
|
||||||
|
|
||||||
/* Encrypt sector number with ESSIV key to get IV */
|
|
||||||
aes_encrypt(256, sector_iv, essiv_expkey, iv);
|
|
||||||
|
|
||||||
/* Show the first sector for debugging */
|
|
||||||
if (!rel_sect) {
|
|
||||||
log_debug("rel_sect %x, ", rel_sect);
|
|
||||||
log_debug_hex("IV[0-7]:", iv, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decrypt this sector */
|
|
||||||
aes_cbc_decrypt_blocks(key_size * 8, expkey, iv,
|
|
||||||
km + (rel_sect * blksz),
|
|
||||||
split_key + (rel_sect * blksz),
|
|
||||||
blksz / AES_BLOCK_LENGTH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* decrypt_km_xts() - Decrypt key material using XTS mode
|
* decrypt_km_xts() - Decrypt key material using XTS mode
|
||||||
*
|
*
|
||||||
@@ -607,9 +544,9 @@ static int decrypt_km_xts(const u8 *derived_key, uint key_size, const u8 *km,
|
|||||||
* @blksz: Block size in bytes
|
* @blksz: Block size in bytes
|
||||||
* Return: 0 on success, negative error code on failure
|
* Return: 0 on success, negative error code on failure
|
||||||
*/
|
*/
|
||||||
static int decrypt_km_cbc(u8 *derived_key, uint key_size, const char *encrypt,
|
static int decrypt_km_cbc(const u8 *derived_key, uint key_size,
|
||||||
u8 *km, u8 *split_key, int size, int km_blocks,
|
const char *encrypt, u8 *km, u8 *split_key,
|
||||||
int blksz)
|
int size, int km_blocks, int blksz)
|
||||||
{
|
{
|
||||||
u8 expkey[AES256_EXPAND_KEY_LENGTH];
|
u8 expkey[AES256_EXPAND_KEY_LENGTH];
|
||||||
|
|
||||||
@@ -649,8 +586,9 @@ static int decrypt_km_cbc(u8 *derived_key, uint key_size, const char *encrypt,
|
|||||||
* Return: 0 on success, negative error code on failure
|
* Return: 0 on success, negative error code on failure
|
||||||
*/
|
*/
|
||||||
static int try_keyslot_pbkdf2(struct udevice *blk, struct disk_partition *pinfo,
|
static int try_keyslot_pbkdf2(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const struct luks2_keyslot *ks, const char *pass,
|
const struct luks2_keyslot *ks, const u8 *pass,
|
||||||
mbedtls_md_type_t md_type, u8 *cand_key)
|
size_t pass_len, mbedtls_md_type_t md_type,
|
||||||
|
u8 *cand_key)
|
||||||
{
|
{
|
||||||
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
||||||
int ret, km_blocks, size;
|
int ret, km_blocks, size;
|
||||||
@@ -660,10 +598,10 @@ static int try_keyslot_pbkdf2(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
log_debug("LUKS2: trying keyslot with %u iters\n", ks->kdf.iters);
|
log_debug("LUKS2: trying keyslot with %u iters\n", ks->kdf.iters);
|
||||||
|
|
||||||
/* Derive key from passphrase */
|
/* Derive key from passphrase */
|
||||||
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, (const u8 *)pass,
|
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, pass, pass_len,
|
||||||
strlen(pass), ks->kdf.salt,
|
ks->kdf.salt, ks->kdf.salt_len,
|
||||||
ks->kdf.salt_len, ks->kdf.iters,
|
ks->kdf.iters, ks->area.key_size,
|
||||||
ks->area.key_size, derived_key);
|
derived_key);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
@@ -718,8 +656,8 @@ out:
|
|||||||
|
|
||||||
/* Unlock using Argon2 keyslot */
|
/* Unlock using Argon2 keyslot */
|
||||||
static int try_keyslot_argon2(struct udevice *blk, struct disk_partition *pinfo,
|
static int try_keyslot_argon2(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const struct luks2_keyslot *ks, const char *pass,
|
const struct luks2_keyslot *ks, const u8 *pass,
|
||||||
u8 *cand_key)
|
size_t pass_len, u8 *cand_key)
|
||||||
{
|
{
|
||||||
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
struct blk_desc *desc = dev_get_uclass_plat(blk);
|
||||||
int ret, km_blocks, size;
|
int ret, km_blocks, size;
|
||||||
@@ -730,15 +668,18 @@ static int try_keyslot_argon2(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
ks->kdf.time, ks->kdf.memory, ks->kdf.cpus);
|
ks->kdf.time, ks->kdf.memory, ks->kdf.cpus);
|
||||||
|
|
||||||
/* Derive key from passphrase using Argon2id */
|
/* Derive key from passphrase using Argon2id */
|
||||||
log_debug("LUKS2 Argon2: passphrase='%s', t=%u, m=%u, p=%u, saltlen=%d, keylen=%u\n",
|
log_debug("LUKS2 Argon2: pass_len=%zu, t=%u, m=%u, p=%u, saltlen=%d, keylen=%u\n",
|
||||||
pass, ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
|
pass_len, ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
|
||||||
ks->kdf.salt_len, ks->area.key_size);
|
ks->kdf.salt_len, ks->area.key_size);
|
||||||
ret = argon2id_hash_raw(ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
|
ret = argon2id_hash_raw(ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
|
||||||
pass, strlen(pass), ks->kdf.salt,
|
pass, pass_len, ks->kdf.salt,
|
||||||
ks->kdf.salt_len, derived_key,
|
ks->kdf.salt_len, derived_key,
|
||||||
ks->area.key_size);
|
ks->area.key_size);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
log_err("Argon2id failed: %s\n", argon2_error_message(ret));
|
log_err("Argon2id failed: %s (code=%d)\n",
|
||||||
|
argon2_error_message(ret), ret);
|
||||||
|
if (ret == ARGON2_MEMORY_ALLOCATION_ERROR)
|
||||||
|
return -ENOMEM;
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
log_debug("LUKS2 Argon2: key derivation succeeded\n");
|
log_debug("LUKS2 Argon2: key derivation succeeded\n");
|
||||||
@@ -873,7 +814,8 @@ static int verify_master_key(const struct luks2_digest *digest,
|
|||||||
*
|
*
|
||||||
* This function attempts to unlock one keyslot by:
|
* This function attempts to unlock one keyslot by:
|
||||||
* 1. Reading keyslot metadata from ofnode
|
* 1. Reading keyslot metadata from ofnode
|
||||||
* 2. Deriving the candidate master key using the appropriate KDF
|
* 2. Deriving the candidate master key using the appropriate KDF (or using
|
||||||
|
* pre-derived key directly)
|
||||||
* 3. Verifying the candidate key against the stored digest
|
* 3. Verifying the candidate key against the stored digest
|
||||||
*
|
*
|
||||||
* @blk: Block device containing the LUKS partition
|
* @blk: Block device containing the LUKS partition
|
||||||
@@ -881,7 +823,9 @@ static int verify_master_key(const struct luks2_digest *digest,
|
|||||||
* @keyslot_node: ofnode for this specific keyslot
|
* @keyslot_node: ofnode for this specific keyslot
|
||||||
* @digest: Digest information for verification
|
* @digest: Digest information for verification
|
||||||
* @md_type: mbedtls message digest type (for PBKDF2)
|
* @md_type: mbedtls message digest type (for PBKDF2)
|
||||||
* @pass: User-provided passphrase
|
* @pass: User-provided passphrase or pre-derived key
|
||||||
|
* @pass_len: Length of passphrase
|
||||||
|
* @pre_derived: True if pass is a pre-derived key, false for passphrase
|
||||||
* @master_key: Output buffer for verified master key
|
* @master_key: Output buffer for verified master key
|
||||||
* @key_sizep: Returns the key size
|
* @key_sizep: Returns the key size
|
||||||
* Return: 0 if unlocked successfully, -EAGAIN to continue trying, -ve on error
|
* Return: 0 if unlocked successfully, -EAGAIN to continue trying, -ve on error
|
||||||
@@ -889,7 +833,8 @@ static int verify_master_key(const struct luks2_digest *digest,
|
|||||||
static int try_unlock_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
static int try_unlock_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
ofnode keyslot_node,
|
ofnode keyslot_node,
|
||||||
const struct luks2_digest *digest,
|
const struct luks2_digest *digest,
|
||||||
mbedtls_md_type_t md_type, const char *pass,
|
mbedtls_md_type_t md_type, const u8 *pass,
|
||||||
|
size_t pass_len, bool pre_derived,
|
||||||
u8 *master_key, uint *key_sizep)
|
u8 *master_key, uint *key_sizep)
|
||||||
{
|
{
|
||||||
struct luks2_keyslot keyslot;
|
struct luks2_keyslot keyslot;
|
||||||
@@ -906,15 +851,27 @@ static int try_unlock_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
|
|
||||||
log_debug("LUKS2: trying keyslot (type=%d)\n", keyslot.kdf.type);
|
log_debug("LUKS2: trying keyslot (type=%d)\n", keyslot.kdf.type);
|
||||||
|
|
||||||
/* Try the keyslot using the appropriate KDF */
|
/* If using pre-derived key, use it directly */
|
||||||
if (keyslot.kdf.type == LUKS2_KDF_PBKDF2) {
|
if (pre_derived) {
|
||||||
log_debug("LUKS2: calling try_keyslot_pbkdf2\n");
|
if (pass_len != keyslot.key_size) {
|
||||||
ret = try_keyslot_pbkdf2(blk, pinfo, &keyslot, pass, md_type,
|
log_debug("Pre-derived key size mismatch: got %zu, need %u\n",
|
||||||
cand_key);
|
pass_len, keyslot.key_size);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
memcpy(cand_key, pass, pass_len);
|
||||||
|
ret = 0;
|
||||||
} else {
|
} else {
|
||||||
/* Argon2 (already checked for CONFIG_ARGON2 support) */
|
/* Try the keyslot using the appropriate KDF */
|
||||||
log_debug("LUKS2: calling try_keyslot_argon2\n");
|
if (keyslot.kdf.type == LUKS2_KDF_PBKDF2) {
|
||||||
ret = try_keyslot_argon2(blk, pinfo, &keyslot, pass, cand_key);
|
log_debug("LUKS2: calling try_keyslot_pbkdf2\n");
|
||||||
|
ret = try_keyslot_pbkdf2(blk, pinfo, &keyslot, pass, pass_len,
|
||||||
|
md_type, cand_key);
|
||||||
|
} else {
|
||||||
|
/* Argon2 (already checked for CONFIG_ARGON2 support) */
|
||||||
|
log_debug("LUKS2: calling try_keyslot_argon2\n");
|
||||||
|
ret = try_keyslot_argon2(blk, pinfo, &keyslot, pass, pass_len,
|
||||||
|
cand_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log_debug("LUKS2: keyslot try returned %d\n", ret);
|
log_debug("LUKS2: keyslot try returned %d\n", ret);
|
||||||
|
|
||||||
@@ -937,7 +894,8 @@ static int try_unlock_keyslot(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
|
int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const char *pass, u8 *master_key, uint *key_sizep)
|
const u8 *pass, size_t pass_len, bool pre_derived,
|
||||||
|
u8 *master_key, uint *key_sizep)
|
||||||
{
|
{
|
||||||
ofnode keyslots_node, keyslot_node;
|
ofnode keyslots_node, keyslot_node;
|
||||||
struct luks2_digest digest;
|
struct luks2_digest digest;
|
||||||
@@ -955,7 +913,8 @@ int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
|
|||||||
ret = -EACCES;
|
ret = -EACCES;
|
||||||
ofnode_for_each_subnode(keyslot_node, keyslots_node) {
|
ofnode_for_each_subnode(keyslot_node, keyslots_node) {
|
||||||
ret = try_unlock_keyslot(blk, pinfo, keyslot_node, &digest,
|
ret = try_unlock_keyslot(blk, pinfo, keyslot_node, &digest,
|
||||||
md_type, pass, master_key, key_sizep);
|
md_type, pass, pass_len, pre_derived,
|
||||||
|
master_key, key_sizep);
|
||||||
if (!ret) /* Successfully unlocked! */
|
if (!ret) /* Successfully unlocked! */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -27,17 +27,39 @@
|
|||||||
int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
|
int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
|
||||||
const char *hash_spec);
|
const char *hash_spec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* essiv_decrypt() - Decrypt key material using ESSIV mode
|
||||||
|
*
|
||||||
|
* ESSIV (Encrypted Salt-Sector Initialization Vector) mode generates a unique
|
||||||
|
* IV for each sector by encrypting the sector number with a key derived from
|
||||||
|
* hashing the encryption key. Used by both LUKS1 and LUKS2.
|
||||||
|
*
|
||||||
|
* @derived_key: Key derived from passphrase
|
||||||
|
* @key_size: Size of the encryption key in bytes
|
||||||
|
* @expkey: Expanded AES key for decryption
|
||||||
|
* @km: Encrypted key material buffer
|
||||||
|
* @split_key: Output buffer for decrypted key material
|
||||||
|
* @km_blocks: Number of blocks of key material
|
||||||
|
* @blksz: Block size in bytes
|
||||||
|
*/
|
||||||
|
void essiv_decrypt(const u8 *derived_key, uint key_size, u8 *expkey, u8 *km,
|
||||||
|
u8 *split_key, uint km_blocks, uint blksz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* unlock_luks2() - Unlock a LUKS2 partition with a passphrase
|
* unlock_luks2() - Unlock a LUKS2 partition with a passphrase
|
||||||
*
|
*
|
||||||
* @blk: Block device
|
* @blk: Block device
|
||||||
* @pinfo: Partition information
|
* @pinfo: Partition information
|
||||||
* @pass: Passphrase to unlock the partition
|
* @pass: Passphrase to unlock the partition or pre-derived key
|
||||||
|
* @pass_len: Length of the passphrase in bytes
|
||||||
|
* @pre_derived: True if pass is a pre-derived key, false for passphrase
|
||||||
* @master_key: Buffer to receive the decrypted master key
|
* @master_key: Buffer to receive the decrypted master key
|
||||||
* @key_sizep: Returns the key size
|
* @key_sizep: Returns the key size
|
||||||
* Return: 0 on success, -ve on error
|
* Return: 0 on success, -EACCES if no keyslots matched, other -ve on other
|
||||||
|
* error
|
||||||
*/
|
*/
|
||||||
int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
|
int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const char *pass, u8 *master_key, uint *key_sizep);
|
const u8 *pass, size_t pass_len, bool pre_derived,
|
||||||
|
u8 *master_key, uint *key_sizep);
|
||||||
|
|
||||||
#endif /* __LUKS_INTERNAL_H__ */
|
#endif /* __LUKS_INTERNAL_H__ */
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ int luks_show_info(struct udevice *blk, struct disk_partition *pinfo);
|
|||||||
* @blk: Block device
|
* @blk: Block device
|
||||||
* @pinfo: Partition information
|
* @pinfo: Partition information
|
||||||
* @pass: Passphrase to unlock the partition
|
* @pass: Passphrase to unlock the partition
|
||||||
|
* @pass_len: Length of the passphrase in bytes
|
||||||
* @master_key: Buffer to receive the decrypted master key
|
* @master_key: Buffer to receive the decrypted master key
|
||||||
* @key_size: Size of the master_key buffer
|
* @key_size: Size of the master_key buffer
|
||||||
* Return: 0 on success,
|
* Return: 0 on success,
|
||||||
@@ -157,7 +158,8 @@ int luks_show_info(struct udevice *blk, struct disk_partition *pinfo);
|
|||||||
* -EIO if failed to read from block device
|
* -EIO if failed to read from block device
|
||||||
*/
|
*/
|
||||||
int luks_unlock(struct udevice *blk, struct disk_partition *pinfo,
|
int luks_unlock(struct udevice *blk, struct disk_partition *pinfo,
|
||||||
const char *pass, u8 *master_key, u32 *key_size);
|
const u8 *pass, size_t pass_len, bool pre_derived,
|
||||||
|
u8 *master_key, u32 *key_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* luks_create_blkmap() - Create a blkmap device for a LUKS partition
|
* luks_create_blkmap() - Create a blkmap device for a LUKS partition
|
||||||
|
|||||||
@@ -274,8 +274,8 @@ static int bootstd_test_luks2_unlock(struct unit_test_state *uts)
|
|||||||
|
|
||||||
/* Test that unlock fails for partition 1 (not LUKS) */
|
/* Test that unlock fails for partition 1 (not LUKS) */
|
||||||
ut_assertok(part_get_info(desc, 1, &info));
|
ut_assertok(part_get_info(desc, 1, &info));
|
||||||
ut_asserteq(-ENOENT, luks_unlock(desc->bdev, &info, "test", master_key,
|
ut_asserteq(-ENOENT, luks_unlock(desc->bdev, &info, (const u8 *)"test",
|
||||||
&key_size));
|
4, false, master_key, &key_size));
|
||||||
|
|
||||||
/* Test unlocking partition 2 with correct passphrase */
|
/* Test unlocking partition 2 with correct passphrase */
|
||||||
ut_assertok(run_command("luks unlock mmc c:2 test", 0));
|
ut_assertok(run_command("luks unlock mmc c:2 test", 0));
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
# Static configuration data for pytest. pytest reads this at startup time.
|
# Static configuration data for pytest. pytest reads this at startup time.
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
|
addopts = -p no:labgrid
|
||||||
markers =
|
markers =
|
||||||
boardspec: U-Boot: Describes the set of boards a test can/can't run on.
|
boardspec: U-Boot: Describes the set of boards a test can/can't run on.
|
||||||
buildconfigspec: U-Boot: Describes Kconfig/config-header constraints.
|
buildconfigspec: U-Boot: Describes Kconfig/config-header constraints.
|
||||||
|
|||||||
Reference in New Issue
Block a user