]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homed: move fido2 setup code to src/shared/
authorLennart Poettering <lennart@poettering.net>
Wed, 25 Nov 2020 15:34:54 +0000 (16:34 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 17 Dec 2020 19:00:03 +0000 (20:00 +0100)
That way we can reuse it from systemd-cryptenroll

src/home/homectl-fido2.c
src/shared/libfido2-util.c
src/shared/libfido2-util.h

index 99a6bd5d9df9daeefe02d7b2b2edf55524ee23d9..d5edec1bc4536744961fec4cf06c5df6ef292077 100644 (file)
@@ -110,109 +110,22 @@ static int add_fido2_salt(
 }
 #endif
 
-#define FIDO2_SALT_SIZE 32
-
 int identity_add_fido2_parameters(
                 JsonVariant **v,
                 const char *device) {
 
 #if HAVE_LIBFIDO2
-        _cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
-        _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
-        _cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
-        _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
-        _cleanup_(erase_and_freep) char *used_pin = NULL;
-        _cleanup_(erase_and_freep) void *salt = NULL;
         JsonVariant *un, *realm, *rn;
-        bool found_extension = false;
-        const void *cid, *secret;
+        _cleanup_(erase_and_freep) void *secret = NULL, *salt = NULL;
+        _cleanup_(erase_and_freep) char *used_pin = NULL;
+        size_t cid_size, salt_size, secret_size;
+        _cleanup_free_ void *cid = NULL;
         const char *fido_un;
-        size_t n, cid_size, secret_size;
-        char **e;
         int r;
 
-        /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
-         * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
-         * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
-         * device never sees the volume key.
-         *
-         * S = HMAC-SHA256(I, D)
-         *
-         * with: S → LUKS/account authentication key                                         (never stored)
-         *       I → internal key on FIDO2 device                              (stored in the FIDO2 device)
-         *       D → salt we generate here               (stored in the privileged part of the JSON record)
-         *
-         */
-
         assert(v);
         assert(device);
 
-        r = dlopen_libfido2();
-        if (r < 0)
-                return log_error_errno(r, "FIDO2 token support is not installed.");
-
-        salt = malloc(FIDO2_SALT_SIZE);
-        if (!salt)
-                return log_oom();
-
-        r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate salt: %m");
-
-        d = sym_fido_dev_new();
-        if (!d)
-                return log_oom();
-
-        r = sym_fido_dev_open(d, device);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
-
-        if (!sym_fido_dev_is_fido2(d))
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
-                                       "Specified device %s is not a FIDO2 device.", device);
-
-        di = sym_fido_cbor_info_new();
-        if (!di)
-                return log_oom();
-
-        r = sym_fido_dev_get_cbor_info(d, di);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to get CBOR device info for %s: %s", device, sym_fido_strerr(r));
-
-        e = sym_fido_cbor_info_extensions_ptr(di);
-        n = sym_fido_cbor_info_extensions_len(di);
-
-        for (size_t i = 0; i < n; i++)
-                if (streq(e[i], "hmac-secret")) {
-                        found_extension = true;
-                        break;
-                }
-
-        if (!found_extension)
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
-                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device);
-
-        c = sym_fido_cred_new();
-        if (!c)
-                return log_oom();
-
-        r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
-
-        r = sym_fido_cred_set_rp(c, "io.systemd.home", "Home Directory");
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r));
-
-        r = sym_fido_cred_set_type(c, COSE_ES256);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
-
         un = json_variant_by_key(*v, "userName");
         if (!un)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -236,164 +149,37 @@ int identity_add_fido2_parameters(
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "realName field of user record is not a string");
 
