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 2/2] bitlocker: Add clearkey option
Date: Sat,  4 Oct 2025 03:35:54 +0300	[thread overview]
Message-ID: <20251004003554.234647-3-kfirka3@gmail.com> (raw)
In-Reply-To: <20251004003554.234647-1-kfirka3@gmail.com>

In order to bypass getting the password.
---
 lib/keyslot_context.c     | 97 +++++++++++++++++++++++++++++++++++++++
 lib/keyslot_context.h     |  2 +
 lib/libcryptsetup.h       | 24 ++++++++++
 lib/libcryptsetup.sym     |  1 +
 lib/setup.c               | 24 +++++++++-
 src/cryptsetup.c          | 20 +++++---
 src/cryptsetup_arg_list.h |  2 +
 src/cryptsetup_args.h     |  1 +
 src/utils_arg_names.h     |  1 +
 9 files changed, 165 insertions(+), 7 deletions(-)

diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c
index 1b137823..a6df53fb 100644
--- a/lib/keyslot_context.c
+++ b/lib/keyslot_context.c
@@ -304,6 +304,68 @@ static int get_fvault2_volume_key_by_key(struct crypt_device *cd,
 	return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
 }
 
+/* Clearkey-specific functions */
+static int get_luks1_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	int keyslot __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)))
+{
+	/* LUKS1 does not support clearkey protection */
+	return -ENOTSUP;
+}
+
+static int get_luks2_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	int keyslot __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)))
+{
+	/* LUKS2 does not support clearkey protection */
+	return -ENOTSUP;
+}
+
+static int get_plain_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)))
+{
+	/* Plain does not support clearkey protection */
+	return -ENOTSUP;
+}
+
+static int get_bitlk_volume_key_by_clearkey(struct crypt_device *cd,
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	const struct bitlk_metadata *params,
+	struct volume_key **r_vk)
+{
+	/* For BitLocker clearkey, call BITLK_get_volume_key without passphrase */
+	return BITLK_get_volume_key(cd, NULL, 0, params, r_vk);
+}
+
+static int get_fvault2_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	const struct fvault2_params *params __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)))
+{
+	/* FVAULT2 does not support clearkey protection */
+	return -ENOTSUP;
+}
+
+static int get_verity_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)),
+	struct volume_key **r_signature __attribute__((unused)))
+{
+	/* Verity does not support clearkey protection */
+	return -ENOTSUP;
+}
+
+static int get_integrity_volume_key_by_clearkey(struct crypt_device *cd __attribute__((unused)),
+	struct crypt_keyslot_context *kc __attribute__((unused)),
+	struct volume_key **r_vk __attribute__((unused)))
+{
+	/* Integrity does not support clearkey protection */
+	return -ENOTSUP;
+}
+
 static int get_generic_signed_key_by_key(struct crypt_device *cd,
 	struct crypt_keyslot_context *kc,
 	struct volume_key **r_vk,
@@ -629,6 +691,26 @@ void crypt_keyslot_context_init_by_key_internal(struct crypt_keyslot_context *kc
 	crypt_keyslot_context_init_common(kc);
 }
 
+void crypt_keyslot_context_init_by_clearkey_internal(struct crypt_keyslot_context *kc)
+{
+	assert(kc);
+
+	kc->type = CRYPT_KC_TYPE_KEY;
+
+	kc->get_luks1_volume_key = get_luks1_volume_key_by_clearkey;
+	kc->get_luks2_volume_key = get_luks2_volume_key_by_clearkey;
+	kc->get_plain_volume_key = get_plain_volume_key_by_clearkey;
+	kc->get_bitlk_volume_key = get_bitlk_volume_key_by_clearkey;
+	kc->get_fvault2_volume_key = get_fvault2_volume_key_by_clearkey;
+	kc->get_verity_volume_key = get_verity_volume_key_by_clearkey;
+	kc->get_integrity_volume_key = get_integrity_volume_key_by_clearkey;
+	
+	kc->get_key_size = key_get_key_size;
+	kc->context_free = key_context_free;
+	crypt_keyslot_context_init_common(kc);
+}
+
+
 static void signed_key_context_free(struct crypt_keyslot_context *kc)
 {
 	assert(kc && kc->type == CRYPT_KC_TYPE_SIGNED_KEY);
@@ -1196,6 +1278,21 @@ CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_vk_in_keyring, 2, 7,
 	return _crypt_keyslot_context_init_by_vk_in_keyring(key_description, kc, false);
 }
 
+CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_clearkey, 2, 8, struct crypt_device *cd,
+	struct crypt_keyslot_context **kc)
+{
+	if (!kc)
+		return -EINVAL;
+
+	*kc = crypt_zalloc(sizeof(**kc));
+	if (!*kc)
+		return -ENOMEM;
+
+	crypt_keyslot_context_init_by_clearkey_internal(*kc);
+
+	return 0;
+}
+
 int crypt_keyslot_context_get_error(struct crypt_keyslot_context *kc)
 {
 	return kc ? kc->error : -EINVAL;
diff --git a/lib/keyslot_context.h b/lib/keyslot_context.h
index a2e42b20..f373bf8d 100644
--- a/lib/keyslot_context.h
+++ b/lib/keyslot_context.h
@@ -175,6 +175,8 @@ void crypt_keyslot_context_init_by_token_internal(struct crypt_keyslot_context *
 void crypt_keyslot_context_init_by_keyring_internal(struct crypt_keyslot_context *kc,
 	const char *key_description);
 
+void crypt_keyslot_context_init_by_clearkey_internal(struct crypt_keyslot_context *kc);
+
 const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc);
 
 #endif /* KEYSLOT_CONTEXT_H */
diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h
index 77eb29ef..07b18f70 100644
--- a/lib/libcryptsetup.h
+++ b/lib/libcryptsetup.h
@@ -1352,6 +1352,17 @@ int crypt_keyslot_context_init_by_vk_in_keyring(struct crypt_device *cd,
 	const char *key_description,
 	struct crypt_keyslot_context **kc);
 
+/**
+ * Initialize keyslot context for clear key.
+ *
+ * @param cd crypt device handle initialized to LUKS device context
+ * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEY
+ *
+ * @return zero on success or negative errno otherwise.
+ */
+int crypt_keyslot_context_init_by_clearkey(struct crypt_device *cd,
+	struct crypt_keyslot_context **kc);
+
 /**
  * Get error code per keyslot context from last failed call.
  *
@@ -1823,6 +1834,19 @@ int crypt_activate_by_keyring(struct crypt_device *cd,
 	int keyslot,
 	uint32_t flags);
 
+/**
+ * Activate BITLK device using clearkey.
+ *
+ * @param cd crypt device handle
+ * @param name name of device to create
+ * @param flags activation flags
+ *
+ * @return @e 0 on success or negative errno value otherwise.
+ */
+int crypt_activate_by_clearkey(struct crypt_device *cd,
+	const char *name,
+	uint32_t flags);
+
 /** lazy deactivation - remove once last user releases it */
 #define CRYPT_DEACTIVATE_DEFERRED (UINT32_C(1) << 0)
 /** force deactivation - if the device is busy, it is replaced by error device */
diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym
index 3a54431a..f6463af2 100644
--- a/lib/libcryptsetup.sym
+++ b/lib/libcryptsetup.sym
@@ -61,6 +61,7 @@ CRYPTSETUP_2.0 {
 		crypt_activate_by_volume_key;
 		crypt_activate_by_signed_key;
 		crypt_activate_by_keyring;
+		crypt_activate_by_clearkey;
 		crypt_deactivate;
 		crypt_deactivate_by_name;
 		crypt_volume_key_get;
diff --git a/lib/setup.c b/lib/setup.c
index 37e6f7d9..1be05cfe 100644
--- a/lib/setup.c
+++ b/lib/setup.c
@@ -5707,6 +5707,20 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
 	return r;
 }
 
+int crypt_activate_by_clearkey(struct crypt_device *cd,
+	const char *name,
+	uint32_t flags)
+{
+	int r;
+	struct crypt_keyslot_context kc = {};
+
+	crypt_keyslot_context_init_by_clearkey_internal(&kc);
+	r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc, CRYPT_ANY_SLOT, &kc, flags);
+	crypt_keyslot_context_destroy_internal(&kc);
+
+	return r;
+}
+
 int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t flags)
 {
 	struct crypt_device *fake_cd = NULL;
@@ -5917,7 +5931,7 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd,
 	struct volume_key *vk = NULL;
 
 	if (!cd || !volume_key || !volume_key_size ||
-	    (!kc && !isLUKS(cd->type) && !isTCRYPT(cd->type) && !isVERITY(cd->type)))
+	    (!kc && !isLUKS(cd->type) && !isTCRYPT(cd->type) && !isVERITY(cd->type) && !isBITLK(cd->type)))
 		return -EINVAL;
 
 	if (isLUKS2(cd->type) && keyslot != CRYPT_ANY_SLOT)
