summaryrefslogtreecommitdiffstats
path: root/test/units/TEST-70-TPM2.cryptsetup.sh
blob: cb7c8b1f3130c7cffd306b5e91310560f2d9efaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail

# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh

export SYSTEMD_LOG_LEVEL=debug

cryptsetup_has_token_plugin_support() {
    local plugin_path

    plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)"
    cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]]
}

tpm_check_failure_with_wrong_pin() {
    local testIMAGE="${1:?}"
    local badpin="${2:?}"
    local goodpin="${3:?}"

    # We need to be careful not to trigger DA lockout; allow 2 failures
    tpm2_dictionarylockout -s -n 2
    (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1)
    # Verify the correct PIN works, to be sure the failure wasn't a DA lockout
    PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1
    systemd-cryptsetup detach test-volume
    # Clear/reset the DA lockout counter
    tpm2_dictionarylockout -c
}

at_exit() {
    # Evict the TPM primary key that we persisted
    if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then
        tpm2_evictcontrol -c "$PERSISTENT_HANDLE"
    fi
}

trap at_exit EXIT

# Prepare a fresh disk image
IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)"

truncate -s 20M "$IMAGE"
echo -n passphrase >/tmp/passphrase
# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages
chmod 0600 /tmp/passphrase
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase

# Unlocking via keyfile
systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE"

# Enroll unlock with default PCR policy
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# Check with wrong PCR
tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)

# Enroll unlock with PCR+PIN policy
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout
for _ in {0..3}; do
    tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
done

# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
if cryptsetup_has_token_plugin_support; then
    PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1
    systemd-cryptsetup detach test-volume

    # Check failure with wrong PIN
    for _ in {0..3}; do
        tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
    done
else
    echo 'cryptsetup has no LUKS2 token plugin support, skipping'
fi

# Check failure with wrong PCR (and correct PIN)
tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)

# Enroll unlock with PCR 0+7
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# Check with wrong PCR 0
tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)

if tpm_has_pcr sha256 12; then
    # Enroll using an explicit PCR value (that does match current PCR value)
    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
    systemd-cryptsetup detach test-volume

    # Same as above plus more PCRs without the value or alg specified
    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
    systemd-cryptsetup detach test-volume

    # Same as above plus more PCRs with hash alg specified but hash value not specified
    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
    systemd-cryptsetup detach test-volume

    # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
    EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
    (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
    tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
    systemd-cryptsetup detach test-volume

    # enroll TPM using device key instead of direct access, then verify unlock using TPM
    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
    systemd-analyze srk > /tmp/srk2.pub
    cmp /tmp/srk.pub /tmp/srk2.pub
    if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then
        cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public
    fi

    # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
    if openssl_supports_kdf SSKDF; then
        PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
        systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
        systemd-cryptsetup detach test-volume
    fi

    rm -f /tmp/pcr.dat /tmp/srk.pub
fi

# Use default (0) seal key handle
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# Use SRK seal key handle
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# Test invalid ranges: pcr, nv, session, permanent
systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE")          # PCR
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session
(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent

# Use non-SRK persistent seal key handle (by creating/persisting new key)
PRIMARY=/tmp/primary.ctx
tpm2_createprimary -c "$PRIMARY"
PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
tpm2_flushcontext -t

systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
systemd-cryptsetup detach test-volume

# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
if openssl_supports_kdf SSKDF; then
    # Make sure that --tpm2-device-key= also works with systemd-repart
    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
    mkdir /tmp/dditest
    cat > /tmp/dditest/50-root.conf <<EOF
[Partition]
Type=root
Format=ext4
CopyFiles=/tmp/dditest:/
Encrypt=tpm2
EOF
    PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs=
    DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)"
    udevadm wait --settle --timeout=10 "$DEVICE"p1
    systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
    mkdir /tmp/dditest.mnt
    mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
    cmp /tmp/dditest.mnt/50-root.conf /tmp/dditest/50-root.conf
    umount /tmp/dditest.mnt
    rmdir /tmp/dditest.mnt
    rm /tmp/dditest.raw
    rm /tmp/dditest/50-root.conf
    rmdir /tmp/dditest
fi

rm -f "$IMAGE" "$PRIMARY"