summaryrefslogtreecommitdiffstats
path: root/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptsetup/cryptsetup-tokens/luks2-fido2.c')
-rw-r--r--src/cryptsetup/cryptsetup-tokens/luks2-fido2.c157
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;
+}