@@ -5977,6 +5991,14 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd,
 	} else if (isBITLK(cd->type)) {
 		if (kc && kc->get_bitlk_volume_key)
 			r = kc->get_bitlk_volume_key(cd, kc, &cd->u.bitlk.params, &vk);
+		else if (!kc) {
+			struct crypt_keyslot_context *kc_clearkey = NULL;
+			r = crypt_keyslot_context_init_by_clearkey(cd, &kc_clearkey);
+			if (r >= 0) {
+				r = kc_clearkey->get_bitlk_volume_key(cd, kc_clearkey, &cd->u.bitlk.params, &vk);
+			}
+			crypt_keyslot_context_free(kc_clearkey);
+		}
 		if (r < 0)
 			log_err(cd, _("Cannot retrieve volume key for BITLK device."));
 	} else if (isFVAULT2(cd->type)) {
diff --git a/src/cryptsetup.c b/src/cryptsetup.c
index bd2ee780..134162c6 100644
--- a/src/cryptsetup.c
+++ b/src/cryptsetup.c
@@ -508,6 +508,11 @@ static int action_open_bitlk(void)
 			goto out;
 		r = crypt_activate_by_volume_key(cd, activated_name,
 						 key, keysize, activate_flags);
+	} else if (ARG_SET(OPT_CLEARKEY_ID)) {
+		/* For clearkey, we don't need a passphrase - pass NULL to activate_by_passphrase */
+		r = crypt_activate_by_clearkey(cd, activated_name, activate_flags);
+		if (r < 0)
+			log_err(_("No clearkey protection found on BITLK device."));
 	} else {
 		tries = set_tries_tty(false);
 		do {
@@ -617,14 +622,17 @@ static int bitlkDump_with_volume_key(struct crypt_device *cd)
 	if (!vk)
 		return -ENOMEM;
 
-	r = tools_get_key(NULL, &password, &passwordLen,
-			  ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
-			  ARG_UINT32(OPT_TIMEOUT_ID), 0, 0, cd);
-	if (r < 0)
-		goto out;
+	if (!ARG_SET(OPT_CLEARKEY_ID)) {
+		r = tools_get_key(NULL, &password, &passwordLen,
+				  ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+				  ARG_UINT32(OPT_TIMEOUT_ID), 0, 0, cd);
+		if (r < 0)
+			goto out;
+	}		
 
 	r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, vk, &vk_size,
-				 password, passwordLen);
+					password, passwordLen);
+
 	tools_passphrase_msg(r);
 	check_signal(&r);
 	if (r < 0)
