Skip to content

Commit

Permalink
Implement support for LUKS2
Browse files Browse the repository at this point in the history
Support for LUKS2 should be transparent. All commands work the same on both
LUKSv1 and LUKSv2.
  • Loading branch information
npmccallum committed Aug 14, 2018
1 parent 9f69af1 commit 84ccd62
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 130 deletions.
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_project_arguments(
language: 'c'
)

jansson = dependency('jansson', version: '>=2.10', required: false)
jose = dependency('jose', version: '>=8')
a2x = find_program('a2x', required: false)

Expand Down
90 changes: 49 additions & 41 deletions src/luks/clevis-luks-bind
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

SUMMARY="Binds a LUKSv1 device using the specified policy"
SUMMARY="Binds a LUKS device using the specified policy"
UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e

function onerr() {
if [ -n "$DEV" -a -n "$SLT" ]; then
luksmeta wipe -f -d "$DEV" -u "$UUID" -s "$SLT"
SLT=
fi
stty echo
exit 1
}

trap 'onerr' ERR HUP INT QUIT PIPE TERM

function usage() {
echo >&2
echo "Usage: clevis luks bind [-f] [-s SLT] [-k KEY] -d DEV PIN CFG" >&2
Expand All @@ -43,7 +32,7 @@ function usage() {
echo >&2
echo " -d DEV The LUKS device on which to perform binding" >&2
echo >&2
echo " -s SLT The LUKSMeta slot to use for metadata storage" >&2
echo " -s SLT The LUKS slot to use" >&2
echo >&2
echo " -k KEY Non-interactively read LUKS password from KEY file" >&2
echo " -k - Non-interactively read LUKS password from standard input" >&2
Expand All @@ -59,7 +48,7 @@ fi
while getopts ":hfd:s:k:" o; do
case "$o" in
f) FRC=-f;;
d) DEV=$OPTARG;;
d) export DEV=$OPTARG;;
s) SLT=$OPTARG;;
k) KEY=$OPTARG;;
*) usage;;
Expand All @@ -71,8 +60,8 @@ if [ -z "$DEV" ]; then
usage
fi

if ! cryptsetup isLuks --type luks1 "$DEV"; then
echo "$DEV is not a LUKSv1 device!" >&2
if ! cryptsetup isLuks "$DEV"; then
echo "$DEV is not a LUKS device!" >&2
exit 1
fi

Expand All @@ -88,9 +77,11 @@ fi

if [ -n "$KEY" ]; then
if [ "$KEY" == "-" ]; then
if ! luksmeta test -d $DEV && [ -z "$FRC" ]; then
echo "Cannot use '-k-' without '-f' unless already initialized!" >&2
usage
if cryptsetup isLuks --type luks1 "$DEV"; then
if ! luksmeta test -d $DEV && [ -z "$FRC" ]; then
echo "Cannot use '-k-' without '-f' unless already initialized!" >&2
usage
fi
fi
elif ! [ -f "$KEY" ]; then
echo "Key file '$KEY' not found!" >&2
Expand All @@ -100,38 +91,55 @@ fi

# Generate a key with the same entropy as the LUKS Master Key
dump=`cryptsetup luksDump $DEV`
bits=`sed -r -n 's|MK bits:[ \t]*([0-9]+)|\1|p' <<< "$dump"`
key=`pwmake $bits`
if cryptsetup isLuks --type luks1 "$DEV"; then
filt=`sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p' <<< "$dump"`
else
filt=`sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p' <<< "$dump"`
fi
bits=`sort -n <<<"$filt" | tail -n 1`
export key=`pwmake $bits`

# Encrypt the new key
jwe=`echo -n "$key" | clevis encrypt "$PIN" "$CFG"`

# If necessary, initialize the LUKS volume
if ! luksmeta test -d $DEV; then
luksmeta init -d $DEV $FRC
if cryptsetup isLuks --type luks1 "$DEV" && ! luksmeta test -d "$DEV"; then
luksmeta init -d "$DEV" $FRC
fi

# Write the JWE into the specified slot. Or, if no slot is given, ...
if [ -n "$SLT" ]; then
if ! echo -n $jwe | luksmeta save -d "$DEV" -u "$UUID" -s $SLT 2>/dev/null; then
echo "Error while saving Clevis metadata in LUKS header!" >&2
false
fi

# ... write the JWE to the first slot unused by both LUKS and LUKSMeta
elif ! SLT=`echo -n $jwe | luksmeta save -d "$DEV" -u "$UUID" 2>/dev/null`; then
echo "Error while saving Clevis metadata in LUKS header!" >&2
false
fi

export DEV
export SLT

# Add the new key to the LUKS slot that matches the LUKSMeta slot
# Get the old key
case "$KEY" in
"") read -s -p "Enter existing LUKS password: " old; echo;;
-) old=`/bin/cat`;;
*) old=`/bin/cat "$KEY"`;;
esac

