Compare commits

...

31 commits
1.0 ... master

Author SHA1 Message Date
luc
f7af42f396 README.md: update installation section 2025-03-02 20:48:52 +01:00
Luc
4d1ab8d9fc src/zlevis-encrypt typeset update 2025-02-19 21:48:25 +01:00
Luc
361efcc26b src/zlevis-decrypt: typeset update 2025-02-19 21:48:05 +01:00
Luc
740418826c src/meson.build: remove zlevis-fetch 2025-02-19 21:37:04 +01:00
Luc
8c62ea0b88 src/zlevis-fetch: removed 2025-02-19 21:35:57 +01:00
Luc
1b067ff34c src/zlevis: removed key option indefinitely 2025-02-19 21:14:25 +01:00
Luc
5b914c13a3 src/zlevis: removed key option 2025-02-19 21:09:44 +01:00
Luc
9ac3017da4 src/zlevis: removed minor bug 2025-02-14 23:50:39 +01:00
Luc
e2135379d8 src/zlevis-encrypt: update 2025-02-14 23:48:04 +01:00
Luc
17eb015918 src/zlevis: restructured argument path structure 2025-02-14 23:46:19 +01:00
Luc
d42a13b560 src/zlevis: updated info call 2025-02-14 23:15:36 +01:00
Luc
6f2a105188 src/zlevis-encrypt: update error message 2025-02-14 22:09:28 +01:00
Luc
e0424c61a8 src/zlevis-decrypt update error messages 2025-02-14 22:09:06 +01:00
Luc
078495815c scr/zlevis: update format 2025-02-14 22:08:23 +01:00
Luc
f022b1fe3c src/zlevis: specified options 2025-02-14 21:22:52 +01:00
Luc
44e6d7efd0 README.md: useful update 2025-02-13 21:26:31 +01:00
Luc
9bddc2d8da src/zlevis: made executable 2025-02-13 21:26:02 +01:00
Luc
d6376c2672 src/zlevis-encrypt: update usage information 2025-02-13 21:19:34 +01:00
Luc
1bb31363c1 src/zlevis-decrypt: update usage information 2025-02-13 21:19:05 +01:00
Luc
0454a9c388 src/meson.build: add zlevis 2025-02-13 21:18:26 +01:00
Luc
788a251e6b src/zlevis: add main binary w.r.t. issue #2 2025-02-13 21:17:53 +01:00
Luc
3344160e52 src/zlevis-encrypt: update w.r.t. issue #1 2025-02-11 21:13:31 +01:00
Luc
67e14db930 src/zlevis-decrypt: added id to tmp-files properly 2025-02-10 15:55:18 +01:00
Luc
024ea1195b src/zlevis-decrypt: removed minor bug 2025-02-10 15:44:04 +01:00
Luc
03127b4e46 src/zlevis-decrypt: update w.r.t. issue #1 2025-02-10 15:36:04 +01:00
Luc
31cafc8e7d meson.build: updated formatting 2024-12-22 13:29:57 +01:00
Luc
ed325fd09b src/meson.build: added find and test scripts functionality 2024-12-22 13:29:11 +01:00
Luc
f2405d842b src/zlevis-fetch: added summary and improved zlevis-decrypt check 2024-12-22 13:24:47 +01:00
Luc
f573008075 Removed key line offset bug and changed pin from clevis to zlevis. 2024-12-14 16:33:21 +01:00
Luc
03f5c95b33 Updated markup of scripts. 2024-12-14 12:41:38 +01:00
Luc
7ab23985e1 Removed direct substitution in encrypt and decrypt scripts. 2024-12-14 12:32:34 +01:00
7 changed files with 161 additions and 140 deletions

View file

