diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:35:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:35:18 +0000 |
commit | b750101eb236130cf056c675997decbac904cc49 (patch) | |
tree | a5df1a06754bdd014cb975c051c83b01c9a97532 /src/cryptsetup/cryptsetup-tokens/luks2-fido2.c | |
parent | Initial commit. (diff) | |
download | systemd-upstream.tar.xz systemd-upstream.zip |
Adding upstream version 252.22.upstream/252.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/luks2-fido2.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c new file mode 100644 index 0000000..a0e1ccb --- /dev/null +++ b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <libcryptsetup.h> + +#include "cryptsetup-token-util.h" +#include "hexdecoct.h" +#include "json.h" +#include "luks2-fido2.h" +#include "memory-util.h" +#include "strv.h" + +int acquire_luks2_key( + struct crypt_device *cd, + const char *json, + const char *device, + const char *pin, + char **ret_keyslot_passphrase, + size_t *ret_keyslot_passphrase_size) { + + int r; + Fido2EnrollFlags required; + size_t cid_size, salt_size, decrypted_key_size; + _cleanup_free_ void *cid = NULL, *salt = NULL; + _cleanup_free_ char *rp_id = NULL; + _cleanup_(erase_and_freep) void *decrypted_key = NULL; + _cleanup_(erase_and_freep) char *base64_encoded = NULL; + _cleanup_strv_free_erase_ char **pins = NULL; + + assert(ret_keyslot_passphrase); + assert(ret_keyslot_passphrase_size); + + r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required); + if (r < 0) + return r; + + if (pin) { + pins = strv_new(pin); + if (!pins) + return crypt_log_oom(cd); + } + + /* configured to use pin but none was provided */ + if ((required & FIDO2ENROLL_PIN) && strv_isempty(pins)) + return -ENOANO; + + r = fido2_use_hmac_hash( + device, + rp_id ?: "io.systemd.cryptsetup", + salt, salt_size, + cid, cid_size, + pins, + required, + &decrypted_key, + &decrypted_key_size); + if (r == -ENOLCK) /* libcryptsetup returns -ENOANO also on wrong PIN */ + r = -ENOANO; + if (r < 0) + return r; + + /* Before using this key as passphrase we base64 encode it, for compat with homed */ + r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); + if (r < 0) + return crypt_log_error_errno(cd, r, "Failed to base64 encode key: %m"); + + *ret_keyslot_passphrase = TAKE_PTR(base64_encoded); + *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase); + + return 0; +} + +/* this function expects valid "systemd-fido2" in json */ +int parse_luks2_fido2_data( + struct crypt_device *cd, + const char *json, + char **ret_rp_id, + void **ret_salt, + size_t *ret_salt_size, + void **ret_cid, + size_t *ret_cid_size, + Fido2EnrollFlags *ret_required) { + + _cleanup_free_ void *cid = NULL, *salt = NULL; + size_t cid_size = 0, salt_size = 0; + _cleanup_free_ char *rp = NULL; + int r; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + JsonVariant *w; + Fido2EnrollFlags required = 0; + + assert(json); + assert(ret_rp_id); + assert(ret_salt); + assert(ret_salt_size); + assert(ret_cid); + assert(ret_cid_size); + assert(ret_required); + + r = json_parse(json, 0, &v, NULL, NULL); + if (r < 0) + return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m"); + + w = json_variant_by_key(v, "fido2-credential"); + if (!w) + return -EINVAL; + + r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size); + if (r < 0) + return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m"); + + w = json_variant_by_key(v, "fido2-salt"); + if (!w) + return -EINVAL; + + r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size); + if (r < 0) + return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m"); + + w = json_variant_by_key(v, "fido2-rp"); + if (w) { + /* The "rp" field is optional. */ + rp = strdup(json_variant_string(w)); + if (!rp) { + crypt_log_error(cd, "Not enough memory."); + return -ENOMEM; + } + } + + w = json_variant_by_key(v, "fido2-clientPin-required"); + if (w) + /* The "fido2-clientPin-required" field is optional. */ + SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w)); + else + required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */ + + w = json_variant_by_key(v, "fido2-up-required"); + if (w) + /* The "fido2-up-required" field is optional. */ + SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w)); + else + required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */ + + w = json_variant_by_key(v, "fido2-uv-required"); + if (w) + /* The "fido2-uv-required" field is optional. */ + SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w)); + else + required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */ + + *ret_rp_id = TAKE_PTR(rp); + *ret_cid = TAKE_PTR(cid); + *ret_cid_size = cid_size; + *ret_salt = TAKE_PTR(salt); + *ret_salt_size = salt_size; + *ret_required = required; + + return 0; +} |