My answer relies on the needed Linux kernel feature CONFIG_KEYS
.
There is a keyscript command intended for what you want available there:
https://github.com/gebi/keyctl_keyscript
It uses the Kernel Key Retention Service (which has been around for a long time) with the command keyctl
to ask and temporarily store the passphrase in memory if not already found, and use it for any related invocations of multiple cryptsetup
commands. It's probably included in most distributions (eg: on Debian, the package cryptsetup has it as /lib/cryptsetup/scripts/decrypt_keyctl
).
Normally you'd use it integrated within the distribution and configured in an /etc/crypttab
file like the given example:
test1 /dev/sda1 test_pw luks,keyscript=decrypt_keyctl
test2 /dev/sda2 test_pw luks,keyscript=decrypt_keyctl
test3 /dev/sda3 test_other_pw luks,keyscript=decrypt_keyctl
Which would trigger asking a passphrase for test1, reuse the answer on test2 (because its key id is the same as the previous one and the passphrase was already given) and ask a new question for test3.
Now if you want to use this tool directly, not along system's integration, you can run the command and pipe it directly to cryptsetup
.
UPDATE: Put a complete full example that can be reproduced except for the few values which will depend on environment (here /dev/loop0
and 969933847
):
Create a test LUKS device from a fake disk. Be very careful that losetup
actually returned /dev/loop0
or abort this example:
# dd if=/dev/zero seek=$(( 2 ** 30 - 1 )) bs=1 count=1 of=/tmp/block.img
1+0 records in
1+0 records out
1 byte copied, 6.0997e-05 s, 16.4 kB/s
# losetup --find --show /tmp/block.img
/dev/loop0
# echo -n goodpass | cryptsetup luksFormat /dev/loop0
# cryptsetup luksDump /dev/loop0
LUKS header information for /dev/loop0
Version: 1
Cipher name: aes
Cipher mode: xts-plain64
Hash spec: sha256
Payload offset: 4096
MK bits: 256
MK digest: 4e 98 86 0b bf 63 3f 68 08 f5 cc 4f 61 ec 8c 19 71 c3 2a 33
MK salt: dc ce 7b a1 56 79 70 c5 02 ad ec 4c 85 c1 6f c1
af 50 f3 8c 89 b9 a9 3a 02 62 5c 2d 3f 7a 9d 52
MK iterations: 312000
UUID: 61a3a890-9564-4c98-866a-1216474b839e
Key Slot 0: ENABLED
Iterations: 2467468
Salt: 04 82 b5 b7 0b 90 e4 62 45 96 e3 c3 ef ba 6d 66
1d 93 6b e0 e9 03 40 3d 39 b9 fe 2c 6f 9e 46 e4
Key material offset: 8
AF stripes: 4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
Now try operations on it using decrypt_keyctl
. The prassphrase is as previously set: goodpass
. Note that /etc/crypttab
is not used nor needed for this example run directly (on Debian 9's modified version, here with two variables to avoid an harmless warning and still print a cosmetic "example" when used outside of the expected environment):
# echo -n securepass > /tmp/newkey
# export CRYPTTAB_TRIED=0 CRYPTTAB_SOURCE=example
# /lib/cryptsetup/scripts/decrypt_keyctl lazy | cryptsetup luksOpen /dev/loop0 test1
Caching passphrase for example: ********
# cryptsetup close test1
# cryptsetup status test1
/dev/mapper/test1 is inactive.
# /lib/cryptsetup/scripts/decrypt_keyctl lazy | cryptsetup luksAddKey /dev/loop0 /tmp/newkey
Using cached passphrase for example.
# /lib/cryptsetup/scripts/decrypt_keyctl lazy | cryptsetup luksOpen /dev/loop0 test1
Using cached passphrase for example.
# cryptsetup status test1
/dev/mapper/test1 is active.
type: LUKS1
cipher: aes-xts-plain64
keysize: 256 bits
device: /dev/loop0
loop: /tmp/block.img
offset: 4096 sectors
size: 2093056 sectors
mode: read/write
Of course you could even use directly keyctl
, here's a following (really bad) example still using the previous luks device, that can be done right after:
# cryptsetup close test1
# echo -n goodpass | keyctl padd user lazy @u
969933847
# keyctl timeout 969933847 120
# keyctl pipe 969933847 | cryptsetup luksOpen /dev/loop0 test1 && echo OK
OK
# keyctl pipe 969933847 | cryptsetup luksAddKey /dev/loop0 /tmp/newkey && echo OK
OK
# keyctl clear @u
Cleanup:
# cryptsetup close test1
# losetup -d /dev/loop0
# rm /tmp/block.img