Merge branch 'secg' into 'master'

luks: Integrate support for a TKey

See merge request u-boot/u-boot!211
This commit is contained in:
Simon Glass
2025-11-17 17:32:41 +00:00
8 changed files with 440 additions and 296 deletions

View File

@@ -8,8 +8,11 @@
#include <blk.h>
#include <command.h>
#include <dm.h>
#include <hexdump.h>
#include <luks.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,
char *const argv[])
@@ -57,18 +60,86 @@ static int do_luks_info(struct cmd_tbl *cmdtp, int flag, int argc,
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,
char *const argv[])
{
struct blk_desc *dev_desc;
struct disk_partition info;
struct udevice *blkmap_dev;
const char *passphrase;
const char *passphrase = NULL;
bool use_tkey = false;
int part, ret, version;
u8 master_key[128];
char label[64];
u32 key_size;
/* Check for -t flag */
if (!strcmp(argv[1], "-t")) {
use_tkey = true;
argc--;
argv++;
}
if (argc != 4)
return CMD_RET_USAGE;
@@ -78,6 +149,10 @@ static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
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 */
version = luks_get_version(dev_desc->bdev, &info);
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);
/* Unlock the partition to get the master key */
ret = luks_unlock(dev_desc->bdev, &info, passphrase, master_key,
&key_size);
if (use_tkey) {
ret = unlock_with_tkey(dev_desc, &info, passphrase, master_key,
&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) {
printf("Failed to unlock LUKS partition (err %dE)\n", ret);
return CMD_RET_FAILURE;
@@ -121,10 +202,11 @@ cleanup:
static char luks_help_text[] =
"detect <interface> <dev[:part]> - detect if partition is LUKS encrypted\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",
luks_help_text,
U_BOOT_SUBCMD_MKENT(detect, 3, 1, do_luks_detect),
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));

View File

@@ -13,7 +13,7 @@ Synopsis
luks detect <interface> <dev[:part]>
luks info <interface> <dev[:part]>
luks unlock <interface> <dev[:part]> <passphrase>
luks unlock [-t] <interface> <dev[:part]> <passphrase>
Description
-----------
@@ -88,12 +88,17 @@ dev[:part]
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)
2. Parses LUKS2 JSON metadata (if LUKS2) using FDT conversion
3. Derives the encryption key using PBKDF2 or Argon2id with the provided
passphrase
3. Derives the encryption key:
- **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
5. Verifies the master key against the stored digest
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
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
The storage interface type (e.g., mmc, usb, scsi)
@@ -125,7 +135,8 @@ dev[:part]
The device number and optional partition number
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.
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}
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
-------------
@@ -254,6 +276,10 @@ For Argon2id support (modern LUKS2 KDF)::
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
------------

View File

