Skip to content

Commit f4ed006

Browse files
tang: default JWK thumbprint is now SHA-256 / deprecate SHA-1
During encryption using tang, the clevis metadata stores the thumbprint of the exchange key in the `kid' attribute. This thumbprint used jose default thumbprint hash algorithm -- SHA-1 as of now, even though RFC 7638 recommends at least SHA-256. During decryption with tang, we then validate the key with the thumbprint represented by `kid' is present in the advertisement. At this point, we used again jose default thumbprint hash for this check. We do have the JWK, though, so we are able to recalculate the thumbprint with different algorithms to make sure it matches. clevis now moved to use SHA-256 for this thumbprint, and deprecated SHA-1 use. It still supports SHA-1 so that previously encrypted data can still be decrypted, but a warning will be issued when decryption finds a thumbprint using a deprecated hash.
1 parent 490c1b6 commit f4ed006

File tree

5 files changed

+158
-8
lines changed

5 files changed

+158
-8
lines changed

src/luks/clevis-luks-common-functions.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ clevis_luks_unlock_device_by_slot() {
323323
return 1
324324
fi
325325

326-
if ! passphrase="$(printf '%s' "${jwe}" | clevis decrypt 2>/dev/null)" \
326+
if ! passphrase="$(printf '%s' "${jwe}" | clevis decrypt)" \
327327
|| [ -z "${passphrase}" ]; then
328328
return 1
329329
fi

src/pins/tang/clevis-decrypt-tang

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,38 @@ if ! kid="$(jose fmt -j- -Og kid -Su- <<< "$jhd")"; then
5050
exit 1
5151
fi
5252

53-
if ! srv="$(jose fmt -j- -Og clevis -g tang -g adv -Oo- <<< "$jhd" \
54-
| jose jwk thp -i- -f "$kid")"; then
53+
# Tang advertisement validation.
54+
if ! keys="$(jose fmt -j- -Og clevis -g tang -g adv -Oo- <<< "${jhd}")"; then
5555
echo "JWE missing required 'clevis.tang.adv' header parameter!" >&2
5656
exit 1
5757
fi
5858

59+
# Check if the thumbprint we have in `kid' is in the advertised keys.
60+
CLEVIS_DEFAULT_THP_ALG=S256 # SHA-256.
61+
CLEVIS_DEFAULT_THP_LEN=43 # Length of SHA-256 thumbprint.
62+
CLEVIS_ALTERNATIVE_THP_ALGS=S1 # SHA-1.
63+
64+
# Issue a warning if we are using a hash that has a shorter length than the
65+
# default one.
66+
if [ "${#kid}" -lt "${CLEVIS_DEFAULT_THP_LEN}" ]; then
67+
echo "WARNING: tang using a deprecated hash for the JWK thumbprints" >&2
68+
fi
69+
70+
if ! srv="$(jose jwk thp -i- -f "${kid}" -a "${CLEVIS_DEFAULT_THP_ALG}" \
71+
<<< "${keys}")"; then
72+
# `kid' thumprint not in the advertised keys, but it's possible it was
73+
# generated using a different algorithm than the default one.
74+
# Let us try the alternative supported algorithms to make sure `kid'
75+
# really is not part of the advertised keys.
76+
for alg in ${CLEVIS_ALTERNATIVE_THP_ALGS}; do
77+
srv="$(jose jwk thp -i- -f "$kid" -a "${alg}" <<< "${keys}")" && break
78+
done
79+
if [ -z "${srv}" ]; then
80+
echo "JWE header validation of 'clevis.tang.adv' failed: key thumbprint does not match" >&2
81+
exit 1
82+
fi
83+
fi
84+
5985
if ! url="$(jose fmt -j- -Og clevis -g tang -g url -Su- <<< "$jhd")"; then
6086
echo "JWE missing required 'clevis.tang.url' header parameter!" >&2
6187
exit 1

src/pins/tang/clevis-encrypt-tang

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ if ! cfg="$(jose fmt -j- -Oo- <<< "$1" 2>/dev/null)"; then
6464
exit 1
6565
fi
6666