echo -e "$old\n$key" | cryptsetup luksAddKey -S $SLT $DEV
# Add the new key
if [ -n "$SLT" ]; then
if ! echo -e "$old\n$key" | cryptsetup luksAddKey --key-slot $SLT $DEV; then
echo "Error while adding new key to LUKS header!" >&2
exit 1
fi
elif ! SLT=`echo -e "$old\n$key" \
| cryptsetup luksAddKey -v $DEV \
| sed -rn 's|^Key slot ([0-9]+) created\.$|\1|p'`; then
echo "Error while adding new key to LUKS header!" >&2
exit 1
fi

if cryptsetup isLuks --type luks1 "$DEV"; then
if ! echo -n $jwe | luksmeta save -d "$DEV" -u "$UUID" -s $SLT 2>/dev/null; then
echo "Error while saving Clevis metadata in LUKSMeta!" >&2
cryptsetup luksRemoveKey "$DEV" <<<"$key"
exit 1
fi
else
jwe=`jose jwe fmt -i- <<<"$jwe"` # Convert to JSON Serialization
tok="{\"type\":\"clevis\",\"keyslots\":[\"$SLT\"],\"jwe\":$jwe}"

if ! cryptsetup token import "$DEV" <<<"$tok" ; then
echo "Error while saving Clevis metadata as a LUKS token!" >&2
cryptsetup luksRemoveKey "$DEV" <<<"$key"
exit 1
fi
fi
73 changes: 43 additions & 30 deletions src/luks/clevis-luks-unbind
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

SUMMARY="Unbinds a pin bound to a LUKSv1 volume"
SUMMARY="Unbinds a pin bound to a LUKS volume"
UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e