@@ -195,20 +195,6 @@ static int af_hash(struct hash_algo *algo, size_t key_size, u8 *block_buf)
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,
const char *hash_spec)
{
@@ -250,23 +236,8 @@ int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
return 0;
}
/**
* 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)
void essiv_decrypt(const 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];
@@ -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
* @pinfo: Partition information
* @hdr: LUKS header
* @slot_idx: Key slot index to try
* @pass: Passphrase to try
* @slot: LUKS keyslot containing salt and iteration count
* @pass: Passphrase
* @pass_len: Length of passphrase
* @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)
* @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 passphrase), -EPROTO on mbedtls error, -ve on
* other error
* Return: 0 on success, -EPROTO on error
*/
static int try_keyslot(struct udevice *blk, struct disk_partition *pinfo,
struct luks1_phdr *hdr, int slot_idx,
const char *pass, mbedtls_md_type_t md_type,
uint key_size, u8 *derived_key, u8 *km, uint km_blocks,
u8 *split_key, u8 *candidate_key)
static int derive_key_pbkdf2(struct luks1_keyslot *slot, const u8 *pass,
size_t pass_len, mbedtls_md_type_t md_type,
uint key_size, u8 *derived_key)
{
struct luks1_keyslot *slot = &hdr->key_slot[slot_idx];
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];
uint iters = be32_to_cpu(slot->iterations);
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 */
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("iter %u, keylen %u)\n", iters, key_size);
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, (const u8 *)pass,
strlen(pass),
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, pass, pass_len,
(const u8 *)slot->salt,
LUKS_SALTSIZE, iters, key_size,
derived_key);
LUKS_SALTSIZE, iters,
key_size, derived_key);
if (ret) {
log_debug("PBKDF2 failed: %d\n", ret);
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);
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 */
ret = blk_read(blk, pinfo->start + km_offset, km_blocks, km);
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 */
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);
log_debug_hex("expanded key [0-15]:", expkey, 16);
/* 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);
} else {
/* Plain CBC with zero IV */
log_debug("using plain CBC mode\n");
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,
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 */
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;
}
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;
}
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;
struct hash_algo *hash_algo;
u8 candidate_key[128], *km;
mbedtls_md_type_t md_type;
struct luks1_phdr *hdr;
struct blk_desc *desc;
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;
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));
if (version == LUKS_VERSION_2)
return unlock_luks2(blk, pinfo, pass, master_key, key_size);
if (version != LUKS_VERSION_1) {
switch (version) {
case LUKS_VERSION_1:
hdr = (struct luks1_phdr *)buffer;
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);
return -ENOTSUPP;
}
if (ret)
return ret;
hdr = (struct luks1_phdr *)buffer;
/* 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;
return 0;
}
/**
* 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,
const u8 *master_key, u32 key_size, const char *label,
struct udevice **blkmapp)

View File

@@ -454,69 +454,6 @@ out:
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
*
@@ -607,9 +544,9 @@ static int decrypt_km_xts(const u8 *derived_key, uint key_size, const u8 *km,
* @blksz: Block size in bytes
* Return: 0 on success, negative error code on failure
*/
static int decrypt_km_cbc(u8 *derived_key, uint key_size, const char *encrypt,
u8 *km, u8 *split_key, int size, int km_blocks,
int blksz)
static int decrypt_km_cbc(const u8 *derived_key, uint key_size,
const char *encrypt, u8 *km, u8 *split_key,
int size, int km_blocks, int blksz)
{
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
*/
static int try_keyslot_pbkdf2(struct udevice *blk, struct disk_partition *pinfo,
const struct luks2_keyslot *ks, const char *pass,
mbedtls_md_type_t md_type, u8 *cand_key)
const struct luks2_keyslot *ks, const u8 *pass,
size_t pass_len, mbedtls_md_type_t md_type,
u8 *cand_key)
{
struct blk_desc *desc = dev_get_uclass_plat(blk);
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);
/* Derive key from passphrase */
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, (const u8 *)pass,
strlen(pass), ks->kdf.salt,
ks->kdf.salt_len, ks->kdf.iters,
ks->area.key_size, derived_key);
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, pass, pass_len,
ks->kdf.salt, ks->kdf.salt_len,
ks->kdf.iters, ks->area.key_size,
derived_key);
if (ret)
return -EPROTO;
@@ -718,8 +656,8 @@ out:
/* Unlock using Argon2 keyslot */
static int try_keyslot_argon2(struct udevice *blk, struct disk_partition *pinfo,
const struct luks2_keyslot *ks, const char *pass,
u8 *cand_key)
const struct luks2_keyslot *ks, const u8 *pass,
size_t pass_len, u8 *cand_key)
{
struct blk_desc *desc = dev_get_uclass_plat(blk);
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);
/* Derive key from passphrase using Argon2id */
log_debug("LUKS2 Argon2: passphrase='%s', t=%u, m=%u, p=%u, saltlen=%d, keylen=%u\n",
pass, ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
log_debug("LUKS2 Argon2: pass_len=%zu, t=%u, m=%u, p=%u, saltlen=%d, keylen=%u\n",
pass_len, ks->kdf.time, ks->kdf.memory, ks->kdf.cpus,
ks->kdf.salt_len, ks->area.key_size);
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->area.key_size);
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;
}
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:
* 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
*
* @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
* @digest: Digest information for verification
* @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
* @key_sizep: Returns the key size
* 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,
ofnode keyslot_node,
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)
{
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);
/* Try the keyslot using the appropriate KDF */
if (keyslot.kdf.type == LUKS2_KDF_PBKDF2) {
log_debug("LUKS2: calling try_keyslot_pbkdf2\n");
ret = try_keyslot_pbkdf2(blk, pinfo, &keyslot, pass, md_type,
cand_key);
/* If using pre-derived key, use it directly */
if (pre_derived) {
if (pass_len != keyslot.key_size) {
log_debug("Pre-derived key size mismatch: got %zu, need %u\n",
pass_len, keyslot.key_size);
return -EAGAIN;
}
memcpy(cand_key, pass, pass_len);
ret = 0;
} else {
/* Argon2 (already checked for CONFIG_ARGON2 support) */
log_debug("LUKS2: calling try_keyslot_argon2\n");
ret = try_keyslot_argon2(blk, pinfo, &keyslot, pass, cand_key);
/* Try the keyslot using the appropriate KDF */
if (keyslot.kdf.type == LUKS2_KDF_PBKDF2) {
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);
@@ -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,
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;
struct luks2_digest digest;
@@ -955,7 +913,8 @@ int unlock_luks2(struct udevice *blk, struct disk_partition *pinfo,
ret = -EACCES;
ofnode_for_each_subnode(keyslot_node, keyslots_node) {
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! */
break;

View File

@@ -27,17 +27,39 @@
int af_merge(const u8 *src, u8 *dst, size_t key_size, uint stripes,
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
*
* @blk: Block device
* @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
* @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,
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__ */

View File

@@ -146,6 +146,7 @@ int luks_show_info(struct udevice *blk, struct disk_partition *pinfo);
* @blk: Block device
* @pinfo: Partition information
* @pass: Passphrase to unlock the partition
* @pass_len: Length of the passphrase in bytes
* @master_key: Buffer to receive the decrypted master key
* @key_size: Size of the master_key buffer
* 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
*/
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

View File

@@ -274,8 +274,8 @@ static int bootstd_test_luks2_unlock(struct unit_test_state *uts)
/* Test that unlock fails for partition 1 (not LUKS) */
ut_assertok(part_get_info(desc, 1, &info));
ut_asserteq(-ENOENT, luks_unlock(desc->bdev, &info, "test", master_key,
&key_size));
ut_asserteq(-ENOENT, luks_unlock(desc->bdev, &info, (const u8 *)"test",
4, false, master_key, &key_size));
/* Test unlocking partition 2 with correct passphrase */
ut_assertok(run_command("luks unlock mmc c:2 test", 0));

View File

@@ -5,6 +5,7 @@
# Static configuration data for pytest. pytest reads this at startup time.
[pytest]
addopts = -p no:labgrid
markers =
boardspec: U-Boot: Describes the set of boards a test can/can't run on.
buildconfigspec: U-Boot: Describes Kconfig/config-header constraints.