67+
CLEVIS_DEFAULT_THP_ALG=S256 # SHA-256.
68+
CLEVIS_ALTERNATIVE_THP_ALGS=S1 # SHA-1.
69+
6770
trust=
6871
[ -n "${2}" ] && [ "${2}" == "-y" ] && trust=yes
6972

@@ -112,15 +115,25 @@ if [ -z "${trust}" ]; then
112115
if [ -z "$thp" ]; then
113116
echo "The advertisement contains the following signing keys:" >&2
114117
echo >&2
115-
jose jwk thp -i- <<< "$ver" >&2
118+
jose jwk thp -i- -a "${CLEVIS_DEFAULT_THP_ALG}" <<< "$ver" >&2
116119
echo >&2
117120
read -r -p "Do you wish to trust these keys? [ynYN] " ans < /dev/tty
118121
[[ "$ans" =~ ^[yY]$ ]] || exit 1
119122

120123
elif [ "$thp" != "any" ] && \
121-
! jose jwk thp -i- -f "$thp" -o /dev/null <<< "$ver"; then
122-
echo "Trusted JWK '$thp' did not sign the advertisement!" >&2
123-
exit 1
124+
! jose jwk thp -i- -f "${thp}" -a "${CLEVIS_DEFAULT_THP_ALG}" \
125+
<<< "$ver"; then
126+
# Thumbprint of trusted JWK did not match the signature. Let's check
127+
# alternative thumbprints generated with clevis supported hash
128+
# algorithms to be sure.
129+
for alg in ${CLEVIS_ALTERNATIVE_THP_ALGS}; do
130+
srv="$(jose jwk thp -i- -f "${thp}" -a "${alg}" <<< "${ver}")" \
131+
&& break
132+
done
133+
if [ -z "${srv}" ]; then
134+
echo "Trusted JWK '$thp' did not sign the advertisement!" >&2
135+
exit 1
136+
fi
124137
fi
125138
fi
126139

@@ -139,7 +152,7 @@ fi
139152