@ -4,18 +4,16 @@ A minimal fork of [Clevis](https://github.com/latchset/clevis), rewritten in POS
## Installation
### Alpine Linux
Work in progress.
### Manual
Zlevis can be manually installed with `meson`, after cloning the repository, setup the build directory
Zlevis can be installed with `meson`, after cloning the repository, setup the build directory
```
$ meson setup builddir
```
> Using the `--prefix=/usr` flag will install `zlevis` in `/usr/bin` instead of `/usr/local/bin`.
> Using the `--reconfigure` flag will reconfigure the build directory.
Installation of the zlevis scripts is now performed with
```

View file

@ -2,10 +2,10 @@
project('zlevis', license: 'GPL3', version: '1')
# Define bindir
bindir=join_paths(get_option('prefix'), get_option('bindir'))
bindir = join_paths(get_option('prefix'), get_option('bindir'))
# Define bins list
bins=[]
bins = []
# Define subdir with bins
subdir('src')

View file

@ -1,4 +1,14 @@
# Find scripts
main = find_program('zlevis')
encrypt = find_program('zlevis-encrypt')
decrypt = find_program('zlevis-decrypt')
# Test the scripts
test('zlevis', main, args: '--summary')
test('zlevis-encrypt', encrypt, args: '--summary')
test('zlevis-decrypt', decrypt, args: '--summary')
# Add paths of scripts to bins
bins += join_paths(meson.current_source_dir(), 'zlevis')
bins += join_paths(meson.current_source_dir(), 'zlevis-encrypt')
bins += join_paths(meson.current_source_dir(), 'zlevis-decrypt')
bins += join_paths(meson.current_source_dir(), 'zlevis-fetch')
bins += join_paths(meson.current_source_dir(), 'zlevis-decrypt')

51
src/zlevis Executable file
View file

@ -0,0 +1,51 @@
#!/bin/sh
# Exit immediately if a command exits with a non-zero status
set -e
# Summary of the script's functionality
summary="A tool that enables automatic decryption of ZFS rpools with TPM2"
# Display summary if requested
if [ "$1" = "--summary" ]; then
echo "$summary"
exit 0
fi
# Function to display usage information of zlevis
info() {
exec >&2
echo "Usage: \"zlevis {decrypt|encrypt} <pool>\""
exit 2
}
# Function to display usage information of zlevis encrypt pool
encrypt_pool_info() {
exec >&2
echo "Usage: \"zlevis encrypt <pool> '{\"property\":\"value\"}' < file.key\""
echo
echo "This command uses the following configuration properties:"
echo " hash: <string> -> Hash algorithm used in the computation of the object name (default: sha256)."
echo " key: <string> -> Algorithm type for the generated key (default: ecc)."
echo " pcr_bank: <string> -> PCR algorithm bank to use for policy (default: first supported by TPM)."
echo " pcr_ids: <string> -> PCR list used for policy. If not present, no policy is used."
echo " pcr_digest: <string> -> Binary PCR hashes encoded in base64. If not present, the hash values are looked up."
exit 2
}
# Determine the argument path and execute the relevant script or function
if [ -t 0 ]; then
case "$1" in
"decrypt") zfs list -Ho tpm:jwe "$2" | zlevis-decrypt;;
"encrypt") encrypt_pool_info;;
*) info;;
esac
else
case "$1" in
"encrypt") read -r -d . key || zfs set tpm:jwe=$(printf "%s" "$key" | zlevis-encrypt "$3") "$2";;
*) info;;
esac
fi
# Exit with the status of the last command
exit $?

View file

@ -4,7 +4,7 @@
set -e
# Summary of the script's functionality
summary="Decrypts a JWE using a TPM2.0 chip."
summary="Decrypts a JWE using a TPM2.0 chip"
# TPM2.0 owner hierarchy to be used by the Operating System
auth="o"
@ -18,98 +18,82 @@ fi
# Display usage information if input is from a terminal
if [ -t 0 ]; then
exec >&2
echo
echo "Usage: \"zlevis-decrypt < file.jwe\""
echo "Usage ZFS: \"zfs list -Ho tpm:jwe <pool> | zlevis-decrypt\""
echo
echo "$summary"
exit 2
fi
# Function to clean up temporary files on exit
on_exit() {
if [ ! -d "$tmp" ] || ! rm -rf "$tmp"; then
echo "Delete temporary files failed" >&2
echo "You need to clean up: $tmp" >&2
exit 1
fi
}
# Get the version of tpm2-tools
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
# Check if the tpm2-tools version is supported
if [ -z "$tpm2tools_version" ] || [ "$tpm2tools_version" -lt 4 ] || [ "$tpm2tools_version" -gt 5 ]; then
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5"
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5" >&2
exit 1
fi
# Create a temporary directory for TPM files
if ! tmp="$(mktemp -d)"; then
echo "Creating a temporary dir for TPM files failed" >&2
exit 1
fi
# Set up cleanup on exit
trap 'on_exit' EXIT
# Read the JWE protected header
read -r -d . hdr
# Decode the JWE protected header
if ! jhd="$(jose b64 dec -i- < <(echo "$hdr"))"; then
if ! jhd="$(printf "%s" "$hdr" | jose b64 dec -i-)"; then
echo "Error decoding JWE protected header" >&2
exit 1
fi
echo "$jhd" > "$tmp"/jhd
# Validate the JWE pin type
if [ "$(jose fmt -j- -Og clevis -g pin -u- < "$tmp"/jhd)" != "tpm2" ]; then
if [ "$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g pin -u-)" != "tpm2" ]; then
echo "JWE pin mismatch" >&2
exit 1
fi
# Extract required parameters from the JWE header
if ! hash="$(jose fmt -j- -Og clevis -g tpm2 -g hash -Su- < "$tmp"/jhd)"; then
echo "JWE missing required 'hash' header parameter!" >&2
if ! hash="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g hash -Su-)"; then
echo "JWE missing required 'hash' header parameter" >&2
exit 1
fi
if ! key="$(jose fmt -j- -Og clevis -g tpm2 -g key -Su- < "$tmp"/jhd)"; then
echo "JWE missing required 'key' header parameter!" >&2
if ! key="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g key -Su-)"; then
echo "JWE missing required 'key' header parameter" >&2
exit 1
fi
if ! jwk_pub="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_pub -Su- < "$tmp"/jhd)"; then
echo "JWE missing required 'jwk_pub' header parameter!" >&2
if ! jwk_pub="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g jwk_pub -Su-)"; then
echo "JWE missing required 'jwk_pub' header parameter" >&2
exit 1
fi
echo "$jwk_pub" > "$tmp"/jwk_pub
if ! jwk_priv="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_priv -Su- < "$tmp"/jhd)"; then
echo "JWE missing required 'jwk_priv' header parameter!" >&2
if ! jwk_priv="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g jwk_priv -Su-)"; then
echo "JWE missing required 'jwk_priv' header parameter" >&2
exit 1
fi
echo "$jwk_priv" > "$tmp"/jwk_priv
# Handle optional PCR parameters
pcr_ids="$(jose fmt -j- -Og clevis -g tpm2 -g pcr_ids -Su- < "$tmp"/jhd)" || true
pcr_ids="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g pcr_ids -Su-)" || true
pcr_spec=""
if [ -n "$pcr_ids" ]; then
pcr_bank="$(jose fmt -j- -Og clevis -g tpm2 -g pcr_bank -Su- < "$tmp"/jhd)"
pcr_bank="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g pcr_bank -Su-)"
pcr_spec="$pcr_bank:$pcr_ids"
fi
# Define and trap tmp jwk_pub and jwk_priv
tmp_jwk_pub="/tmp/jwk_pub.$$"
tmp_jwk_priv="/tmp/jwk_priv.$$"
trap 'rm -f "$tmp_jwk_pub" "$tmp_jwk_priv"' EXIT
# Decode the public and private keys from Base64
if ! jose b64 dec -i- -O "$tmp"/jwk.pub < "$tmp"/jwk_pub; then
if ! printf "%s" "$jwk_pub" | jose b64 dec -i- -O "$tmp_jwk_pub"; then
echo "Decoding jwk.pub from Base64 failed" >&2
exit 1
fi
if ! jose b64 dec -i- -O "$tmp"/jwk.priv < "$tmp"/jwk_priv; then
if ! printf "%s" "$jwk_priv" | jose b64 dec -i- -O "$tmp_jwk_priv"; then
echo "Decoding jwk.priv from Base64 failed" >&2
exit 1
fi
# Define and trap primary_context
tmp_primary_context="/tmp/primary_context.$$"
trap 'rm -f "$tmp_jwk_pub" "$tmp_jwk_priv" "$tmp_primary_context"' EXIT
# Create the primary key in the TPM
case "$tpm2tools_version" in
4|5) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$tmp"/primary.context || fail=$?;;
4|5) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$tmp_primary_context" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -118,9 +102,13 @@ if [ -n "$fail" ]; then
fi
tpm2_flushcontext -t
# Define and trap load_context
tmp_load_context="/tmp/load_context.$$"
trap 'rm -f "$tmp_jwk_pub" "$tmp_jwk_priv" "$tmp_primary_context" "$tmp_load_context"' EXIT
# Load the JWK into the TPM
case "$tpm2tools_version" in
4|5) tpm2_load -Q -C "$tmp"/primary.context -u "$tmp"/jwk.pub -r "$tmp"/jwk.priv -c "$tmp"/load.context || fail=$?;;
4|5) tpm2_load -Q -C "$tmp_primary_context" -u "$tmp_jwk_pub" -r "$tmp_jwk_priv" -c "$tmp_load_context" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -129,9 +117,12 @@ if [ -n "$fail" ]; then
fi
tpm2_flushcontext -t
# Remove tmp_jwk_pub, tmp_jwk_priv and tmp_primary_context
rm -f "$tmp_jwk_pub" "$tmp_jwk_priv" "$tmp_primary_context"
# Unseal the JWK from the TPM
case "$tpm2tools_version" in
4|5) jwk="$(tpm2_unseal -c "$tmp/load.context" ${pcr_spec:+-p pcr:$pcr_spec})" || fail=$?;;
4|5) jwk="$(tpm2_unseal -c "$tmp_load_context" ${pcr_spec:+-p pcr:$pcr_spec})" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -140,6 +131,9 @@ if [ -n "$fail" ]; then
fi
tpm2_flushcontext -t
# Remove tmp_load_context
rm -f "$tmp_load_context"
# Output the decrypted JWK along with the original header
(echo "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-

View file

@ -4,7 +4,7 @@
set -e
# Summary of the script's functionality
summary="Encrypts using a TPM2.0 chip binding policy."
summary="Encrypts using a TPM2.0 chip binding policy"
# TPM2.0 owner hierarchy to be used by the Operating System
auth="o"
@ -21,14 +21,8 @@ fi
# Display usage information if input is from a terminal
if [ -t 0 ]; then
exec >&2
echo
echo "Usage: \"zlevis-encrypt '{\"property\":\"value\"}' < file.key > file.jwe\""
echo
echo "Usage ZFS: \"zfs set tpm:jwe=\$(zlevis-encrypt '{\"property\":\"value\"}' < tank.key) <pool>\""
echo
echo
echo "$summary"
echo
echo "This command uses the following configuration properties:"
echo " hash: <string> -> Hash algorithm used in the computation of the object name (default: sha256)."
echo " key: <string> -> Algorithm type for the generated key (default: ecc)."
@ -61,48 +55,27 @@ validate_pcrs() {
return 0
}
# Function to clean up temporary files on exit
on_exit() {
if [ ! -d "$tmp" ] || ! rm -rf "$tmp"; then
echo "Delete temporary files failed" >&2
echo "You need to clean up: $tmp" >&2
exit 1
fi
}
# Get the version of tpm2-tools
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
# Check if the tpm2-tools version is supported
if [ -z "$tpm2tools_version" ] || [ "$tpm2tools_version" -lt 4 ] || [ "$tpm2tools_version" -gt 5 ]; then
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5"
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5" >&2
exit 1
fi
# Create a temporary directory for TPM files
if ! tmp="$(mktemp -d)"; then
echo "Creating a temporary dir for TPM files failed" >&2
exit 1
fi
# Set up cleanup on exit
trap 'on_exit' EXIT
# Validate the configuration input
if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
echo "Configuration is malformed" >&2
echo "Configuration '{\"property\":\"value\"}' is not present or malformed" >&2
exit 1
fi
# Store the configuration in a temporary file
echo "$cfg" > "$tmp"/cfg
# Extract hash and key from the configuration, defaulting if not present
hash="$(jose fmt -j- -Og hash -u- < "$tmp"/cfg)" || hash="sha256"
key="$(jose fmt -j- -Og key -u- < "$tmp"/cfg)" || key="ecc"
hash="$(printf "%s" "$cfg" | jose fmt -j- -Og hash -u-)" || hash="sha256"
key="$(printf "%s" "$cfg" | jose fmt -j- -Og key -u-)" || key="ecc"
# Determine the PCR bank to use for policy
pcr_bank="$(jose fmt -j- -Og pcr_bank -u- < "$tmp"/cfg)" || {
pcr_bank="$(printf "%s" "$cfg" | jose fmt -j- -Og pcr_bank -u-)" || {
# If not specified, find a non-empty PCR algorithm bank
if ! pcr_bank=$(tpm2_getcap pcrs | awk '/^[[:space:]]*-[[:space:]]*([^:]+):[[:space:]]*\[[[:space:]]*[^][:space:]]/ {found=1; split($0, m, /[-:[:space:]]+/); print m[2]; exit} END {exit !found}'); then
echo "Unable to find non-empty PCR algorithm bank, please check output of tpm2_getcap pcrs" >&2
@ -111,15 +84,14 @@ pcr_bank="$(jose fmt -j- -Og pcr_bank -u- < "$tmp"/cfg)" || {
}
# Trim spaces from the configuration for parsing PCR IDs
pcr_cfg=$(tr -d '[:space:]' < "$tmp"/cfg)
echo "$pcr_cfg" > "$tmp"/pcr_cfg
pcr_cfg=$(printf "%s" "$cfg" | tr -d '[:space:]')
# Handle both string and JSON array formats for pcr_ids
if jose fmt -j- -Og pcr_ids 2>/dev/null < "$tmp"/pcr_cfg && ! pcr_ids="$(jose fmt -j- -Og pcr_ids -u- 2>/dev/null < "$tmp"/pcr_cfg)"; then
if printf "%s" "$pcr_cfg" | jose fmt -j- -Og pcr_ids 2>/dev/null && ! pcr_ids="$(jose fmt -j- -Og pcr_ids -u- 2>/dev/null < "$tmp"/pcr_cfg)"; then
# Attempt to parse as a JSON array if string parsing fails
if jose fmt -j- -Og pcr_ids -A 2>/dev/null < "$tmp"/pcr_cfg; then
if printf "%s" "$pcr_cfg" | jose fmt -j- -Og pcr_ids -A 2>/dev/null; then
# Construct a comma-separated string from the array
for pcr in $(jose fmt -j- -Og pcr_ids -Af- < "$tmp"/pcr_cfg | tr -d '"'); do
for pcr in $(printf "%s" "$pcr_cfg" | jose fmt -j- -Og pcr_ids -Af- | tr -d '"'); do
pcr_ids=$(printf '%s,%s' "${pcr_ids}" "${pcr}")
done
# Remove leading comma
@ -137,19 +109,21 @@ if ! validate_pcrs "${tpm2tools_version}" "${pcr_bank}" "${pcr_ids}"; then
fi
# Get the PCR digest from the configuration or read it if not provided
pcr_digest="$(jose fmt -j- -Og pcr_digest -u- < "$tmp"/cfg)" || true
echo "$pcr_digest" > "$tmp"/pcr_digest
pcr_digest="$(printf "%s" "$cfg" | jose fmt -j- -Og pcr_digest -u-)" || true
# Generate a JSON Web Key (JWK)
if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
echo "Generating a jwk failed" >&2
exit 1
fi
echo "$jwk" > "$tmp"/jwk
# Define and trap primary_context
tmp_primary_context="/tmp/primary_context.$$"
trap 'rm -f "$tmp_primary_context"' EXIT
# Create the primary key in the TPM
case "$tpm2tools_version" in
4|5) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$tmp"/primary.context || fail=$?;;
4|5) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$tmp_primary_context" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -158,12 +132,17 @@ if [ -n "$fail" ]; then
fi
tpm2_flushcontext -t
# Define and trap pcr_digest and pcr_policy
tmp_pcr_digest="/tmp/pcr_digest.$$"
tmp_pcr_policy="/tmp/pcr_policy.$$"
trap 'rm -f "$tmp_primary_context" "$tmp_pcr_digest" "$tmp_pcr_policy"' EXIT
# Handle PCRs and policy creation if PCR IDs are provided
policy_options=""
if [ -n "$pcr_ids" ]; then
if [ -z "$pcr_digest" ]; then
case "$tpm2tools_version" in
4|5) tpm2_pcrread -Q "$pcr_bank":"$pcr_ids" -o "$tmp"/pcr.digest || fail=$?;;
4|5) tpm2_pcrread -Q "$pcr_bank":"$pcr_ids" -o "$tmp_pcr_digest" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -172,7 +151,7 @@ if [ -n "$pcr_ids" ]; then
fi
tpm2_flushcontext -t
else
if ! jose b64 dec -i- -O "$tmp"/pcr.digest < "$tmp"/pcr_digest; then
if ! printf "%s" "$pcr_digest" | jose b64 dec -i- -O "$tmp_pcr_digest"; then
echo "Error decoding PCR digest" >&2
exit 1
fi
@ -180,7 +159,7 @@ if [ -n "$pcr_ids" ]; then
# Create the policy based on PCRs
case "$tpm2tools_version" in
4|5) tpm2_createpolicy -Q -g "$hash" --policy-pcr -l "$pcr_bank":"$pcr_ids" -f "$tmp"/pcr.digest -L "$tmp"/pcr.policy || fail=$?;;
4|5) tpm2_createpolicy -Q -g "$hash" --policy-pcr -l "$pcr_bank":"$pcr_ids" -f "$tmp_pcr_digest" -L "$tmp_pcr_policy" || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -191,15 +170,23 @@ if [ -n "$pcr_ids" ]; then
tpm2_flushcontext -l
# Set the policy options to the created policy file
policy_options="$tmp/pcr.policy"
policy_options="$tmp_pcr_policy"
else
# If no PCR IDs are provided, add user authentication to the object attributes
obj_attr="$obj_attr|userwithauth"
fi
# Remove tmp_pcr_digest and tmp_pcr_policy
rm -f "$tmp_pcr_digest" "$tmp_pcr_policy"
# Define and trap tmp jwk_pub and jwk_priv
tmp_jwk_pub="/tmp/jwk_pub.$$"
tmp_jwk_priv="/tmp/jwk_priv.$$"
trap 'rm -f "$tmp_primary_context" "$tmp_jwk_pub" "$tmp_jwk_priv"' EXIT
# Create the TPM2 object for the JWK
case "$tpm2tools_version" in
4|5) tpm2_create -Q -g "$hash" -C "$tmp"/primary.context -u "$tmp"/jwk.pub -r "$tmp"/jwk.priv -a "$obj_attr" -L "$policy_options" -i- < "$tmp"/jwk || fail=$?;;
4|5) printf "%s" "$jwk" | tpm2_create -Q -g "$hash" -C "$tmp_primary_context" -u "$tmp_jwk_pub" -r "$tmp_jwk_priv" -a "$obj_attr" -L "$policy_options" -i- || fail=$?;;
*) fail=1;;
esac
if [ -n "$fail" ]; then
@ -208,33 +195,39 @@ if [ -n "$fail" ]; then
fi
tpm2_flushcontext -t
# Remove tmp_primary_context
rm -f "$tmp_primary_context"
# Encode the JWK public and private keys in Base64
if ! jwk_pub="$(jose b64 enc -I "$tmp"/jwk.pub)"; then
if ! jwk_pub="$(jose b64 enc -I "$tmp_jwk_pub")"; then
echo "Encoding jwk.pub in Base64 failed" >&2
exit 1
fi
if ! jwk_priv="$(jose b64 enc -I "$tmp"/jwk.priv)"; then
if ! jwk_priv="$(jose b64 enc -I "$tmp_jwk_priv")"; then
echo "Encoding jwk.priv in Base64 failed" >&2
exit 1
fi
# Remove tmp_jwk_pub and tmp_jwk_priv
rm -f "$tmp_jwk_pub" "$tmp_jwk_priv"
# Construct the JWE (JSON Web Encryption) structure
jwe='{"protected":{"clevis":{"pin":"tpm2","tpm2":{}}}}'
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$hash" -s hash -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$key" -s key -UUUUo-)"
jwe='{"protected":{"zlevis":{"pin":"tpm2","tpm2":{}}}}'
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$hash" -s hash -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$key" -s key -UUUUo-)"
# Include PCR bank and IDs in the JWE if they are provided
if [ -n "$pcr_ids" ]; then
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_bank" -s pcr_bank -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_ids" -s pcr_ids -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$pcr_bank" -s pcr_bank -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$pcr_ids" -s pcr_ids -UUUUo-)"
fi
# Add the Base64 encoded JWK public and private keys to the JWE
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_pub" -s jwk_pub -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_priv" -s jwk_priv -UUUUo-)"
# Clean up the temporary directory at the end of the script
[ -d "${tmp}" ] && rm -rf "${tmp}"
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$jwk_pub" -s jwk_pub -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$jwk_priv" -s jwk_priv -UUUUo-)"
# Output the final JWE
exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)
(echo "$jwe$jwk$(/bin/cat)") | jose jwe enc -i- -k- -I- -c
# Exit with the status of the last command
exit $?

View file

@ -1,25 +0,0 @@
#!/bin/sh
# Exit immediately if a command exits with a non-zero status
set -e
# Check if zlevis-decrypt is present
command -v zlevis-decrypt > /dev/null || exit 1
# Read ZFS dataset information.
zfs list -Ho name,encryption,keystatus,encryptionroot,tpm:jwe | while IFS=$'\t' read -r ds enc keystatus encroot jwe; do
# Check if the dataset is the encryption root.
if [ "$ds" = "$encroot" ] && [ "$enc" != "off" ] && [ "$jwe" != "-" ]; then
if [ "$keystatus" = "available" ]; then
echo "Pool $ds already unlocked"
else
echo "Loading key for $ds"
if echo -n "$jwe" | zlevis-decrypt | zfs load-key -L prompt "$ds"; then
echo "Unlocked $ds"
else
echo "FAILED TO UNLOCK $ds" >&2
exit 1
fi
fi
fi
done