]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptenroll,homectl: Introduce --fido2-credential-algorithm option
authorMkfsSion <mkfssion@mkfssion.com>
Sun, 17 Apr 2022 07:42:49 +0000 (15:42 +0800)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Apr 2022 18:22:40 +0000 (20:22 +0200)
* Some authenticators(like Yubikey) support credential algorithm other than ES256
* Introduce a new option so users can make use of it

man/homectl.xml
man/systemd-cryptenroll.xml
src/cryptenroll/cryptenroll-fido2.c
src/cryptenroll/cryptenroll-fido2.h
src/cryptenroll/cryptenroll.c
src/home/homectl-fido2.c
src/home/homectl-fido2.h
src/home/homectl.c
src/shared/libfido2-util.c
src/shared/libfido2-util.h

index eaed7897b1c4563d3504d1695f301f64c580d70a..dacbd17b1e0b6df2a43fadc65eb1f1b682810e12 100644 (file)
         generally do not required that, and work out of the box.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+        <listitem><para>Specify COSE algorithm used in credential generation. The default value is
+        <literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
+        and <literal>eddsa</literal>.</para>
+
+        <para><literal>es256</literal> denotes ECDSA over NIST P-256 with SHA-256. <literal>rs256</literal>
+        denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. <literal>eddsa</literal> denotes
+        EDDSA over Curve25519 with SHA-512.</para>
+
+        <para>Note that your authenticator may not support some algorithms.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
 
index 6616d8bdb9f2476cd037d3531adf6b3401294dae..a18b070a3262416f502905d6c9c4a1a2d08dedab 100644 (file)
         <filename>/etc/crypttab</filename> line.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+        <listitem><para>Specify COSE algorithm used in credential generation. The default value is
+        <literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
+        and <literal>eddsa</literal>.</para>
+
+        <para><literal>es256</literal> denotes ECDSA over NIST P-256 with SHA-256. <literal>rs256</literal>
+        denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. <literal>eddsa</literal> denotes
+        EDDSA over Curve25519 with SHA-512.</para>
+
+        <para>Note that your authenticator may not support some algorithms.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
 
index b519b8651b57678ce6939d5f44e17877fac9ee2c..80adaefa17485d305e518d09e9a2ed5e55af7eb0 100644 (file)
@@ -12,7 +12,8 @@ int enroll_fido2(
                 const void *volume_key,
                 size_t volume_key_size,
                 const char *device,
-                Fido2EnrollFlags lock_with) {
+                Fido2EnrollFlags lock_with,
+                int cred_alg) {
 
         _cleanup_(erase_and_freep) void *salt = NULL, *secret = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
@@ -42,6 +43,7 @@ int enroll_fido2(
                         /* user_icon_name= */ NULL,
                         /* askpw_icon_name= */ "drive-harddisk",
                         lock_with,
+                        cred_alg,
                         &cid, &cid_size,
                         &salt, &salt_size,
                         &secret, &secret_size,
index b82a9ca842cc8800d1f2273adf2e5c4898e39888..11667afe9ca3d37243c6e5311903c964126e92d2 100644 (file)
@@ -8,9 +8,9 @@
 #include "log.h"
 
 #if HAVE_LIBFIDO2
-int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with);
+int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg);
 #else
-static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with) {
+static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg) {
         return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                "FIDO2 key enrollment not supported.");
 }
index 2e11ffe291e0b90c9ee55137ece2485bf29bf269..045adf871a1666d5ce76f1b4855bcd75ee81e80d 100644 (file)
@@ -39,6 +39,11 @@ static size_t arg_n_wipe_slots = 0;
 static WipeScope arg_wipe_slots_scope = WIPE_EXPLICIT;
 static unsigned arg_wipe_slots_mask = 0; /* Bitmask of (1U << EnrollType), for wiping all slots of specific types */
 static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
+#if HAVE_LIBFIDO2
+static int arg_fido2_cred_alg = COSE_ES256;
+#else
+static int arg_fido2_cred_alg = 0;
+#endif
 
 assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
 
