| /* 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; |
| ssize_t base64_encoded_size; |
| |
| 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 */ |
| base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); |
| if (base64_encoded_size < 0) |
| return crypt_log_error_errno(cd, (int) base64_encoded_size, "Failed to base64 encode key: %m"); |
| |
| *ret_keyslot_passphrase = TAKE_PTR(base64_encoded); |
| *ret_keyslot_passphrase_size = base64_encoded_size; |
| |
| 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; |
| } |