Skip to content

Commit

Permalink
Handle systemd TPM2 + passphrase
Browse files Browse the repository at this point in the history
Counterpart implementation of
systemd/systemd#22563

Implements #198
  • Loading branch information
anatol committed Dec 22, 2022
1 parent 25fbdac commit caf14cb
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 6 deletions.
16 changes: 15 additions & 1 deletion init/luks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
Expand Down Expand Up @@ -241,6 +242,7 @@ func recoverSystemdTPM2Password(t luks.Token) ([]byte, error) {
PCRs []int `json:"tpm2-pcrs"`
PCRBank string `json:"tpm2-pcr-bank"` // either sha1 or sha256
PolicyHash string `json:"tpm2-policy-hash"` // base64
Pin bool `json:"tpm2-pin"`
}
if err := json.Unmarshal(t.Payload, &node); err != nil {
return nil, err
Expand Down Expand Up @@ -271,7 +273,19 @@ func recoverSystemdTPM2Password(t luks.Token) ([]byte, error) {

bank := parsePCRBank(node.PCRBank)

password, err := tpm2Unseal(public, private, node.PCRs, bank, policyHash)
var authValue []byte
if node.Pin {
prompt := fmt.Sprintf("Please enter TPM pin: ")
pin, err := readPassword(prompt, "")
if err != nil {
return nil, err
}

hash := sha256.Sum256(pin)
authValue = hash[:]
}

password, err := tpm2Unseal(public, private, node.PCRs, bank, policyHash, authValue)
if err != nil {
return nil, err
}
Expand Down
14 changes: 10 additions & 4 deletions init/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func tpmAwaitReady() bool {
return !timedOut
}

func tpm2Unseal(public, private []byte, pcrs []int, bank tpm2.Algorithm, policyHash []byte) ([]byte, error) {
func tpm2Unseal(public, private []byte, pcrs []int, bank tpm2.Algorithm, policyHash, password []byte) ([]byte, error) {
tpmAwaitReady()

dev, err := openTPM()
Expand All @@ -68,7 +68,7 @@ func tpm2Unseal(public, private []byte, pcrs []int, bank tpm2.Algorithm, policyH
return nil, fmt.Errorf("open %s: device is not a TPM 2.0", dev)
}

sessHandle, _, err := policyPCRSession(dev, pcrs, bank, policyHash)
sessHandle, _, err := policyPCRSession(dev, pcrs, bank, policyHash, password != nil)
if err != nil {
return nil, err
}
Expand All @@ -95,7 +95,7 @@ func tpm2Unseal(public, private []byte, pcrs []int, bank tpm2.Algorithm, policyH
}
defer tpm2.FlushContext(dev, objectHandle)

unsealed, err := tpm2.UnsealWithSession(dev, sessHandle, objectHandle, "")
unsealed, err := tpm2.UnsealWithSession(dev, sessHandle, objectHandle, string(password))
if err != nil {
return nil, fmt.Errorf("unable to unseal data: %v", err)
}
Expand All @@ -114,7 +114,7 @@ func parsePCRBank(bank string) tpm2.Algorithm {
}

// Returns session handle and policy digest.
func policyPCRSession(dev io.ReadWriteCloser, pcrs []int, algo tpm2.Algorithm, expectedDigest []byte) (handle tpmutil.Handle, policy []byte, retErr error) {
func policyPCRSession(dev io.ReadWriteCloser, pcrs []int, algo tpm2.Algorithm, expectedDigest []byte, usePassword bool) (handle tpmutil.Handle, policy []byte, retErr error) {
// This session assumes the bus is trusted, so we:
// - use nil for tpmkey, encrypted salt, and symmetric
// - use and all-zeros caller nonce, and ignore the returned nonce
Expand Down Expand Up @@ -144,6 +144,12 @@ func policyPCRSession(dev io.ReadWriteCloser, pcrs []int, algo tpm2.Algorithm, e
return tpm2.HandleNull, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err)
}

if usePassword {
if err := tpm2.PolicyPassword(dev, sessHandle); err != nil {
return tpm2.HandleNull, nil, err
}
}

policy, err = tpm2.PolicyGetDigest(dev, sessHandle)
if err != nil {
return tpm2.HandleNull, nil, fmt.Errorf("unable to get policy digest: %v", err)
Expand Down
1 change: 1 addition & 0 deletions tests/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var assetGenerators = map[string]assetGenerator{
"alpinelinux.img": {"alpinelinux.sh", nil},
"systemd-fido2.img": {"systemd_fido2.sh", []string{"LUKS_UUID=b12cbfef-da87-429f-ac96-7dda7232c189", "FS_UUID=bb351f0d-07f2-4fe4-bc53-d6ae39fa1c23", "LUKS_PASSWORD=567", "FIDO2_PIN=1111"}}, // use yubikey-manager-qt (or fido2-token -C) to setup FIDO2 pin value to 1111
"systemd-tpm2.img": {"systemd_tpm2.sh", []string{"LUKS_UUID=5cbc48ce-0e78-4c6b-ac90-a8a540514b90", "FS_UUID=d8673e36-d4a3-4408-a87d-be0cb79f91a2", "LUKS_PASSWORD=567"}},
"systemd-tpm2-withpin.img": {"systemd_tpm2.sh", []string{"LUKS_UUID=8bb97618-7ef4-4c93-b4f7-f2cb17cf7da1", "FS_UUID=26dbbe17-9af9-4322-bb5f-c1d74a40e618", "LUKS_PASSWORD=9999", "CRYPTENROLL_TPM2_PIN=foo654"}},
"systemd-recovery.img": {"systemd_recovery.sh", []string{"LUKS_UUID=62020168-58b9-4095-a3d0-176403353d20", "FS_UUID=b0cfeb48-c1e2-459d-a327-4d611804ac24", "LUKS_PASSWORD=2211"}},
"swap.raw": {"swap.sh", nil},
"zfs.img": {"zfs.sh", nil},
Expand Down
11 changes: 10 additions & 1 deletion tests/generators/systemd_tpm2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ sudo cryptsetup luksFormat --uuid "${LUKS_UUID}" --type luks2 "${lodev}" <<< "${

printf '%s' "${LUKS_PASSWORD}" > assets/cryptenroll.passphrase
# it looks like edk2 extends PCR 0-7 so let's use some other PCR outside of this range
sudo CREDENTIALS_DIRECTORY="$(pwd)/assets" systemd-cryptenroll --tpm2-device=swtpm: --tpm2-pcrs=10+13 "${lodev}"
if [ "${CRYPTENROLL_TPM2_PIN}" != "" ]; then
printf '%s' "${CRYPTENROLL_TPM2_PIN}" > assets/cryptenroll.tpm2-pin
sudo CREDENTIALS_DIRECTORY="$(pwd)/assets" systemd-cryptenroll --tpm2-device=swtpm: --tpm2-pcrs=10+13 --tpm2-with-pin=true "${lodev}"
else
sudo CREDENTIALS_DIRECTORY="$(pwd)/assets" systemd-cryptenroll --tpm2-device=swtpm: --tpm2-pcrs=10+13 "${lodev}"
fi

sudo cryptsetup open --disable-external-tokens --type luks2 "${lodev}" "${LUKS_DEV_NAME}" <<< "${LUKS_PASSWORD}"
sudo mkfs.ext4 -U "${FS_UUID}" -L atestlabel12 "/dev/mapper/${LUKS_DEV_NAME}"
Expand All @@ -34,3 +39,7 @@ sudo mount "/dev/mapper/${LUKS_DEV_NAME}" "${dir}"
sudo chown "${USER}" "${dir}"
mkdir "${dir}/sbin"
cp assets/init "${dir}/sbin/init"

if [ "${CRYPTENROLL_TPM2_PIN}" != "" ]; then
sudo cryptsetup -v luksKillSlot "${lodev}" 0
fi
20 changes: 20 additions & 0 deletions tests/systemd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ func TestSystemdTPM2(t *testing.T) {
require.NoError(t, vm.ConsoleExpect("Hello, booster!"))
}

func TestSystemdTPM2WithPin(t *testing.T) {
swtpm, params, err := startSwtpm()
require.NoError(t, err)
defer swtpm.Kill()

vm, err := buildVmInstance(t, Opts{
disk: "assets/systemd-tpm2-withpin.img",
kernelArgs: []string{"rd.luks.uuid=8bb97618-7ef4-4c93-b4f7-f2cb17cf7da1", "root=UUID=26dbbe17-9af9-4322-bb5f-c1d74a40e618"},
params: params,
extraFiles: "fido2-assert",
})
require.NoError(t, err)
defer vm.Shutdown()

require.NoError(t, vm.ConsoleExpect("Please enter TPM pin:"))
require.NoError(t, vm.ConsoleWrite("foo654\n"))

require.NoError(t, vm.ConsoleExpect("Hello, booster!"))
}

func TestSystemdRecovery(t *testing.T) {
vm, err := buildVmInstance(t, Opts{
disk: "assets/systemd-recovery.img",
Expand Down

0 comments on commit caf14cb

Please sign in to comment.