summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/HACKING.md35
-rw-r--r--rules.d/99-systemd.rules.in6
-rw-r--r--src/basic/hmac.c4
-rw-r--r--src/boot/efi/random-seed.c13
-rw-r--r--src/creds/creds.c17
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.c2
-rw-r--r--src/cryptenroll/cryptenroll.c17
-rw-r--r--src/cryptsetup/cryptsetup.c17
-rw-r--r--src/fundamental/sha256.c11
-rw-r--r--src/fundamental/sha256.h6
-rw-r--r--src/partition/repart.c17
-rw-r--r--src/random-seed/random-seed.c2
-rw-r--r--src/shared/creds-util.c2
-rw-r--r--src/shared/tpm2-util.c47
-rw-r--r--src/shared/tpm2-util.h20
-rw-r--r--test/fuzz/fuzz-udev-rules/99-systemd.rules6
-rwxr-xr-xtest/units/testsuite-70.sh3
17 files changed, 126 insertions, 99 deletions
diff --git a/docs/HACKING.md b/docs/HACKING.md
index 4b93741f3a..33b5ac166a 100644
--- a/docs/HACKING.md
+++ b/docs/HACKING.md
@@ -217,7 +217,7 @@ $(pwd)/mkosi.installdir=/root/dest\\
--header-insertion=never
EOF
chmod +x mkosi-clangd.build
-exec sudo mkosi --source-file-transfer=mount --incremental --skip-final-phase --build-script mkosi-clangd.build build
+exec pkexec mkosi --source-file-transfer=mount --incremental --skip-final-phase --build-script mkosi-clangd.build build
```
Next, mark the script as executable and point your editor plugin to use this script to start clangd. For
@@ -252,12 +252,9 @@ some bundle clangd in the clang package.
Because mkosi needs to run as root, we also need to make sure we can enter the root password when the editor
plugin tries to run the mkosi-clangd.sh script. To be able to enter the root password in non-interactive
-scripts, we use an askpass provider. This is a program that sudo will launch if it detects it's being
-executed from a non-interactive shell so that the root password can still be entered. There are multiple
-implementations such as gnome askpass and KDE askpass. Install one of the askpass packages your distro
-provides and set the `SUDO_ASKPASS` environment variable to the path of the askpass binary you want to use.
-If configured correctly, a window will appear when your editor plugin tries to run the mkosi-clangd.sh script
-allowing you to enter the root password.
+scripts, we use pkexec instead of sudo. pkexec will launch a graphical interface to let the user enter their
+password, so that the password can be entered by the user even when pkexec is executed from a non-interactive
+shell.
Due to a bug in btrfs, it's currently impossible to mount two mkosi btrfs images at the same time. Because of
this, trying to do a regular build while the clangd image is running will fail. To circumvent this, use ext4
@@ -274,6 +271,30 @@ the cached images are initialized (`mkosi -i`).
Now, your editor will start clangd in the mkosi build image and all of clangd's features will work as
expected.
+## Debugging binaries that need to run as root in vscode
+
+When trying to debug binaries that need to run as root, we need to do some custom configuration in vscode to
+have it try to run the applications as root and to ask the user for the root password when trying to start
+the binary. To achieve this, we'll use a custom debugger path which points to a script that starts `gdb` as
+root using `pkexec`. pkexec will prompt the user for their root password via a graphical interface. This
+guide assumes the C/C++ extension is used for debugging.
+
+First, create a file `sgdb` in the root of the systemd repository with the following contents and make it
+executable:
+
+```
+#!/bin/sh
+exec pkexec gdb "$@"
+```
+
+Then, open launch.json in vscode, and set `miDebuggerPath` to `${workspaceFolder}/sgdb` for the corresponding
+debug configuration. Now, whenever you try to debug the application, vscode will try to start gdb as root via
+pkexec which will prompt you for your password via a graphical interface. After entering your password,
+vscode should be able to start debugging the application.
+
+For more information on how to set up a debug configuration for C binaries, please refer to the official
+vscode documentation [here](https://code.visualstudio.com/docs/cpp/launch-json-reference)
+
## Debugging systemd with mkosi + vscode
To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach
diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in
index 25b8a590a6..3dbba1f850 100644
--- a/rules.d/99-systemd.rules.in
+++ b/rules.d/99-systemd.rules.in
@@ -15,12 +15,18 @@ KERNEL=="vport*", TAG+="systemd"
SUBSYSTEM=="ubi", TAG+="systemd"
SUBSYSTEM=="block", TAG+="systemd"
+
+# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules
+SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY", GOTO="systemd_end"
SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
# Ignore encrypted devices with no identified superblock on it, since
# we are probably still calling mke2fs or mkswap on it.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
+# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
+SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
+
# add symlink to GPT root disk
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
diff --git a/src/basic/hmac.c b/src/basic/hmac.c
index 1e4e380aaa..a5f66d5605 100644
--- a/src/basic/hmac.c
+++ b/src/basic/hmac.c
@@ -29,9 +29,7 @@ void hmac_sha256(const void *key,
/* The key needs to be block size length or less, hash it if it's longer. */
if (key_size > HMAC_BLOCK_SIZE) {
- sha256_init_ctx(&hash);
- sha256_process_bytes(key, key_size, &hash);
- sha256_finish_ctx(&hash, replacement_key);
+ sha256_direct(key, key_size, replacement_key);
key = replacement_key;
key_size = SHA256_DIGEST_SIZE;
}
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 652634bdc9..aea4f7e532 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -215,17 +215,8 @@ static void validate_sha256(void) {
0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1 }},
};
- for (UINTN i = 0; i < ELEMENTSOF(array); i++) {
- struct sha256_ctx hash;
- uint8_t result[HASH_VALUE_SIZE];
-
- sha256_init_ctx(&hash);
- sha256_process_bytes(array[i].string, strlen8(array[i].string), &hash);
- sha256_finish_ctx(&hash, result);
-
- assert(memcmp(result, array[i].hash, HASH_VALUE_SIZE) == 0);
- }
-
+ for (UINTN i = 0; i < ELEMENTSOF(array); i++)
+ assert(memcmp(SHA256_DIRECT(array[i].string, strlen8(array[i].string)), array[i].hash, HASH_VALUE_SIZE) == 0);
#endif
}
diff --git a/src/creds/creds.c b/src/creds/creds.c
index e9c7c96fc5..c067c886db 100644
--- a/src/creds/creds.c
+++ b/src/creds/creds.c
@@ -832,25 +832,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_tpm2_device = streq(optarg, "auto") ? NULL : optarg;
break;
- case ARG_TPM2_PCRS: {
- uint32_t mask;
-
- if (isempty(optarg)) {
- arg_tpm2_pcr_mask = 0;
- break;
- }
-
- r = tpm2_parse_pcrs(optarg, &mask);
+ case ARG_TPM2_PCRS:
+ r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
if (r < 0)
return r;
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = mask;
- else
- arg_tpm2_pcr_mask |= mask;
-
break;
- }
case ARG_NAME:
if (isempty(optarg)) {
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index e8c64dd753..01f2e1183c 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -147,7 +147,7 @@ int enroll_tpm2(struct crypt_device *cd,
assert(cd);
assert(volume_key);
assert(volume_key_size > 0);
- assert(pcr_mask < (1U << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
+ assert(TPM2_PCR_MASK_VALID(pcr_mask));
assert_se(node = crypt_get_device_name(cd));
diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c
index f7fc4963a8..3c2b914a43 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -315,25 +315,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
- case ARG_TPM2_PCRS: {
- uint32_t mask;
-
- if (isempty(optarg)) {
- arg_tpm2_pcr_mask = 0;
- break;
- }
-
- r = tpm2_parse_pcrs(optarg, &mask);
+ case ARG_TPM2_PCRS:
+ r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
if (r < 0)
return r;
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = mask;
- else
- arg_tpm2_pcr_mask |= mask;
-
break;
- }
case ARG_TPM2_PIN:
r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index f0130eb238..c9a55a314b 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -394,20 +394,9 @@ static int parse_one_option(const char *option) {
} else if ((val = startswith(option, "tpm2-pcrs="))) {
- if (isempty(val))
- arg_tpm2_pcr_mask = 0;
- else {
- uint32_t mask;
-
- r = tpm2_parse_pcrs(val, &mask);
- if (r < 0)
- return r;
-
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = mask;
- else
- arg_tpm2_pcr_mask |= mask;
- }
+ r = tpm2_parse_pcr_argument(val, &arg_tpm2_pcr_mask);
+ if (r < 0)
+ return r;
} else if ((val = startswith(option, "tpm2-pin="))) {
diff --git a/src/fundamental/sha256.c b/src/fundamental/sha256.c
index 31d9674d09..43ee996b6f 100644
--- a/src/fundamental/sha256.c
+++ b/src/fundamental/sha256.c
@@ -104,7 +104,7 @@ void sha256_init_ctx(struct sha256_ctx *ctx) {
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF. */
-void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]) {
/* Take yet unprocessed bytes into account. */
uint32_t bytes = ctx->buflen;
size_t pad;
@@ -129,7 +129,7 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
/* Put result from CTX in first 32 bytes following RESBUF. */
for (size_t i = 0; i < 8; ++i)
if (UNALIGNED_P(resbuf))
- memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
+ memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
else
((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
@@ -289,3 +289,10 @@ static void sha256_process_block(const void *buffer, size_t len, struct sha256_c
ctx->H[6] = g;
ctx->H[7] = h;
}
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]) {
+ struct sha256_ctx ctx;
+ sha256_init_ctx(&ctx);
+ sha256_process_bytes(buffer, sz, &ctx);
+ return sha256_finish_ctx(&ctx, result);
+}
diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h
index f296f76ae8..31790c2ebd 100644
--- a/src/fundamental/sha256.h
+++ b/src/fundamental/sha256.h
@@ -25,5 +25,9 @@ struct sha256_ctx {
};
void sha256_init_ctx(struct sha256_ctx *ctx);
-void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf);
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]);
void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]);
+
+#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {})
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 0c0c0de794..f44e239b37 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -4359,25 +4359,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
- case ARG_TPM2_PCRS: {
- uint32_t mask;
-
- if (isempty(optarg)) {
- arg_tpm2_pcr_mask = 0;
- break;
- }
-
- r = tpm2_parse_pcrs(optarg, &mask);
+ case ARG_TPM2_PCRS:
+ r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
if (r < 0)
return r;
- if (arg_tpm2_pcr_mask == UINT32_MAX)
- arg_tpm2_pcr_mask = mask;
- else
- arg_tpm2_pcr_mask |= mask;
-
break;
- }
case '?':
return -EINVAL;
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index b265b7f85c..dedbdb2667 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -293,7 +293,7 @@ static int run(int argc, char *argv[]) {
* and replace the last 32 bytes of the seed with the hash output, so that the
* new seed file can't regress in entropy. */
if (hashed_old_seed) {
- uint8_t hash[32];
+ uint8_t hash[SHA256_DIGEST_SIZE];
sha256_process_bytes(&k, sizeof(k), &hash_state); /* Hash length to distinguish from old seed. */
sha256_process_bytes(buf, k, &hash_state);
sha256_finish_ctx(&hash_state, hash);
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index c1a9d35528..03f5fb8c3f 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -879,7 +879,7 @@ int decrypt_credential_and_warn(
#if HAVE_TPM2
struct tpm2_credential_header* t = (struct tpm2_credential_header*) ((uint8_t*) input + p);
- if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
+ if (!TPM2_PCR_MASK_VALID(t->pcr_mask))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
if (!tpm2_pcr_bank_to_string(le16toh(t->pcr_bank)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported");
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 36999437f6..38ca862123 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -865,7 +865,7 @@ int tpm2_seal(
assert(ret_pcr_hash_size);
assert(ret_pcr_bank);
- assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
+ assert(TPM2_PCR_MASK_VALID(pcr_mask));
/* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
* is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
@@ -1069,7 +1069,7 @@ int tpm2_unseal(
assert(ret_secret);
assert(ret_secret_size);
- assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
+ assert(TPM2_PCR_MASK_VALID(pcr_mask));
r = dlopen_tpm2();
if (r < 0)
@@ -1483,20 +1483,26 @@ int tpm2_make_luks2_json(
}
const char *tpm2_pcr_bank_to_string(uint16_t bank) {
- /* For now, let's officially only support these two. We can extend this later on, should the need
- * arise. */
- if (bank == TPM2_ALG_SHA256)
- return "sha256";
if (bank == TPM2_ALG_SHA1)
return "sha1";
+ if (bank == TPM2_ALG_SHA256)
+ return "sha256";
+ if (bank == TPM2_ALG_SHA384)
+ return "sha384";
+ if (bank == TPM2_ALG_SHA512)
+ return "sha512";
return NULL;
}
int tpm2_pcr_bank_from_string(const char *bank) {
- if (streq_ptr(bank, "sha256"))
- return TPM2_ALG_SHA256;
if (streq_ptr(bank, "sha1"))
return TPM2_ALG_SHA1;
+ if (streq_ptr(bank, "sha256"))
+ return TPM2_ALG_SHA256;
+ if (streq_ptr(bank, "sha384"))
+ return TPM2_ALG_SHA384;
+ if (streq_ptr(bank, "sha512"))
+ return TPM2_ALG_SHA512;
return -EINVAL;
}
@@ -1542,3 +1548,28 @@ Tpm2Support tpm2_support(void) {
return support;
}
+
+int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) {
+ uint32_t m;
+ int r;
+
+ assert(mask);
+
+ /* For use in getopt_long() command line parsers: merges masks specified on the command line */
+
+ if (isempty(arg)) {
+ *mask = 0;
+ return 0;
+ }
+
+ r = tpm2_parse_pcrs(arg, &m);
+ if (r < 0)
+ return r;
+
+ if (*mask == UINT32_MAX)
+ *mask = m;
+ else
+ *mask |= m;
+
+ return 0;
+}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 3e9464ea67..4030f2a804 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -59,19 +59,31 @@ int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, TPM2Flags flags, JsonVariant **ret);
-#define TPM2_PCRS_MAX 24
+#define TPM2_PCRS_MAX 24U
+
+static inline bool TPM2_PCR_MASK_VALID(uint64_t pcr_mask) {
+ return pcr_mask < (UINT64_C(1) << TPM2_PCRS_MAX); /* Support 24 PCR banks */
+}
/* Default to PCR 7 only */
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
/* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if
* they are missing. */
+#ifndef TPM2_ALG_SHA1
+#define TPM2_ALG_SHA1 0x4
+#endif
+
#ifndef TPM2_ALG_SHA256
#define TPM2_ALG_SHA256 0xB
#endif
-#ifndef TPM2_ALG_SHA1
-#define TPM2_ALG_SHA1 0x4
+#ifndef TPM2_ALG_SHA384
+#define TPM2_ALG_SHA384 0xC
+#endif
+
+#ifndef TPM2_ALG_SHA512
+#define TPM2_ALG_SHA512 0xD
#endif
#ifndef TPM2_ALG_ECC
@@ -104,3 +116,5 @@ typedef enum Tpm2Support {
} Tpm2Support;
Tpm2Support tpm2_support(void);
+
+int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
diff --git a/test/fuzz/fuzz-udev-rules/99-systemd.rules b/test/fuzz/fuzz-udev-rules/99-systemd.rules
index e8a0f5a739..278383b02c 100644
--- a/test/fuzz/fuzz-udev-rules/99-systemd.rules
+++ b/test/fuzz/fuzz-udev-rules/99-systemd.rules
@@ -15,12 +15,18 @@ KERNEL=="vport*", TAG+="systemd"
SUBSYSTEM=="ubi", TAG+="systemd"
SUBSYSTEM=="block", TAG+="systemd"
+
+# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules
+SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY", GOTO="systemd_end"
SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
# Ignore encrypted devices with no identified superblock on it, since
# we are probably still calling mke2fs or mkswap on it.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
+# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
+SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
+
# add symlink to GPT root disk
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 258f39c38d..1f626ee62f 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -5,10 +5,9 @@ set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
-
# Prepare fresh disk image
img="/var/tmp/test.img"
-dd if=/dev/zero of=$img bs=1024k count=20 status=none
+truncate -s 20M $img
echo -n passphrase >/tmp/passphrase
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom $img /tmp/passphrase