From patchwork Fri Oct 31 06:54:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 664 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893765; bh=xw7hoFgCHGu3FVduXhJhoj95RdV1yPbDSwzG/LNHdD8=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=JJ4xNlPKG3FWH2Sht8yDHOlQmN7o4LMdUXSjid3AKNUSk8xvsBFrwBFGWpmWsjzDQ 0t+LavBUbb4S8yS4Lb8Jt0sMlLyEkXQW+6ujnzcd12wgcE5ELlMAAOVseAn0JI0d1M VrhJ3amc8pfEBK4raYSKP0cQE3PfrSoboU/Qx5NIui2Lps1C2gSUj9znWEEq6lN5AF HFDN0ujhPY3DvN5DC8hOeAj+58KA3GzWsl/VyRH4cFP6Bq3d++Em1+czN1qbnBJtyf jyMTT3UjrULmd2R7XCI49ua9P+QQjlTOO1b+nzWlL3HtrH+txk/qPJuJRbWyS5ddKM Pb/asEUtFoenw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 24EDB68362 for ; Fri, 31 Oct 2025 00:56:05 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id VbLA19QMQ8Ex for ; Fri, 31 Oct 2025 00:56:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893765; bh=xw7hoFgCHGu3FVduXhJhoj95RdV1yPbDSwzG/LNHdD8=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=JJ4xNlPKG3FWH2Sht8yDHOlQmN7o4LMdUXSjid3AKNUSk8xvsBFrwBFGWpmWsjzDQ 0t+LavBUbb4S8yS4Lb8Jt0sMlLyEkXQW+6ujnzcd12wgcE5ELlMAAOVseAn0JI0d1M VrhJ3amc8pfEBK4raYSKP0cQE3PfrSoboU/Qx5NIui2Lps1C2gSUj9znWEEq6lN5AF HFDN0ujhPY3DvN5DC8hOeAj+58KA3GzWsl/VyRH4cFP6Bq3d++Em1+czN1qbnBJtyf jyMTT3UjrULmd2R7XCI49ua9P+QQjlTOO1b+nzWlL3HtrH+txk/qPJuJRbWyS5ddKM Pb/asEUtFoenw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 13D7E68321 for ; Fri, 31 Oct 2025 00:56:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893762; bh=dmeuneN5tEI4bjU7cqAnzU2VoQMvZEx8kTnGTLfGns8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=krwpfVrQpUGm5IX84kFHMC4S7hXCNNzgWsy8xcBcDP0/vuEv6MLpz+AtN01XeT2m3 urllQXvEvUkewrzF0QwccWiWZ/Vailq+UM6CQrmC4jz6hAAdlnOV9yRN6LYT9/NWG2 /0ng6M+H/iQD6HD3a6IfhMlaSIZtkDGUc0nGL6+3i/CXSkGSo7zZInQBUvcwAC/38T fjn6z5pwNqNNwzMHun3+XIF5aiLyG+EhLWFysK+N728JJJuIoBJJK276JALGjKzybY SOwsiAffu+h3wjnd8e8X1f3cHMxDEQ7EJuBfXY+fCG+39k+SlSdv/gVhjkSFz3njk7 fnP9x/TqRF10g== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 802CD5FBA9; Fri, 31 Oct 2025 00:56:02 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id hUwr51h8RvH0; Fri, 31 Oct 2025 00:56:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893758; bh=wr2fcKvmvtdwW4lq7DKXr1GGHhQHhpvJ1D3FsKuXW94=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hG4HRX2fVWM+KUelwaxn3pijLi6A+sEvgLFVO/LAjb26yn0VnQX9Ms38C2pkWaCZz Smh+Vszh0EznfOUGFxy3WEEPfvJ3MDayZJDVMvxXBwE62EhPJBCy2Id0OflKsqGvic 88reCW8ijLvBffrYQTBPi5iQpbkvi36P2Ue20gs1fwmrlOpd+6+GU7RkXOULyKY3cc oyxmo8l+braFiCCvkEBRoniEbLd5K/3SlbR5DBtVvfE68xPe0uVK2VQS7ISMw6P+OT kg28oPPSbyNxaRb3v4I+nawg4299rBVNgBw9BHtWFMpaQa+I62z9Ww7z6hpsuGZWQ2 A+uTLokt8Fg9Q== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 0B3EB682E9; Fri, 31 Oct 2025 00:55:57 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 31 Oct 2025 00:54:10 -0600 Message-ID: <20251031065439.3251464-15-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251031065439.3251464-1-sjg@u-boot.org> References: <20251031065439.3251464-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: H2NG3XJACOBGPKUO2FVC7NXT6KAJXW5X X-Message-ID-Hash: H2NG3XJACOBGPKUO2FVC7NXT6KAJXW5X X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 14/24] luks: Add a way to create an encrypted partition List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass If requested, use cryptsetup to encrypt a partition with the provided passphrase. This requires use of sudo since there doesn't seem to be any other way to write files into a filesystem on an encrypted disk. Co-developed-by: Claude Signed-off-by: Simon Glass --- test/py/tests/fs_helper.py | 109 ++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/test/py/tests/fs_helper.py b/test/py/tests/fs_helper.py index e4ac946064f..49747be1788 100644 --- a/test/py/tests/fs_helper.py +++ b/test/py/tests/fs_helper.py @@ -8,7 +8,7 @@ import re import os import shutil -from subprocess import call, check_call, check_output, CalledProcessError +from subprocess import call, check_call, check_output, CalledProcessError, run from subprocess import DEVNULL import tempfile @@ -38,11 +38,20 @@ class FsHelper: fsh.mk_fs() ... + To create an encrypted LUKS1 partition: + + with FsHelper(ubman.config, 'ext4', 10, 'mmc1', + encrypt_passphrase='test') as fsh: + # create files in the fsh.srcdir directory + fsh.mk_fs() # Creates and encrypts the filesystem + ... + Properties: fs_img (str): Filename for the filesystem image; this is set to a default value but can be overwritten """ - def __init__(self, config, fs_type, size_mb, prefix, part_mb=None): + def __init__(self, config, fs_type, size_mb, prefix, part_mb=None, + encrypt_passphrase=None): """Set up a new object Args: @@ -54,6 +63,8 @@ class FsHelper: part_mb (int, optional): Size of partition in MB. If None, defaults to size_mb. This can be used to make the partition larger than the filesystem, to create space for disk-encryption metadata + encrypt_passphrase (str, optional): If provided, encrypt the + filesystem with LUKS1 using this passphrase """ if ('fat' not in fs_type and 'ext' not in fs_type and fs_type not in ['exfat', 'fs_generic']): @@ -65,6 +76,7 @@ class FsHelper: self.partition_mb = part_mb if part_mb is not None else size_mb self.prefix = prefix self.quiet = True + self.encrypt_passphrase = encrypt_passphrase # Use a default filename; the caller can adjust it leaf = f'{prefix}.{fs_type}.img' @@ -136,6 +148,10 @@ class FsHelper: check_call(f'mcopy -i {fs_img} {flags} {self.srcdir}/* ::/', shell=True) + # Encrypt the filesystem if requested + if self.encrypt_passphrase: + self.encrypt_luks(self.encrypt_passphrase) + def setup(self): """Set up the srcdir ready to receive files""" if not self.srcdir: @@ -149,6 +165,95 @@ class FsHelper: self.tmpdir = tempfile.TemporaryDirectory('fs_helper') self.srcdir = self.tmpdir.name + def encrypt_luks(self, passphrase): + """Encrypt the filesystem image with LUKS1 + + This replaces the filesystem image with a LUKS1-encrypted version. + LUKS1 is used because U-Boot's unlock implementation currently only + supports LUKS version 1. + + Args: + passphrase (str): Passphrase for the LUKS container + + Returns: + str: Path to the encrypted image + + Raises: + CalledProcessError: If cryptsetup is not available or fails + """ + # LUKS1 encryption parameters + cipher = 'aes-cbc-essiv:sha256' + key_size = 256 + hash_alg = 'sha256' + + # Save the original filesystem image + orig_fs_img = f'{self.fs_img}.orig' + os.rename(self.fs_img, orig_fs_img) + + # Create a new image file for the LUKS container + luks_img = self.fs_img + luks_size_mb = self.partition_mb + check_call(f'dd if=/dev/zero of={luks_img} bs=1M count={luks_size_mb}', + shell=True, stdout=DEVNULL if self.quiet else None) + + # Ensure device-mapper kernel module is loaded + if not os.path.exists('/sys/class/misc/device-mapper'): + # Try to load the dm_mod kernel module + result = run(['sudo', 'modprobe', 'dm_mod'], + stdout=DEVNULL, stderr=DEVNULL, check=False) + if result.returncode != 0: + raise RuntimeError( + 'Device-mapper is not available. Please ensure the dm_mod ' + 'kernel module is loaded and you have permission to use ' + 'device-mapper. This is required for LUKS encryption tests.') + + device_name = f'luks_test_{os.getpid()}' + + # Clean up any stale device with the same name + run(['sudo', 'cryptsetup', 'close', device_name], + stdout=DEVNULL, stderr=DEVNULL, check=False) + + try: + # Format as LUKS1 + run(['cryptsetup', 'luksFormat', + '--type', 'luks1', + '--cipher', cipher, + '--key-size', str(key_size), + '--hash', hash_alg, + '--iter-time', '10', # Very fast for testing (low security) + luks_img], + input=f'{passphrase}\n'.encode(), + stdout=DEVNULL if self.quiet else None, + stderr=DEVNULL if self.quiet else None, + check=True) + + # Open the LUKS device (requires sudo) + # Use --key-file=- to read passphrase from stdin + result = run(['sudo', 'cryptsetup', 'open', '--key-file=-', + luks_img, device_name], input=passphrase.encode(), + stdout=DEVNULL if self.quiet else None, stderr=None, + check=True) + # Copy the filesystem data into the LUKS container + check_call(f'sudo dd if={orig_fs_img} of=/dev/mapper/{device_name} bs=1M', + shell=True, stdout=DEVNULL if self.quiet else None) + + # Remove the original filesystem image + os.remove(orig_fs_img) + + except Exception: + # Clean up on error + if os.path.exists(luks_img): + os.remove(luks_img) + if os.path.exists(orig_fs_img): + os.rename(orig_fs_img, self.fs_img) + raise + finally: + # Always close the device if it's still open + run(['sudo', 'cryptsetup', 'close', device_name], + stdout=DEVNULL, stderr=DEVNULL, check=False) + + return self.fs_img + def cleanup(self): """Remove created image""" if self.tmpdir: