From: Kfir Kahanov <kfirka3@gmail.com>
To: cryptsetup@lists.linux.dev
Cc: Kfir Kahanov <kfirka3@gmail.com>
Subject: [PATCH 1/2] bitlocker: Support clearkey
Date: Sat, 4 Oct 2025 03:35:53 +0300 [thread overview]
Message-ID: <20251004003554.234647-2-kfirka3@gmail.com> (raw)
In-Reply-To: <20251004003554.234647-1-kfirka3@gmail.com>
Clearkey does not mean partially encrypted, and may be on fully
encrypted volumes.
---
lib/bitlk/bitlk.c | 106 ++++++++++++++++++++++++++++++----------------
1 file changed, 69 insertions(+), 37 deletions(-)
diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c
index bc3a55bb..11edd7d9 100644
--- a/lib/bitlk/bitlk.c
+++ b/lib/bitlk/bitlk.c
@@ -264,10 +264,11 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
bool supported = false;
int r = 0;
- /* only passphrase or recovery passphrase vmks are supported (can be used to activate) */
+ /* only passphrase, recovery passphrase, startup key and clearkey vmks are supported (can be used to activate) */
supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE ||
(*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE ||
- (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY;
+ (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY ||
+ (*vmk)->protection == BITLK_PROTECTION_CLEAR_KEY;
while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) {
/* size of this entry */
@@ -324,17 +325,13 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
crypt_volume_key_add_next(&((*vmk)->vk), vk);
/* clear key for a partially decrypted volume */
} else if (key_entry_value == BITLK_ENTRY_VALUE_KEY) {
- /* We currently don't want to support opening a partially decrypted
- * device so we don't need to store this key.
- *
- * key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4);
- * key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4;
- * vk = crypt_alloc_volume_key(key_size, key);
- * if (vk == NULL)
- * return -ENOMEM;
- * crypt_volume_key_add_next(&((*vmk)->vk), vk);
- */
- log_dbg(cd, "Skipping clear key metadata entry.");
+ /* For clearkey protection, we need to store this key */
+ key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4);
+ key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4;
+ vk = crypt_alloc_volume_key(key_size, key);
+ if (vk == NULL)
+ return -ENOMEM;
+ crypt_volume_key_add_next(&((*vmk)->vk), vk);
/* unknown timestamps in recovery protected VMK */
} else if (key_entry_value == BITLK_ENTRY_VALUE_RECOVERY_TIME) {
;
@@ -1249,6 +1246,43 @@ out:
return r;
}
+static int get_clear_key(struct crypt_device *cd, const struct bitlk_vmk *vmk, struct volume_key **vmk_dec_key)
+{
+ struct volume_key *nested_key = vmk->vk; // First key in chain
+
+ if (!nested_key) {
+ log_dbg(cd, "Clearkey VMK structure incomplete - missing nested key");
+ return -ENOTSUP;
+ }
+
+ struct volume_key *encrypted_vmk = crypt_volume_key_next(nested_key); // Second key in chain
+
+ if (!encrypted_vmk) {
+ log_dbg(cd, "Clearkey VMK structure incomplete - missing encrypted VMK");
+ return -ENOTSUP;
+ }
+
+ log_dbg(cd, "Clearkey VMK structure: nested_key=%zu bytes, encrypted_vmk=%zu bytes",
+ crypt_volume_key_length(nested_key),
+ crypt_volume_key_length(encrypted_vmk));
+
+ /* For clearkey protection, we need to decrypt the encrypted VMK using the nested key */
+ /* and return the decrypted VMK as vmk_dec_key */
+ struct volume_key *decrypted_vmk = NULL;
+ int r = decrypt_key(cd, &decrypted_vmk, encrypted_vmk, nested_key,
+ vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE,
+ vmk->nonce, BITLK_NONCE_SIZE, false);
+
+ if (r == 0 && decrypted_vmk) {
+ log_dbg(cd, "Successfully decrypted VMK using nested key");
+ *vmk_dec_key = decrypted_vmk;
+ return 0;
+ } else {
+ log_dbg(cd, "Failed to decrypt VMK using nested key (error: %d)", r);
+ return r;
+ }
+}
+
int BITLK_get_volume_key(struct crypt_device *cd,
const char *password,
size_t passwordLen,
@@ -1261,6 +1295,8 @@ int BITLK_get_volume_key(struct crypt_device *cd,
struct volume_key *recovery_key = NULL;
struct bitlk_validation_hash dec_hash = {};
const struct bitlk_vmk *next_vmk = NULL;
+ bool is_decrypted = false;
+
next_vmk = params->vmks;
while (next_vmk) {
@@ -1298,8 +1334,18 @@ int BITLK_get_volume_key(struct crypt_device *cd,
continue;
}
log_dbg(cd, "Trying to use external key found in provided password.");
+ } else if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) {
+ r = get_clear_key(cd, next_vmk, &vmk_dec_key);
+ if (r) {
+ /* something wrong happened, but we still want to check other key slots */
+ next_vmk = next_vmk->next;
+ continue;
+ }
+ is_decrypted = true;
+ open_vmk_key = vmk_dec_key;
+ log_dbg(cd, "Extracted VMK using clearkey.\n");
} else {
- /* only passphrase, recovery passphrase and startup key VMKs supported right now */
+ /* only passphrase, recovery passphrase, startup key and clearkey VMKs supported right now */
log_dbg(cd, "Skipping %s", get_vmk_protection_string(next_vmk->protection));
next_vmk = next_vmk->next;
if (r == 0)
@@ -1308,19 +1354,22 @@ int BITLK_get_volume_key(struct crypt_device *cd,
continue;
}
- log_dbg(cd, "Trying to decrypt %s.", get_vmk_protection_string(next_vmk->protection));
- r = decrypt_key(cd, &open_vmk_key, next_vmk->vk, vmk_dec_key,
- next_vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE,
- next_vmk->nonce, BITLK_NONCE_SIZE, false);
+ if (!is_decrypted) {
+ r = decrypt_key(cd, &open_vmk_key, next_vmk->vk, vmk_dec_key,
+ next_vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE,
+ next_vmk->nonce, BITLK_NONCE_SIZE, false);
+
+ crypt_free_volume_key(vmk_dec_key);
+ is_decrypted = true;
+ }
if (r < 0) {
log_dbg(cd, "Failed to decrypt VMK using provided passphrase.");
- crypt_free_volume_key(vmk_dec_key);
+
if (r == -ENOTSUP)
return r;
next_vmk = next_vmk->next;
continue;
}
- crypt_free_volume_key(vmk_dec_key);
log_dbg(cd, "Trying to decrypt validation metadata using VMK.");
r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(open_vmk_key),
@@ -1379,8 +1428,6 @@ int BITLK_get_volume_key(struct crypt_device *cd,
static int _activate_check(struct crypt_device *cd,
const struct bitlk_metadata *params)
{
- const struct bitlk_vmk *next_vmk = NULL;
-
if (!params->state) {
log_err(cd, _("This BITLK device is in an unsupported state and cannot be activated."));
return -ENOTSUP;
@@ -1391,15 +1438,6 @@ static int _activate_check(struct crypt_device *cd,
return -ENOTSUP;
}
- next_vmk = params->vmks;
- while (next_vmk) {
- if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) {
- log_err(cd, _("Activation of BITLK device with clear key protection is not supported."));
- return -ENOTSUP;
- }
- next_vmk = next_vmk->next;
- }
-
return 0;
}
@@ -1589,11 +1627,5 @@ int BITLK_activate_by_volume_key(struct crypt_device *cd,
const struct bitlk_metadata *params,
uint32_t flags)
{
- int r;
-
- r = _activate_check(cd, params);
- if (r)
- return r;
-
return _activate(cd, name, vk, params, flags);
}
--
2.43.0
next prev parent reply other threads:[~2025-10-04 0:36 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-04 0:35 [PATCH 0/2] Bitlocker: Support clearkey Kfir Kahanov
2025-10-04 0:35 ` Kfir Kahanov [this message]
2025-10-04 0:35 ` [PATCH 2/2] bitlocker: Add clearkey option Kfir Kahanov
2025-10-04 18:17 ` [PATCH 0/2] Bitlocker: Support clearkey Milan Broz
2025-10-04 19:28 ` Kfir Ka
2025-10-04 19:46 ` Milan Broz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251004003554.234647-2-kfirka3@gmail.com \
--to=kfirka3@gmail.com \
--cc=cryptsetup@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox