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
|
## 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
|
$ 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
|
Installation of the zlevis scripts is now performed with
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
project('zlevis', license: 'GPL3', version: '1')
|
project('zlevis', license: 'GPL3', version: '1')
|
||||||
|
|
||||||
# Define bindir
|
# Define bindir
|
||||||
bindir = join_paths(get_option('prefix'), get_option('bindir'))
|
bindir=join_paths(get_option('prefix'), get_option('bindir'))
|
||||||
|
|
||||||
# Define bins list
|
# Define bins list
|
||||||
bins = []
|
bins=[]
|
||||||
|
|
||||||
# Define subdir with bins
|
# Define subdir with bins
|
||||||
subdir('src')
|
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
|
# 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-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
|
set -e
|
||||||
|
|
||||||
# Summary of the script's functionality
|
# 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
|
# TPM2.0 owner hierarchy to be used by the Operating System
|
||||||
auth="o"
|
auth="o"
|
||||||
|
@ -18,82 +18,98 @@ fi
|
||||||
# Display usage information if input is from a terminal
|
# Display usage information if input is from a terminal
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
exec >&2
|
exec >&2
|
||||||
|
echo
|
||||||
echo "Usage: \"zlevis-decrypt < file.jwe\""
|
echo "Usage: \"zlevis-decrypt < file.jwe\""
|
||||||
|
echo "Usage ZFS: \"zfs list -Ho tpm:jwe <pool> | zlevis-decrypt\""
|
||||||
|
echo
|
||||||
|
echo "$summary"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
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
|
# Get the version of tpm2-tools
|
||||||
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
|
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
|
||||||
|
|
||||||
# Check if the tpm2-tools version is supported
|
# Check if the tpm2-tools version is supported
|
||||||
if [ -z "$tpm2tools_version" ] || [ "$tpm2tools_version" -lt 4 ] || [ "$tpm2tools_version" -gt 5 ]; then
|
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
|
exit 1
|
||||||
fi
|
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 the JWE protected header
|
||||||
read -r -d . hdr
|
read -r -d . hdr
|
||||||
|
|
||||||
# Decode the JWE protected header
|
# 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
|
echo "Error decoding JWE protected header" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo "$jhd" > "$tmp"/jhd
|
||||||
|
|
||||||
# Validate the JWE pin type
|
# 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
|
echo "JWE pin mismatch" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract required parameters from the JWE header
|
# Extract required parameters from the JWE header
|
||||||
if ! hash="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g hash -Su-)"; then
|
if ! hash="$(jose fmt -j- -Og clevis -g tpm2 -g hash -Su- < "$tmp"/jhd)"; then
|
||||||
echo "JWE missing required 'hash' header parameter" >&2
|
echo "JWE missing required 'hash' header parameter!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! key="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g key -Su-)"; then
|
if ! key="$(jose fmt -j- -Og clevis -g tpm2 -g key -Su- < "$tmp"/jhd)"; then
|
||||||
echo "JWE missing required 'key' header parameter" >&2
|
echo "JWE missing required 'key' header parameter!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! jwk_pub="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g jwk_pub -Su-)"; then
|
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
|
echo "JWE missing required 'jwk_pub' header parameter!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! jwk_priv="$(printf "%s" "$jhd" | jose fmt -j- -Og zlevis -g tpm2 -g jwk_priv -Su-)"; then
|
echo "$jwk_pub" > "$tmp"/jwk_pub
|
||||||
echo "JWE missing required 'jwk_priv' header parameter" >&2
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo "$jwk_priv" > "$tmp"/jwk_priv
|
||||||
|
|
||||||
# Handle optional PCR parameters
|
# 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=""
|
pcr_spec=""
|
||||||
if [ -n "$pcr_ids" ]; then
|
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"
|
pcr_spec="$pcr_bank:$pcr_ids"
|
||||||
fi
|
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
|
# 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
|
echo "Decoding jwk.pub from Base64 failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
echo "Decoding jwk.priv from Base64 failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
# Create the primary key in the TPM
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -102,13 +118,9 @@ if [ -n "$fail" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
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
|
# Load the JWK into the TPM
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -117,12 +129,9 @@ if [ -n "$fail" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
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
|
# Unseal the JWK from the TPM
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -131,9 +140,6 @@ if [ -n "$fail" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
tpm2_flushcontext -t
|
||||||
|
|
||||||
# Remove tmp_load_context
|
|
||||||
rm -f "$tmp_load_context"
|
|
||||||
|
|
||||||
# Output the decrypted JWK along with the original header
|
# Output the decrypted JWK along with the original header
|
||||||
(echo "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-
|
(echo "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Summary of the script's functionality
|
# 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
|
# TPM2.0 owner hierarchy to be used by the Operating System
|
||||||
auth="o"
|
auth="o"
|
||||||
|
@ -21,8 +21,14 @@ fi
|
||||||
# Display usage information if input is from a terminal
|
# Display usage information if input is from a terminal
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
exec >&2
|
exec >&2
|
||||||
|
echo
|
||||||
echo "Usage: \"zlevis-encrypt '{\"property\":\"value\"}' < file.key > file.jwe\""
|
echo "Usage: \"zlevis-encrypt '{\"property\":\"value\"}' < file.key > file.jwe\""
|
||||||
echo
|
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 "This command uses the following configuration properties:"
|
||||||
echo " hash: <string> -> Hash algorithm used in the computation of the object name (default: sha256)."
|
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 " key: <string> -> Algorithm type for the generated key (default: ecc)."
|
||||||
|
@ -55,27 +61,48 @@ validate_pcrs() {
|
||||||
return 0
|
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
|
# Get the version of tpm2-tools
|
||||||
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
|
tpm2tools_version=$(tpm2_createprimary -v | awk -F'version="' '{print $2}' | awk -F'.' '{print $1}')
|
||||||
|
|
||||||
# Check if the tpm2-tools version is supported
|
# Check if the tpm2-tools version is supported
|
||||||
if [ -z "$tpm2tools_version" ] || [ "$tpm2tools_version" -lt 4 ] || [ "$tpm2tools_version" -gt 5 ]; then
|
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
|
exit 1
|
||||||
fi
|
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
|
# Validate the configuration input
|
||||||
if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Store the configuration in a temporary file
|
||||||
|
echo "$cfg" > "$tmp"/cfg
|
||||||
|
|
||||||
# Extract hash and key from the configuration, defaulting if not present
|
# Extract hash and key from the configuration, defaulting if not present
|
||||||
hash="$(printf "%s" "$cfg" | jose fmt -j- -Og hash -u-)" || hash="sha256"
|
hash="$(jose fmt -j- -Og hash -u- < "$tmp"/cfg)" || hash="sha256"
|
||||||
key="$(printf "%s" "$cfg" | jose fmt -j- -Og key -u-)" || key="ecc"
|
key="$(jose fmt -j- -Og key -u- < "$tmp"/cfg)" || key="ecc"
|
||||||
|
|
||||||
# Determine the PCR bank to use for policy
|
# 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 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
|
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
|
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
|
# 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
|
# 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
|
# 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
|
# 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}")
|
pcr_ids=$(printf '%s,%s' "${pcr_ids}" "${pcr}")
|
||||||
done
|
done
|
||||||
# Remove leading comma
|
# Remove leading comma
|
||||||
|
@ -109,21 +137,19 @@ if ! validate_pcrs "${tpm2tools_version}" "${pcr_bank}" "${pcr_ids}"; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the PCR digest from the configuration or read it if not provided
|
# 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)
|
# Generate a JSON Web Key (JWK)
|
||||||
if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
|
if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
|
||||||
echo "Generating a jwk failed" >&2
|
echo "Generating a jwk failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
# Create the primary key in the TPM
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -132,17 +158,12 @@ if [ -n "$fail" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
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
|
# Handle PCRs and policy creation if PCR IDs are provided
|
||||||
policy_options=""
|
policy_options=""
|
||||||
if [ -n "$pcr_ids" ]; then
|
if [ -n "$pcr_ids" ]; then
|
||||||
if [ -z "$pcr_digest" ]; then
|
if [ -z "$pcr_digest" ]; then
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -151,7 +172,7 @@ if [ -n "$pcr_ids" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
tpm2_flushcontext -t
|
||||||
else
|
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
|
echo "Error decoding PCR digest" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -159,7 +180,7 @@ if [ -n "$pcr_ids" ]; then
|
||||||
|
|
||||||
# Create the policy based on PCRs
|
# Create the policy based on PCRs
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -170,23 +191,15 @@ if [ -n "$pcr_ids" ]; then
|
||||||
tpm2_flushcontext -l
|
tpm2_flushcontext -l
|
||||||
|
|
||||||
# Set the policy options to the created policy file
|
# Set the policy options to the created policy file
|
||||||
policy_options="$tmp_pcr_policy"
|
policy_options="$tmp/pcr.policy"
|
||||||
else
|
else
|
||||||
# If no PCR IDs are provided, add user authentication to the object attributes
|
# If no PCR IDs are provided, add user authentication to the object attributes
|
||||||
obj_attr="$obj_attr|userwithauth"
|
obj_attr="$obj_attr|userwithauth"
|
||||||
fi
|
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
|
# Create the TPM2 object for the JWK
|
||||||
case "$tpm2tools_version" in
|
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;;
|
*) fail=1;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$fail" ]; then
|
if [ -n "$fail" ]; then
|
||||||
|
@ -195,39 +208,33 @@ if [ -n "$fail" ]; then
|
||||||
fi
|
fi
|
||||||
tpm2_flushcontext -t
|
tpm2_flushcontext -t
|
||||||
|
|
||||||
# Remove tmp_primary_context
|
|
||||||
rm -f "$tmp_primary_context"
|
|
||||||
|
|
||||||
# Encode the JWK public and private keys in Base64
|
# 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
|
echo "Encoding jwk.pub in Base64 failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
echo "Encoding jwk.priv in Base64 failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove tmp_jwk_pub and tmp_jwk_priv
|
|
||||||
rm -f "$tmp_jwk_pub" "$tmp_jwk_priv"
|
|
||||||
|
|
||||||
# Construct the JWE (JSON Web Encryption) structure
|
# Construct the JWE (JSON Web Encryption) structure
|
||||||
jwe='{"protected":{"zlevis":{"pin":"tpm2","tpm2":{}}}}'
|
jwe='{"protected":{"clevis":{"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 clevis -g tpm2 -q "$hash" -s hash -UUUUo-)"
|
||||||
jwe="$(jose fmt -j "$jwe" -g protected -g zlevis -g tpm2 -q "$key" -s key -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
|
# Include PCR bank and IDs in the JWE if they are provided
|
||||||
if [ -n "$pcr_ids" ]; then
|
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 clevis -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_ids" -s pcr_ids -UUUUo-)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add the Base64 encoded JWK public and private keys to the JWE
|
# 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 clevis -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_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
|
# Output the final JWE
|
||||||
(echo "$jwe$jwk$(/bin/cat)") | jose jwe enc -i- -k- -I- -c
|
exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)
|
||||||
|
|
||||||
# Exit with the status of the last command
|
|
||||||
exit $?
|
|
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