public inbox for cryptsetup@lists.linux.dev
 help / color / mirror / Atom feed
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


  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