diff --git a/src/cryptsetup_arg_list.h b/src/cryptsetup_arg_list.h
index 26652dd8..95b2c991 100644
--- a/src/cryptsetup_arg_list.h
+++ b/src/cryptsetup_arg_list.h
@@ -241,6 +241,8 @@ ARG(OPT_USE_DIRECTIO, '\0', POPT_ARG_NONE, N_("Use direct-io when accessing devi
 ARG(OPT_USE_FSYNC, '\0', POPT_ARG_NONE, N_("Use fsync after each block"), NULL, CRYPT_ARG_BOOL, {}, {})
 
 ARG(OPT_WRITE_LOG, '\0', POPT_ARG_NONE, N_("Update log file after every block"), NULL, CRYPT_ARG_BOOL, {}, {})
+ 
+ARG(OPT_CLEARKEY, '\0', POPT_ARG_NONE, N_("Open BITLK device using only clearkey protection"), NULL, CRYPT_ARG_BOOL, {}, {})
 
 /* aliases */
 
diff --git a/src/cryptsetup_args.h b/src/cryptsetup_args.h
index 43926f99..3ef58b61 100644
--- a/src/cryptsetup_args.h
+++ b/src/cryptsetup_args.h
@@ -43,6 +43,7 @@
 /* avoid unshielded commas in ARG() macros later */
 #define OPT_ALIGN_PAYLOAD_ACTIONS		{ FORMAT_ACTION, REENCRYPT_ACTION }
 #define OPT_ALLOW_DISCARDS_ACTIONS		{ OPEN_ACTION }
+#define OPT_CLEARKEY_ACTIONS			{ OPEN_ACTION, BITLKDUMP_ACTION }
 #define OPT_DEFERRED_ACTIONS			{ CLOSE_ACTION }
 #define OPT_DEVICE_SIZE_ACTIONS			{ OPEN_ACTION, RESIZE_ACTION, REENCRYPT_ACTION }
 #define OPT_DISABLE_BLKID_ACTIONS		{ FORMAT_ACTION, REENCRYPT_ACTION }
diff --git a/src/utils_arg_names.h b/src/utils_arg_names.h
index a01419d3..f50c26dc 100644
--- a/src/utils_arg_names.h
+++ b/src/utils_arg_names.h
@@ -20,6 +20,7 @@
 #define OPT_CANCEL_DEFERRED		"cancel-deferred"
 #define OPT_CHECK_AT_MOST_ONCE		"check-at-most-once"
 #define OPT_CIPHER			"cipher"
+#define OPT_CLEARKEY			"clearkey"
 #define OPT_DATA_BLOCK_SIZE		"data-block-size"
 #define OPT_DATA_BLOCKS			"data-blocks"
 #define OPT_DATA_DEVICE			"data-device"
-- 
2.43.0


  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 ` [PATCH 1/2] bitlocker: " Kfir Kahanov
2025-10-04  0:35 ` Kfir Kahanov [this message]
2025-10-04 18:17 ` [PATCH 0/2] Bitlocker: " 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-3-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