function usage() {
Expand All @@ -29,7 +29,7 @@ function usage() {
echo >&2
echo " -d DEV The bound LUKS device" >&2
echo >&2
echo " -s SLOT The LUKSMeta slot number for the pin unbind" >&2
echo " -s SLOT The LUKS slot number for the pin unbind" >&2
echo >&2
echo " -f Do not ask for confirmation and wipe slot in batch-mode" >&2
echo >&2
Expand All @@ -55,45 +55,58 @@ if [ -z "$DEV" ]; then
usage
fi

if ! cryptsetup isLuks --type luks1 "$DEV"; then
echo "$DEV is not a LUKSv1 device!" >&2
exit 1
fi

if [ -z "$SLT" ]; then
echo "Did not specify a slot!" >&2
usage
fi

if ! luksmeta test -d $DEV 2>/dev/null; then
echo "The $DEV device is not valid!" >&2
if cryptsetup isLuks --type luks1 "$DEV"; then
if ! luksmeta test -d $DEV 2>/dev/null; then
echo "The $DEV device does not contain a LUKSMeta header!" >&2
exit 1
fi

read -r slot active uuid <<< $(luksmeta show -d "$DEV" | grep "^$SLT *")

if [ "$uuid" == "empty" ]; then
echo "The LUKSMeta slot $SLT on device $DEV is already empty." >&2
exit 1
fi

[ "$active" == "active" ] && KILL=true

elif cryptsetup isLuks --type luks2 "$DEV"; then
dump=`cryptsetup luksDump "$DEV"`
grep -q "^\s*$SLT: luks2" <<<"$dump" && KILL=true
TOK=`grep -E -B1 "^\s+Keyslot:\s+$SLT$" <<<"$dump" \
| sed -rn 's|^\s+([0-9]+): clevis|\1|p'`

else
echo "$DEV is not a supported LUKS device!" >&2
exit 1
fi

read -r slot active uuid <<< $(luksmeta show -d "$DEV" | grep "^$SLT *")

if [ "$uuid" = "empty" ]; then
echo "The LUKSMeta slot $SLT on device $DEV is already empty." >&2
exit 1
if [ -z "$FRC" ]; then
echo "The unbind operation will wipe a slot. This operation is unrecoverable." >&2
read -r -p "Do you wish to erase LUKS slot $SLT on $DEV? [ynYN] " ans < /dev/tty
[[ "$ans" =~ ^[yY]$ ]] || exit 0
fi

if [ "$active" = "active" ]; then
if [ -n "$KILL" ]; then
if ! cryptsetup luksKillSlot "$DEV" "$SLT" $FRC; then
echo "LUKSv1 slot $SLT for device $DEV couldn't be deleted"
exit 1
echo "LUKS slot $SLT for device $DEV couldn't be deleted"
exit 1
fi
else
echo "LUKSv1 slot $SLT not present on $DEV, only LUKSMeta slot will be cleared." >&2
if [ -z "$FRC" ]; then
echo "The unbind operation will wipe a slot. This operation is unrecoverable." >&2
read -r -p "Do you wish to erase LUKSMeta slot $SLT on $DEV? [ynYN] " ans < /dev/tty
[[ "$ans" =~ ^[yY]$ ]] || exit 0
fi
fi

if ! luksmeta wipe -f -d "$DEV" -u "$UUID" -s "$SLT"; then
echo "LUKSMeta slot $SLT for device $DEV couldn't be deleted"
exit 1
fi

exit 0
if cryptsetup isLuks --type luks1 "$DEV"; then
if ! luksmeta wipe -f -d "$DEV" -u "$UUID" -s "$SLT"; then
echo "LUKSMeta slot $SLT for device $DEV couldn't be deleted"
exit 1
fi
elif cryptsetup isLuks --type luks2 "$DEV" && [ -n "$TOK" ]; then
if ! cryptsetup token remove --token-id "$TOK" "$DEV"; then
echo "Error while removing token $TOK from LUKS device $DEV!" >&2
exit 1
fi
fi
31 changes: 22 additions & 9 deletions src/luks/clevis-luks-unlock
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

SUMMARY="Unlocks a LUKSv1 volume"
SUMMARY="Unlocks a LUKS volume"
UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e

function usage() {
Expand Down Expand Up @@ -54,14 +54,27 @@ fi

NAME=${NAME:-luks-`cryptsetup luksUUID $DEV`}

while read -r slot state uuid; do
[ "$state" != "active" ] && continue
[ "$uuid" != "$UUID" ] && continue
if cryptsetup isLuks --type luks1 "$DEV"; then
while read -r slot state uuid; do
[ "$state" != "active" ] && continue
[ "$uuid" != "$UUID" ] && continue

if pt="`luksmeta load -d $DEV -s $slot -u $UUID | clevis decrypt`"; then
echo -n "$pt" | cryptsetup open -d- "$DEV" "$NAME"
exit 0
fi
done <<< "$(luksmeta show -d "$DEV")"
if pt=`luksmeta load -d $DEV -s $slot -u $UUID | clevis decrypt`; then
echo -n "$pt" | cryptsetup open -d- "$DEV" "$NAME"
exit 0
fi
done <<< "$(luksmeta show -d "$DEV")"

elif cryptsetup isLuks --type luks2 "$DEV"; then
for id in `cryptsetup luksDump "$DEV" | sed -rn 's|^\s+([0-9]+): clevis|\1|p'`; do
tok=`cryptsetup token export --token-id "$id" "$DEV"`
jwe=`jose fmt -j- -Og jwe -o- <<<"$tok" | jose jwe fmt -i- -c`

if pt=`echo -n "$jwe" | clevis decrypt`; then
echo -n "$pt" | cryptsetup open -d- "$DEV" "$NAME"
exit 0
fi
done
fi

exit 1
45 changes: 31 additions & 14 deletions src/luks/systemd/clevis-luks-askpass
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e

shopt -s nullglob

while getopts ":l" o; do
path=/run/systemd/ask-password
while getopts ":lp:" o; do
case "$o" in
l) loop=true;;
p) path=$OPTARG;;
esac
done

while true; do
todo=0

for question in /run/systemd/ask-password/ask.*; do
for question in $path/ask.*; do
metadata=false
unlocked=false
d=
Expand All @@ -47,20 +49,35 @@ while true; do

[ -z "$d" -o -z "$s" ] && continue

# If the device is not initialized, sliently skip it.
luksmeta test -d "$d" || continue
if cryptsetup isLuks --type luks1 "$d"; then
# If the device is not initialized, sliently skip it.
luksmeta test -d "$d" || continue

while read -r slot state uuid; do
[ "$state" != "active" ] && continue
[ "$uuid" != "$UUID" ] && continue
metadata=true
while read -r slot state uuid; do
[ "$state" != "active" ] && continue
[ "$uuid" != "$UUID" ] && continue
metadata=true

if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then
echo -n "+$pt" | nc -U -u --send-only "$s"
unlocked=true
break
fi
done < <(luksmeta show -d "$d")
if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then
echo -n "+$pt" | nc -U -u --send-only "$s"
unlocked=true
break
fi
done < <(luksmeta show -d "$d")
elif cryptsetup isLuks --type luks2 "$d"; then
ids=`cryptsetup luksDump "$d" | sed -rn 's|^\s+([0-9]+): clevis|\1|p'`
for id in $ids; do
tok=`cryptsetup token export --token-id "$id" "$d"`
jwe=`jose fmt -j- -Og jwe -o- <<<"$tok" | jose jwe fmt -i- -c`
metadata=true

if pt=`echo -n "$jwe" | clevis decrypt`; then
echo -n "+$pt" | nc -U -u --send-only "$s"
unlocked=true
break
fi
done
fi

[ $metadata == true ] || continue
[ $unlocked == true ] && continue
Expand Down
Loading

0 comments on commit 84ccd62

Please sign in to comment.