140153
jwk="$(jose fmt -j- -Od key_ops -o- <<< "$jwk")"
141154
jwk="$(jose fmt -j- -Od alg -o- <<< "$jwk")"
142-
kid="$(jose jwk thp -i- <<< "$jwk")"
155+
kid="$(jose jwk thp -i- -a "${CLEVIS_DEFAULT_THP_ALG}" <<< "$jwk")"
143156
jwe='{"protected":{"alg":"ECDH-ES","enc":"A256GCM","clevis":{"pin":"tang","tang":{}}}}'
144157
jwe="$(jose fmt -j "$jwe" -g protected -q "$kid" -s kid -UUo-)"
145158
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g tang -q "$url" -s url -UUUUo-)"
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/bin/bash
2+
set -exo pipefail
3+
# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
4+
#
5+
# Copyright (c) 2020 Red Hat, Inc.
6+
# Author: Sergio Correia <[email protected]>
7+
#
8+
# This program is free software: you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation, either version 3 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
22+
. tang-common-test-functions
23+
24+
TEST=$(basename "${0}")
25+
26+
on_exit() {
27+
exit_status=$?
28+
tang_stop "${TMP}"
29+
[ -d "${TMP}" ] && rm -rf "${TMP}"
30+
exit "${exit_status}"
31+
}
32+
33+
trap 'on_exit' EXIT
34+
35+
TMP="$(mktemp -d)"
36+
37+
port=$(tang_new_random_port)
38+
tang_run "${TMP}" "${port}"
39+
40+
url="http://localhost:${port}"
41+
data="just a sample text"
42+
43+
# Get the advertisement and extract the keys.
44+
adv="$(tang_get_adv "${port}")"
45+
46+
jwks="$(jose fmt --json="${adv}" --get payload --b64load --output=-)"
47+
enc="$(printf '%s' "${jwks}" | jose jwk use --input=- --required \
48+
--use deriveKey --output=-)"
49+
50+
jose fmt --json="${enc}" --get keys --array \
51+
|| enc="$(printf '{"keys": [%s]}' "${enc}")"
52+
53+
jwk="$(jose fmt --json="${enc}" --get keys --array --foreach=- \
54+
| jose fmt --json=- --delete key_ops --delete alg --output=-)"
55+
56+
jwe_t='{"protected":{"alg":"ECDH-ES","enc":"A256GCM","clevis":{"pin":"tang","tang":{}}}}'
57+
jwe_t="$(jose fmt --json="${jwe_t}" --get protected --get clevis --get tang --quote "${url}" --set url -UUUUo-)"
58+
jwe_t="$(printf '%s' "${jwks}" | jose fmt --json="${jwe_t}" --get protected --get clevis --get tang --json=- --set adv -UUUUo-)"
59+
60+
# We currently support SHA-1 (legacy) and SHA-256.
61+
CLEVIS_SUPPORTED_THP_ALGS="S1 S256"
62+
# Now we will use every hash algorithm supported by jose to create a thumbprint
63+
# for `kid', then we do the encoding and verify clevis decrypt can decode it
64+
# correctly.
65+
for alg in ${CLEVIS_SUPPORTED_THP_ALGS}; do
66+
kid="$(printf '%s' "${jwk}" | jose jwk thp -a "${alg}" --input=-)"
67+
jwe="$(jose fmt --json="${jwe_t}" --get protected --quote "${kid}" -s kid -UUo-)"
68+
69+
encoded=$(printf '%s%s' "${jwk}" "${data}" \
70+
| jose jwe enc --input="${jwe}" --key=- --detached=- --compact)
71+
72+
if ! decoded="$(printf '%s' "${encoded}" | clevis decrypt)"; then
73+
tang_error "${TEST}: decoding is expected to work (alg = ${alg})"
74+
fi
75+
76+
if [ "${decoded}" != "${data}" ]; then
77+
tang_error "${TEST}: tang decrypt should have succeeded decoded[${decoded}] data[${data}] (alg = ${alg})"
78+
fi
79+
done
80+
81+
# Now let's test encryption providing the thp in the configuration.
82+
for alg in ${CLEVIS_SUPPORTED_THP_ALGS}; do
83+
thp="$(jose fmt --json="${adv}" -g payload -y -o- \
84+
| jose jwk use -i- -r -u verify -o- \
85+
| jose jwk thp -i- -a "${alg}")"
86+
cfg="$(printf '{"url":"%s", "thp":"%s"}' "${url}" "${thp}")"
87+
if ! echo foo | clevis encrypt tang "${cfg}" >/dev/null; then
88+
tang_error "${TEST}: tang encryption should have succeeded when providing the thp (${thp}) with any supported algorithm (${alg})"
89+
fi
90+
done
91+
92+
# Let's also try some unsupported thp hash algorithms.
93+
UNSUPPORTED="S224 S384 S512" # SHA-224, SHA-384, SHA-512.
94+
for alg in ${UNSUPPORTED}; do
95+
thp="$(jose fmt --json="${adv}" -g payload -y -o- \
96+
| jose jwk use -i- -r -u verify -o- \
97+
| jose jwk thp -i- -a "${alg}")"
98+
cfg="$(printf '{"url":"%s", "thp":"%s"}' "${url}" "${thp}")"
99+
if echo foo | clevis encrypt tang "${cfg}" >/dev/null; then
100+
tang_error "${TEST}: tang encryption should have failed when providing the thp (${thp}) with an unsupported algorithm (${alg})"
101+
fi
102+
done
103+
104+
# Now let's try some bad values for thp.
105+
for thp in "" "foo" "invalid"; do
106+
cfg="$(printf '{"url":"%s", "thp":"%s"}' "${url}" "${thp}")"
107+
if echo foo | clevis encrypt tang "${cfg}" >/dev/null; then
108+
tang_error "${TEST}: tang encryption expected to fail when providing a bad thp"
109+
fi
110+
done

src/pins/tang/tests/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,4 @@ env.prepend('PATH',
6363

6464
test('pin-tang', find_program('pin-tang'), env: env)
6565
test('tang-validate-adv', find_program('tang-validate-adv'), env: env)
66+
test('default-thp-alg', find_program('default-thp-alg'), env: env)

0 commit comments

Comments
 (0)