Compare commits
29 commits
Author | SHA1 | Date | |
---|---|---|---|
f7af42f396 | |||
4d1ab8d9fc | |||
361efcc26b | |||
740418826c | |||
8c62ea0b88 | |||
1b067ff34c | |||
5b914c13a3 | |||
9ac3017da4 | |||
e2135379d8 | |||
17eb015918 | |||
d42a13b560 | |||
6f2a105188 | |||
e0424c61a8 | |||
078495815c | |||
f022b1fe3c | |||
44e6d7efd0 | |||
9bddc2d8da | |||
d6376c2672 | |||
1bb31363c1 | |||
0454a9c388 | |||
788a251e6b | |||
3344160e52 | |||
67e14db930 | |||
024ea1195b | |||
03127b4e46 | |||
31cafc8e7d | |||
ed325fd09b | |||
f2405d842b | |||
f573008075 |
7 changed files with 159 additions and 135 deletions
12
README.md
12
README.md
|
@ -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
|
||||
|
||||
```
|
||||
|
|
|
@ -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,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
51
src/zlevis
Executable 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 $?
|
|
@ -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 "$summary"
|
||||
echo
|
||||
echo "Usage: \"zlevis-decrypt < file.jwe\""
|
||||
echo "Usage ZFS: \"zfs list -Ho tpm:jwe <pool> | zlevis-decrypt\""
|
||||
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
|
||||
echo "$hdr" > "$tmp"/hdr
|
||||
|
||||
# Decode the JWE protected header
|
||||
if ! jhd="$(jose b64 dec -i- < "$tmp"/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-
|
||||
|
||||
|
|
|
@ -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,7 +21,7 @@ fi
|
|||
# Display usage information if input is from a terminal
|
||||
if [ -t 0 ]; then
|
||||
exec >&2
|
||||
echo "$summary"
|
||||
echo "Usage: \"zlevis-encrypt '{\"property\":\"value\"}' < file.key > file.jwe\""
|
||||
echo
|
||||
echo "This command uses the following configuration properties:"
|
||||
echo " hash: <string> -> Hash algorithm used in the computation of the object name (default: sha256)."
|
||||
|
@ -29,9 +29,6 @@ if [ -t 0 ]; then
|
|||
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."
|
||||
echo
|
||||
echo "Usage: \"zlevis-encrypt '{\"property\":\"value\"}' < file.key > file.jwe\""
|
||||
echo "Usage ZFS: \"zfs set tpm:jwe=\$(zlevis-encrypt '{\"property\":\"value\"}' < tank.key) <pool>\""
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
@ -58,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
|
||||
|
@ -108,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
|
||||
|
@ -134,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
|
||||
|
@ -155,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
|
||||
|
@ -169,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
|
||||
|
@ -177,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
|
||||
|
@ -188,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
|
||||
|
@ -205,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-)"
|
||||
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
|
||||
(echo "$jwe$jwk"; /bin/cat) | jose jwe enc -i- -k- -I- -c
|
||||
(echo "$jwe$jwk$(/bin/cat)") | jose jwe enc -i- -k- -I- -c
|
||||
|
||||
# Exit with the status of the last command
|
||||
exit $?
|
|
@ -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
|
Loading…
Reference in a new issue