Compare commits
No commits in common. "master" and "1.0" have entirely different histories.
7 changed files with 140 additions and 161 deletions
12
README.md
12
README.md
|
@ -4,16 +4,18 @@ A minimal fork of [Clevis](https://github.com/latchset/clevis), rewritten in POS
|
|||
|
||||
## Installation
|
||||
|
||||
Zlevis can be installed with `meson`, after cloning the repository, setup the build directory
|
||||
### Alpine Linux
|
||||
|
||||
Work in progress.
|
||||
|
||||
### Manual
|
||||
|
||||
Zlevis can be manually 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
|
||||
|
||||
```
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
# 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-decrypt')
|
||||
bins += join_paths(meson.current_source_dir(), 'zlevis-fetch')
|
||||
|
|
51
src/zlevis
51
src/zlevis
|
@ -1,51 +0,0 @@
|
|||
#!/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 $?
|
|
@ -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,82 +18,98 @@ 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" >&2
|
||||
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5"
|
||||
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="$(printf "%s" "$hdr" | jose b64 dec -i-)"; then
|
||||
if ! jhd="$(jose b64 dec -i- < <(echo "$hdr"))"; then
|
||||
echo "Error decoding JWE protected header" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$jhd" > "$tmp"/jhd
|
||||
|
||||
# Validate the JWE pin type
|
||||
if [ "$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g pin -u-)" != "tpm2" ]; then
|
||||
if [ "$(jose fmt -j- -Og clevis -g pin -u- < "$tmp"/jhd)" != "tpm2" ]; then
|
||||
echo "JWE pin mismatch" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract required parameters from the JWE header
|
||||
if ! hash="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g hash -Su-)"; then
|
||||
echo "JWE missing required 'hash' header parameter" >&2
|
||||
if ! hash="$(jose fmt -j- -Og clevis -g tpm2 -g hash -Su- < "$tmp"/jhd)"; then
|
||||
echo "JWE missing required 'hash' header parameter!" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! key="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g key -Su-)"; then
|
||||
echo "JWE missing required 'key' header parameter" >&2
|
||||
if ! key="$(jose fmt -j- -Og clevis -g tpm2 -g key -Su- < "$tmp"/jhd)"; then
|
||||
echo "JWE missing required 'key' header parameter!" >&2
|
||||
exit 1
|
||||
fi
|
||||
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
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
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
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
echo "$jwk_priv" > "$tmp"/jwk_priv
|
||||
|
||||
# Handle optional PCR parameters
|
||||
pcr_ids="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g pcr_ids -Su-)" || true
|
||||
pcr_ids="$(jose fmt -j- -Og clevis -g tpm2 -g pcr_ids -Su- < "$tmp"/jhd)" || true
|
||||
pcr_spec=""
|
||||
if [ -n "$pcr_ids" ]; then
|
||||
pcr_bank="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g pcr_bank -Su-)"
|
||||
pcr_bank="$(jose fmt -j- -Og clevis -g tpm2 -g pcr_bank -Su- < "$tmp"/jhd)"
|
||||
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 ! printf "%s" "$jwk_pub" | jose b64 dec -i- -O "$tmp_jwk_pub"; then
|
||||
if ! jose b64 dec -i- -O "$tmp"/jwk.pub < "$tmp"/jwk_pub; then
|
||||
echo "Decoding jwk.pub from Base64 failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! printf "%s" "$jwk_priv" | jose b64 dec -i- -O "$tmp_jwk_priv"; then
|
||||
if ! jose b64 dec -i- -O "$tmp"/jwk.priv < "$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
|
||||
|
@ -102,13 +118,9 @@ 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
|
||||
|
@ -117,12 +129,9 @@ 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
|
||||
|
@ -131,9 +140,6 @@ 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-
|
||||
|
||||
|
|
|
@ -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,8 +21,14 @@ 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)."
|
||||
|
@ -55,27 +61,48 @@ 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" >&2
|
||||
echo "The tpm2 pin requires a tpm2-tools version between 4 and 5"
|
||||
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 '{\"property\":\"value\"}' is not present or malformed" >&2
|
||||
echo "Configuration is 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="$(printf "%s" "$cfg" | jose fmt -j- -Og hash -u-)" || hash="sha256"
|
||||
key="$(printf "%s" "$cfg" | jose fmt -j- -Og key -u-)" || key="ecc"
|
||||
hash="$(jose fmt -j- -Og hash -u- < "$tmp"/cfg)" || hash="sha256"
|
||||
key="$(jose fmt -j- -Og key -u- < "$tmp"/cfg)" || key="ecc"
|
||||
|
||||
# Determine the PCR bank to use for policy
|
||||
pcr_bank="$(printf "%s" "$cfg" | jose fmt -j- -Og pcr_bank -u-)" || {
|
||||
pcr_bank="$(jose fmt -j- -Og pcr_bank -u- < "$tmp"/cfg)" || {
|
||||
# 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
|
||||
|
@ -84,14 +111,15 @@ pcr_bank="$(printf "%s" "$cfg" | jose fmt -j- -Og pcr_bank -u-)" || {
|
|||
}
|
||||
|
||||
# Trim spaces from the configuration for parsing PCR IDs
|
||||
pcr_cfg=$(printf "%s" "$cfg" | tr -d '[:space:]')
|
||||
pcr_cfg=$(tr -d '[:space:]' < "$tmp"/cfg)
|
||||
echo "$pcr_cfg" > "$tmp"/pcr_cfg
|
||||
|
||||
# Handle both string and JSON array formats for pcr_ids
|
||||
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
|
||||
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
|
||||
# Attempt to parse as a JSON array if string parsing fails
|
||||
if printf "%s" "$pcr_cfg" | jose fmt -j- -Og pcr_ids -A 2>/dev/null; then
|
||||
if jose fmt -j- -Og pcr_ids -A 2>/dev/null < "$tmp"/pcr_cfg; then
|
||||
# Construct a comma-separated string from the array
|
||||
for pcr in $(printf "%s" "$pcr_cfg" | jose fmt -j- -Og pcr_ids -Af- | tr -d '"'); do
|
||||
for pcr in $(jose fmt -j- -Og pcr_ids -Af- < "$tmp"/pcr_cfg | tr -d '"'); do
|
||||
pcr_ids=$(printf '%s,%s' "${pcr_ids}" "${pcr}")
|
||||
done
|
||||
# Remove leading comma
|
||||
|
@ -109,21 +137,19 @@ 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="$(printf "%s" "$cfg" | jose fmt -j- -Og pcr_digest -u-)" || true
|
||||
pcr_digest="$(jose fmt -j- -Og pcr_digest -u- < "$tmp"/cfg)" || true
|
||||
echo "$pcr_digest" > "$tmp"/pcr_digest
|
||||
|
||||
# Generate a JSON Web Key (JWK)
|
||||
if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
|
||||
echo "Generating a jwk failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Define and trap primary_context
|
||||
tmp_primary_context="/tmp/primary_context.$$"
|
||||
trap 'rm -f "$tmp_primary_context"' EXIT
|
||||
echo "$jwk" > "$tmp"/jwk
|
||||
|
||||
# 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
|
||||
|
@ -132,17 +158,12 @@ 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
|
||||
|
@ -151,7 +172,7 @@ if [ -n "$pcr_ids" ]; then
|
|||
fi
|
||||
tpm2_flushcontext -t
|
||||
else
|
||||
if ! printf "%s" "$pcr_digest" | jose b64 dec -i- -O "$tmp_pcr_digest"; then
|
||||
if ! jose b64 dec -i- -O "$tmp"/pcr.digest < "$tmp"/pcr_digest; then
|
||||
echo "Error decoding PCR digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
@ -159,7 +180,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
|
||||
|
@ -170,23 +191,15 @@ 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) 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=$?;;
|
||||
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=$?;;
|
||||
*) fail=1;;
|
||||
esac
|
||||
if [ -n "$fail" ]; then
|
||||
|
@ -195,39 +208,33 @@ 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":{"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-)"
|
||||
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-)"
|
||||
|
||||
# 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 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-)"
|
||||
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-)"
|
||||
fi
|
||||
|
||||
# Add the Base64 encoded JWK public and private keys to the JWE
|
||||
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-)"
|
||||
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}"
|
||||
|
||||
# Output the final JWE
|
||||
(echo "$jwe$jwk$(/bin/cat)") | jose jwe enc -i- -k- -I- -c
|
||||
|
||||
# Exit with the status of the last command
|
||||
exit $?
|
||||
exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)
|
25
src/zlevis-fetch
Executable file
25
src/zlevis-fetch
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/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
|
Loading…
Reference in a new issue