@@ -89,6 +94,8 @@ static int help(void) {
                "     --recovery-key    Enroll a recovery key\n"
                "     --pkcs11-token-uri=URI\n"
                "                       Specify PKCS#11 security token URI\n"
+               "     --fido2-credential-algorithm=STRING\n"
+               "                       Specify COSE algorithm for FIDO2 credential\n"
                "     --fido2-device=PATH\n"
                "                       Enroll a FIDO2-HMAC security token\n"
                "     --fido2-with-client-pin=BOOL\n"
@@ -129,6 +136,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FIDO2_WITH_PIN,
                 ARG_FIDO2_WITH_UP,
                 ARG_FIDO2_WITH_UV,
+                ARG_FIDO2_CRED_ALG,
         };
 
         static const struct option options[] = {
@@ -137,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "password",                     no_argument,       NULL, ARG_PASSWORD         },
                 { "recovery-key",                 no_argument,       NULL, ARG_RECOVERY_KEY     },
                 { "pkcs11-token-uri",             required_argument, NULL, ARG_PKCS11_TOKEN_URI },
+                { "fido2-credential-algorithm",   required_argument, NULL, ARG_FIDO2_CRED_ALG   },
                 { "fido2-device",                 required_argument, NULL, ARG_FIDO2_DEVICE     },
                 { "fido2-with-client-pin",        required_argument, NULL, ARG_FIDO2_WITH_PIN   },
                 { "fido2-with-user-presence",     required_argument, NULL, ARG_FIDO2_WITH_UP    },
@@ -240,6 +249,12 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FIDO2_CRED_ALG:
+                        r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
+                        break;
+
                 case ARG_FIDO2_DEVICE: {
                         _cleanup_free_ char *device = NULL;
 
@@ -566,7 +581,7 @@ static int run(int argc, char *argv[]) {
                 break;
 
         case ENROLL_FIDO2:
-                slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with);
+                slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with, arg_fido2_cred_alg);
                 break;
 
         case ENROLL_TPM2:
index d0457d8e29f4b41651543b6f6649a190f88edb4d..61f0d081a3732ebc20c3bbd9b5c416419b68d6e8 100644 (file)
@@ -118,7 +118,8 @@ static int add_fido2_salt(
 int identity_add_fido2_parameters(
                 JsonVariant **v,
                 const char *device,
-                Fido2EnrollFlags lock_with) {
+                Fido2EnrollFlags lock_with,
+                int cred_alg) {
 
 #if HAVE_LIBFIDO2
         JsonVariant *un, *realm, *rn;
@@ -165,6 +166,7 @@ int identity_add_fido2_parameters(
                         /* user_icon_name= */ NULL,
                         /* askpw_icon_name= */ "user-home",
                         lock_with,
+                        cred_alg,
                         &cid, &cid_size,
                         &salt, &salt_size,
                         &secret, &secret_size,
index 5087069c3c153580abd590ec45e76754bf75f199..558c6747d9e521f209d0ce5c0a5228080e32c505 100644 (file)
@@ -4,4 +4,4 @@
 #include "json.h"
 #include "libfido2-util.h"
 
-int identity_add_fido2_parameters(JsonVariant **v, const char *device, Fido2EnrollFlags lock_with);
+int identity_add_fido2_parameters(JsonVariant **v, const char *device, Fido2EnrollFlags lock_with, int cred_alg);
index f0d1dac6ab0eda260886b929e526c481cc4524e1..56f6096769e18088208fe6c1a93b533b355a630d 100644 (file)
@@ -61,6 +61,11 @@ static uint64_t arg_disk_size_relative = UINT64_MAX;
 static char **arg_pkcs11_token_uri = NULL;
 static char **arg_fido2_device = NULL;
 static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
+#if HAVE_LIBFIDO2
+static int arg_fido2_cred_alg = COSE_ES256;
+#else
+static int arg_fido2_cred_alg = 0;
+#endif
 static bool arg_recovery_key = false;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static bool arg_and_resize = false;
@@ -1114,7 +1119,7 @@ static int acquire_new_home_record(UserRecord **ret) {
         }
 
         STRV_FOREACH(i, arg_fido2_device) {
-                r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with);
+                r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
                 if (r < 0)
                         return r;
         }
@@ -1473,7 +1478,7 @@ static int acquire_updated_home_record(
         }
 
         STRV_FOREACH(i, arg_fido2_device) {
-                r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with);
+                r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
                 if (r < 0)
                         return r;
         }
@@ -2387,6 +2392,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_LUKS_EXTRA_MOUNT_OPTIONS,
                 ARG_AUTO_RESIZE_MODE,
                 ARG_REBALANCE_WEIGHT,
+                ARG_FIDO2_CRED_ALG,
         };
 
         static const struct option options[] = {
@@ -2463,6 +2469,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "json",                        required_argument, NULL, ARG_JSON                        },
                 { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
                 { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
+                { "fido2-credential-algorithm",  required_argument, NULL, ARG_FIDO2_CRED_ALG              },
                 { "fido2-device",                required_argument, NULL, ARG_FIDO2_DEVICE                },
                 { "fido2-with-client-pin",       required_argument, NULL, ARG_FIDO2_WITH_PIN              },
                 { "fido2-with-user-presence",    required_argument, NULL, ARG_FIDO2_WITH_UP               },
@@ -3485,6 +3492,12 @@ static int parse_argv(int argc, char *argv[]) {
                         strv_uniq(arg_pkcs11_token_uri);
                         break;
 
+                case ARG_FIDO2_CRED_ALG:
+                        r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
+                        break;
+
                 case ARG_FIDO2_DEVICE:
                         if (streq(optarg, "list"))
                                 return fido2_list_devices();
index 8c9fa88e321a6de867fefa168a9621d7392e9344..e70a8c863af4ca8c147cf0b4a780b9d048950b23 100644 (file)
@@ -450,6 +450,20 @@ static int fido2_use_hmac_hash_specific_token(
         return 0;
 }
 
+/* COSE_ECDH_ES256 is not usable with fido_cred_set_type() thus it's not listed here. */
+static const char *fido2_algorithm_to_string(int alg) {
+        switch(alg) {
+                case COSE_ES256:
+                        return "es256";
+                case COSE_RS256:
+                        return "rs256";
+                case COSE_EDDSA:
+                        return "eddsa";
+                default:
+                        return NULL;
+        }
+}
+
 int fido2_use_hmac_hash(
                 const char *device,
                 const char *rp_id,
@@ -532,6 +546,7 @@ int fido2_generate_hmac_hash(
                 const char *user_icon,
                 const char *askpw_icon_name,
                 Fido2EnrollFlags lock_with,
+                int cred_alg,
                 void **ret_cid, size_t *ret_cid_size,
                 void **ret_salt, size_t *ret_salt_size,
                 void **ret_secret, size_t *ret_secret_size,
@@ -628,10 +643,10 @@ int fido2_generate_hmac_hash(
                 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);
+        r = sym_fido_cred_set_type(c, cred_alg);
         if (r != FIDO_OK)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
+                                       "Failed to set FIDO2 credential type to %s: %s", fido2_algorithm_to_string(cred_alg), sym_fido_strerr(r));
 
         r = sym_fido_cred_set_user(
                         c,
@@ -721,6 +736,9 @@ int fido2_generate_hmac_hash(
         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_ERR_UNSUPPORTED_ALGORITHM)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                        "Token doesn't support credential algorithm %s.", fido2_algorithm_to_string(cred_alg));
         if (r != FIDO_OK)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
@@ -1124,3 +1142,24 @@ finish:
                                "FIDO2 tokens not supported on this build.");
 #endif
 }
+
+#if HAVE_LIBFIDO2
+int parse_fido2_algorithm(const char *s, int *ret) {
+        int a;
+
+        assert(s);
+
+        if (streq(s, "es256"))
+                a = COSE_ES256;
+        else if (streq(s, "rs256"))
+                a = COSE_RS256;
+        else if (streq(s, "eddsa"))
+                a = COSE_EDDSA;
+        else
+                return -EINVAL;
+
+        if (ret)
+                *ret = a;
+        return 0;
+}
+#endif
index c9cd505f34a2cc4655c9d421e950ebf86eb5210f..a04a3768a5b054a94ee98c8bf0068d6dd594cc09 100644 (file)
@@ -109,12 +109,18 @@ int fido2_generate_hmac_hash(
                 const char *user_icon,
                 const char *askpw_icon_name,
                 Fido2EnrollFlags lock_with,
+                int cred_alg,
                 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,
                 Fido2EnrollFlags *ret_locked_with);
 
+int parse_fido2_algorithm(const char *s, int *ret);
+#else
+static inline int parse_fido2_algorithm(const char *s, int *ret) {
+        return -EOPNOTSUPP;
+}
 #endif
 
 int fido2_list_devices(void);