-        r = sym_fido_cred_set_user(c,
-                               (const unsigned char*) fido_un, strlen(fido_un), /* We pass the user ID and name as the same */
-                               fido_un,
-                               rn ? json_variant_string(rn) : NULL,
-                               NULL /* icon URL */);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r));
-
-        r = sym_fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
-
-        r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
-
-        r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
-
-        log_info("Initializing FIDO2 credential on security token.");
-
-        log_notice("%s%s(Hint: This might require verification of user presence on security token.)",
-                   emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
-                   emoji_enabled() ? " " : "");
-
-        r = sym_fido_dev_make_cred(d, c, NULL);
-        if (r == FIDO_ERR_PIN_REQUIRED) {
-                _cleanup_free_ char *text = NULL;
-
-                if (asprintf(&text, "Please enter security token PIN:") < 0)
-                        return log_oom();
-
-                for (;;) {
-                        _cleanup_(strv_free_erasep) char **pin = NULL;
-                        char **i;
-
-                        r = ask_password_auto(text, "user-home", NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to acquire user PIN: %m");
-
-                        r = FIDO_ERR_PIN_INVALID;
-                        STRV_FOREACH(i, pin) {
-                                if (isempty(*i)) {
-                                        log_info("PIN may not be empty.");
-                                        continue;
-                                }
-
-                                r = sym_fido_dev_make_cred(d, c, *i);
-                                if (r == FIDO_OK) {
-                                        used_pin = strdup(*i);
-                                        if (!used_pin)
-                                                return log_oom();
-                                        break;
-                                }
-                                if (r != FIDO_ERR_PIN_INVALID)
-                                        break;
-                        }
-
-                        if (r != FIDO_ERR_PIN_INVALID)
-                                break;
-
-                        log_notice("PIN incorrect, please try again.");
-                }
-        }
-        if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
-                return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
-                                        "Token PIN is currently blocked, please remove and reinsert token.");
-        if (r == FIDO_ERR_ACTION_TIMEOUT)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
-                                       "Token action timeout. (User didn't interact with token quickly enough.)");
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
-
-        cid = sym_fido_cred_id_ptr(c);
-        if (!cid)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
-
-        cid_size = sym_fido_cred_id_len(c);
-
-        a = sym_fido_assert_new();
-        if (!a)
-                return log_oom();
-
-        r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_rp(a, "io.systemd.home");
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_allow_cred(a, cid, cid_size);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
-
-        r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r));
-
-        log_info("Generating secret key on FIDO2 security token.");
-
-        r = sym_fido_dev_get_assert(d, a, used_pin);
-        if (r == FIDO_ERR_UP_REQUIRED) {
-                r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
-                if (r != FIDO_OK)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                               "Failed to turn on FIDO2 assertion user presence: %s", sym_fido_strerr(r));
-
-                log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
-                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
-                           emoji_enabled() ? " " : "");
-
-                r = sym_fido_dev_get_assert(d, a, used_pin);
-        }
-        if (r == FIDO_ERR_ACTION_TIMEOUT)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
-                                       "Token action timeout. (User didn't interact with token quickly enough.)");
-        if (r != FIDO_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to ask token for assertion: %s", sym_fido_strerr(r));
-
-        secret = sym_fido_assert_hmac_secret_ptr(a, 0);
-        if (!secret)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
-
-        secret_size = sym_fido_assert_hmac_secret_len(a, 0);
+        r = fido2_generate_hmac_hash(
+                        device,
+                        /* rp_id= */ "io.systemd.home",
+                        /* rp_name= */ "Home Directory",
+                        /* user_id= */ fido_un, strlen(fido_un), /* We pass the user ID and name as the same */
+                        /* user_name= */ fido_un,
+                        /* user_display_name= */ rn ? json_variant_string(rn) : NULL,
+                        /* user_icon_name= */ NULL,
+                        /* askpw_icon_name= */ "user-home",
+                        &cid, &cid_size,
+                        &salt, &salt_size,
+                        &secret, &secret_size,
+                        &used_pin);
+        if (r < 0)
+                return r;
 
-        r = add_fido2_credential_id(v, cid, cid_size);
+        r = add_fido2_credential_id(
+                        v,
+                        cid,
+                        cid_size);
         if (r < 0)
                 return r;
 
-        r = add_fido2_salt(v,
-                           cid,
-                           cid_size,
-                           salt,
-                           FIDO2_SALT_SIZE,
-                           secret,
-                           secret_size);
+        r = add_fido2_salt(
+                        v,
+                        cid,
+                        cid_size,
+                        salt,
+                        salt_size,
+                        secret,
+                        secret_size);
         if (r < 0)
                 return r;
 
index 8358aa2fe3749f592dda209709aac6f1c387410b..5438ffc3699802e0046f8850416552079d046987 100644 (file)
@@ -4,10 +4,14 @@
 
 #if HAVE_LIBFIDO2
 #include "alloc-util.h"
+#include "ask-password-api.h"
 #include "dlfcn-util.h"
 #include "format-table.h"
 #include "locale-util.h"
 #include "log.h"
+#include "memory-util.h"
+#include "random-util.h"
+#include "strv.h"
 
 static void *libfido2_dl = NULL;
 
@@ -116,6 +120,288 @@ int dlopen_libfido2(void) {
         libfido2_dl = TAKE_PTR(dl);
         return 1;
 }
+
+#define FIDO2_SALT_SIZE 32
+
+int fido2_generate_hmac_hash(
+                const char *device,
+                const char *rp_id,
+                const char *rp_name,
+                const void *user_id, size_t user_id_len,
+                const char *user_name,
+                const char *user_display_name,
+                const char *user_icon,
+                const char *askpw_icon_name,
+                void **ret_cid, size_t *ret_cid_size,
+                void **ret_salt, size_t *ret_salt_size,
+                void **ret_secret, size_t *ret_secret_size,
+                char **ret_usedpin) {
+
+        _cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
+        _cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
+        _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
+        _cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
+        _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
+        _cleanup_(erase_and_freep) char *used_pin = NULL;
+        _cleanup_free_ char *cid_copy = NULL;
+        size_t n, cid_size, secret_size;
+        bool found_extension = false;
+        const void *cid, *secret;
+        char **e;
+        int r;
+
+        assert(device);
+        assert(ret_cid);
+        assert(ret_cid_size);
+        assert(ret_salt);
+        assert(ret_salt_size);
+        assert(ret_secret);
+        assert(ret_secret_size);
+
+        /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
+         * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
+         * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
+         * device never sees the volume key.
+         *
+         * S = HMAC-SHA256(I, D)
+         *
+         * with: S → LUKS/account authentication key                                         (never stored)
+         *       I → internal key on FIDO2 device                              (stored in the FIDO2 device)
+         *       D → salt we generate here               (stored in the privileged part of the JSON record)
+         *
+         */
+
+        assert(device);
+
+        r = dlopen_libfido2();
+        if (r < 0)
+                return log_error_errno(r, "FIDO2 token support is not installed.");
+
+        salt = malloc(FIDO2_SALT_SIZE);
+        if (!salt)
+                return log_oom();
+
+        r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        d = sym_fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = sym_fido_dev_open(d, device);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
+
+        if (!sym_fido_dev_is_fido2(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is not a FIDO2 device.", device);
+
+        di = sym_fido_cbor_info_new();
+        if (!di)
+                return log_oom();
+
+        r = sym_fido_dev_get_cbor_info(d, di);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to get CBOR device info for %s: %s", device, sym_fido_strerr(r));
+
+        e = sym_fido_cbor_info_extensions_ptr(di);
+        n = sym_fido_cbor_info_extensions_len(di);
+
+        for (size_t i = 0; i < n; i++)
+                if (streq(e[i], "hmac-secret")) {
+                        found_extension = true;
+                        break;
+                }
+
+        if (!found_extension)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device);
+
+        c = sym_fido_cred_new();
+        if (!c)
+                return log_oom();
+
+        r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_rp(c, rp_id, rp_name);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_type(c, COSE_ES256);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_user(
+                        c,
+                        user_id, user_id_len,
+                        user_name,
+                        user_display_name,
+                        user_icon);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
+
+        r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
+
+        log_info("Initializing FIDO2 credential on security token.");
+
+        log_notice("%s%s(Hint: This might require verification of user presence on security token.)",
+                   emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                   emoji_enabled() ? " " : "");
+
+        r = sym_fido_dev_make_cred(d, c, NULL);
+        if (r == FIDO_ERR_PIN_REQUIRED) {
+                for (;;) {
+                        _cleanup_(strv_free_erasep) char **pin = NULL;
+                        char **i;
+
+                        r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to acquire user PIN: %m");
+
+                        r = FIDO_ERR_PIN_INVALID;
+                        STRV_FOREACH(i, pin) {
+                                if (isempty(*i)) {
+                                        log_info("PIN may not be empty.");
+                                        continue;
+                                }
+
+                                r = sym_fido_dev_make_cred(d, c, *i);
+                                if (r == FIDO_OK) {
+                                        used_pin = strdup(*i);
+                                        if (!used_pin)
+                                                return log_oom();
+                                        break;
+                                }
+                                if (r != FIDO_ERR_PIN_INVALID)
+                                        break;
+                        }
+
+                        if (r != FIDO_ERR_PIN_INVALID)
+                                break;
+
+                        log_notice("PIN incorrect, please try again.");
+                }
+        }
+        if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
+                return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
+                                        "Token PIN is currently blocked, please remove and reinsert token.");
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
+
+        cid = sym_fido_cred_id_ptr(c);
+        if (!cid)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
+
+        cid_size = sym_fido_cred_id_len(c);
+
+        a = sym_fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_set_rp(a, rp_id);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_allow_cred(a, cid, cid_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+
+        r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r));
+
+        log_info("Generating secret key on FIDO2 security token.");
+
+        r = sym_fido_dev_get_assert(d, a, used_pin);
+        if (r == FIDO_ERR_UP_REQUIRED) {
+                r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
+                if (r != FIDO_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to turn on FIDO2 assertion user presence: %s", sym_fido_strerr(r));
+
+                log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = sym_fido_dev_get_assert(d, a, used_pin);
+        }
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", sym_fido_strerr(r));
+
+        secret = sym_fido_assert_hmac_secret_ptr(a, 0);
+        if (!secret)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
+
+        secret_size = sym_fido_assert_hmac_secret_len(a, 0);
+
+        secret_copy = memdup(secret, secret_size);
+        if (!secret_copy)
+                return log_oom();
+
+        cid_copy = memdup(cid, cid_size);
+        if (!cid_copy)
+                return log_oom();
+
+        *ret_cid = TAKE_PTR(cid_copy);
+        *ret_cid_size = cid_size;
+        *ret_salt = TAKE_PTR(salt);
+        *ret_salt_size = FIDO2_SALT_SIZE;
+        *ret_secret = TAKE_PTR(secret_copy);
+        *ret_secret_size = secret_size;
+
+        if (ret_usedpin)
+                *ret_usedpin = TAKE_PTR(used_pin);
+
+        return 0;
+}
 #endif
 
 int fido2_list_devices(void) {
index 37b2f921ff2577f83887c08c7054faf61519af13..869168ee7b86ee783e49b67f8a6eece8b950b766 100644 (file)
@@ -69,6 +69,20 @@ static inline void fido_cred_free_wrapper(fido_cred_t **p) {
                 sym_fido_cred_free(p);
 }
 
+int fido2_generate_hmac_hash(
+                const char *device,
+                const char *rp_id,
+                const char *rp_name,
+                const void *user_id, size_t user_id_len,
+                const char *user_name,
+                const char *user_display_name,
+                const char *user_icon,
+                const char *askpw_icon_name,
+                void **ret_cid, size_t *ret_cid_size,
+                void **ret_salt, size_t *ret_salt_size,
+                void **ret_secret, size_t *ret_secret_size,
+                char **ret_usedpin);
+
 #endif
 
 int fido2_list_devices(void);