forked from olastor/clevis-pin-fido2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclevis-encrypt-fido2
executable file
·182 lines (155 loc) · 7.03 KB
/
clevis-encrypt-fido2
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
#!/bin/bash
SUMMARY="Encrypts using a fido2 token that supports the hmac-secret extension"
DEFAULT_TIMEOUT='120'
if [ "$1" == "--summary" ]; then
echo "$SUMMARY"
exit 0
fi
if [ -t 0 ]; then
exec >&2
echo
echo "Usage: clevis encrypt fido2 CONFIG < PLAINTEXT > JWE"
echo
echo "$SUMMARY"
echo
echo "This command uses the following optional configuration properties:"
echo
echo ' type: <string> Algorithm to use for the credential ("es256", "rs256" or "eddsa").'
echo ' (Default: "es256")'
echo
echo ' cred_id: <string> A base64-encoded credential ID as generated by fido2-cred to use'
echo ' instead of generating a new one. By default, a new credential ID'
echo ' is generated for encryption, requiring additional verification of'
echo ' user presence.'
echo
echo ' rp_id: <string> The reyling party id of the credential. (Default: "fido2.clevis")'
echo
echo ' up: <boolean> Whether or not to enable user presence. (Default: true)'
echo
echo ' uv: <boolean> Whether or not to enable user verification. (Default: false)'
echo
echo ' pin: <boolean> Whether or not to require a PIN. (Default: false)'
echo
echo ' device: <string> The device path to use (e.g., "/dev/input/by-id/my-yubikey") for'
echo ' encryption and for every future decryption. A temporary path starting'
echo ' with "/dev/hid" must be specified in the FIDO2_TOKEN env variable instead.'
echo ' See man page "clevis-encrypt-fido2" for more information. (Default: "")'
echo
echo ' timeout: <string> Number of seconds to wait for device to become available.'
echo ' Timeout will be applied for encryption and every decryption.'
echo ' Can be overridden temporarily via the TIMEOUT env variable.'
echo ' (Default: 50)'
echo
exit 2
fi
function create_credential () {
local device
local rp_id
local type
device="$1"
rp_id="$2"
type="$3"
client_data="$(dd if=/dev/urandom bs=1 count=32 status=none | base64 -w0)"
user_id="$(echo -n 'clevis' | base64 -w0)"
cred_id="$(printf '%s\n%s\n%s\n%s\n' "${client_data}" "${rp_id}" 'clevis' "${user_id}" \
| fido2-cred -M -h "${device}" "${type}" \
| head -n5 | tail -n1)" >&2
echo -n "${cred_id}"
}
function generate_hmac () {
local device
local rp_id
local cred_id
local hmac_salt
device="$1"
rp_id="$2"
cred_id="$3"
hmac_salt="$4"
f2a_uv="-t uv=${uv}"
if [ "${uv}" == "false" ] && ! fido2-token -I "${fido2_token}" | grep -qE "options:.* uv" ; then
# Yubikey 5 NFC w/ libfido2 v1.14.0 does _not_ allow to explicitly specify "-t uv=true|false" below
# (cf. https://github.com/Yubico/libfido2/issues/642#issuecomment-1303673367: "`uv` option [...] controls
# device-native UV such as biometrics or on-authenticator PIN pad")
f2a_uv=""
fi
client_hash="$(dd if=/dev/urandom bs=1 count=32 status=none | base64 -w0)"
# shellcheck disable=SC2086
hmac="$(printf '%s\n%s\n%s\n%s\n' "${client_hash}" "${rp_id}" "${cred_id}" "${hmac_salt}" | \
fido2-assert -G -h ${f2a_uv} -t "up=${up}" -t "pin=${pin}" "${device}" | \
head -n5 | tail -n1 | base64 -d | jose b64 enc -I -)" >&2
echo -n "${hmac}"
}
cfg=''
if ! cfg="$(jose fmt -j- -Oo- <<< "$1" 2>/dev/null)"; then
echo "${0##*/}: Error: Configuration is malformed!" >&2
exit 1
fi
type="$(jose fmt -j- -Og type -Bo- <<< "$cfg")" || type='es256'
uv="$(jose fmt -j- -Og uv -Bo- <<< "$cfg")" || uv='false'
up="$(jose fmt -j- -Og up -Bo- <<< "$cfg")" || up='true'
pin="$(jose fmt -j- -Og pin -Bo- <<< "$cfg")" || pin='false'
timeout="$(jose fmt -j- -Og timeout -Bo- <<< "$cfg")" || timeout=''
rp_id="$(jose fmt -j- -Og rp_id -Su- <<< "$cfg")" || rp_id='fido2.clevis'
device="$(jose fmt -j- -Og device -u- <<< "$cfg")" || device=''
if [[ "${device}" =~ /dev/hid ]]; then
echo "${0##*/}: Error: If the device path is ephemeral and starts with '/dev/hid', the FIDO2_TOKEN env variable must be used instead." >&2
exit 1
fi
fido2_token="${FIDO2_TOKEN:-$device}"
show_msg=1
user_timeout="${TIMEOUT:-$timeout}"
timeout1="${user_timeout:-$DEFAULT_TIMEOUT}"
while [[ ! -c "${fido2_token}" ]]; do
sleep 1
if [[ -z "${fido2_token}" ]]; then
fido2_tokens="$(fido2-token -L)"
if [[ -z "${fido2_tokens}" && show_msg -eq 1 ]]; then
echo "${0##*/}: Please insert your FIDO2 token" >&2
show_msg=0
fi
fido2_token="$(echo "${fido2_tokens}" | head -n1 | cut -d':' -f1)"
num_tokens="$(echo "${fido2_tokens}" | wc -l)"
if ((num_tokens > 1)); then
echo "${0##*/}: Warning: There are multiple tokens. Will use the first one (${fido2_token})." >&2
fi
if [[ -c "${fido2_token}" ]]; then
# prevent running into the timeout
break
fi
elif [[ show_msg -eq 1 ]]; then
echo "${0##*/}: Please insert your specified FIDO2 token ${fido2_token}" >&2
show_msg=0
fi
# see 'sleep 1' above--we're keeping things simple here (cf. https://unix.stackexchange.com/a/156133/238272)
timeout1=$((timeout1-1))
if [[ ${timeout1} -lt 1 ]]; then
if [[ -z "${fido2_token}" ]]; then
echo "${0##*/}: Error: No FIDO2 token found within ${timeout} seconds." >&2
else
echo "${0##*/}: Error: specified FIDO2 token ${fido2_token} not found within ${timeout} seconds." >&2
fi
exit 1
fi
done
cred_id="$(jose fmt -j- -Og cred_id -Su- <<< "$cfg")" || cred_id="$(create_credential "${fido2_token}" "${rp_id}" "${type}")"
# generate a random salt for each encrypted payload
hmac_salt="$(dd if=/dev/urandom bs=1 count=32 status=none | base64 -w0)"
# retrieve the hmac result which will be the password to use for key wrapping a CEK.
hmac="$(generate_hmac "${fido2_token}" "${rp_id}" "${cred_id}" "${hmac_salt}")"
if [ -z "${hmac}" ]; then
echo "${0##*/}: Error: could not generate key." >&2
exit 1
fi
jwk='{"kty":"oct", "alg":"A256GCM"}'
jwk="$(jose fmt -j "${jwk}" -q "${hmac}" -s k -Uo-)"
jwe='{"protected":{"enc":"A256GCM","clevis":{"pin":"fido2","fido2":{}}}}'
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${type}" -s type -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${hmac_salt}" -s hmac_salt -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${rp_id}" -s rp_id -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${cred_id}" -s cred_id -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${uv}" -s uv -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${up}" -s up -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${pin}" -s pin -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${timeout}" -s timeout -UUUUo-)"
jwe="$(jose fmt -j "$jwe" -g protected -g clevis -g fido2 -q "${device}" -s device -UUUUo-)"
exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)