From 70e723c000e46e2304e54f8063572d7fa0cdad46 Mon Sep 17 00:00:00 2001 From: MkfsSion Date: Sun, 17 Apr 2022 15:42:49 +0800 Subject: [PATCH] cryptenroll,homectl: Introduce --fido2-credential-algorithm option * Some authenticators(like Yubikey) support credential algorithm other than ES256 * Introduce a new option so users can make use of it --- man/homectl.xml | 13 +++++++++ man/systemd-cryptenroll.xml | 13 +++++++++ src/cryptenroll/cryptenroll-fido2.c | 4 ++- src/cryptenroll/cryptenroll-fido2.h | 4 +-- src/cryptenroll/cryptenroll.c | 17 +++++++++++- src/home/homectl-fido2.c | 4 ++- src/home/homectl-fido2.h | 2 +- src/home/homectl.c | 17 ++++++++++-- src/shared/libfido2-util.c | 43 +++++++++++++++++++++++++++-- src/shared/libfido2-util.h | 6 ++++ 10 files changed, 113 insertions(+), 10 deletions(-) diff --git a/man/homectl.xml b/man/homectl.xml index eaed7897b1c..dacbd17b1e0 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -356,6 +356,19 @@ generally do not required that, and work out of the box. + + STRING + Specify COSE algorithm used in credential generation. The default value is + es256. Supported values are es256, rs256 + and eddsa. + + es256 denotes ECDSA over NIST P-256 with SHA-256. rs256 + denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. eddsa denotes + EDDSA over Curve25519 with SHA-512. + + Note that your authenticator may not support some algorithms. + + PATH diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 6616d8bdb9f..a18b070a326 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -122,6 +122,19 @@ /etc/crypttab line. + + STRING + Specify COSE algorithm used in credential generation. The default value is + es256. Supported values are es256, rs256 + and eddsa. + + es256 denotes ECDSA over NIST P-256 with SHA-256. rs256 + denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. eddsa denotes + EDDSA over Curve25519 with SHA-512. + + Note that your authenticator may not support some algorithms. + + PATH diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c index b519b8651b5..80adaefa174 100644 --- a/src/cryptenroll/cryptenroll-fido2.c +++ b/src/cryptenroll/cryptenroll-fido2.c @@ -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, diff --git a/src/cryptenroll/cryptenroll-fido2.h b/src/cryptenroll/cryptenroll-fido2.h index b82a9ca842c..11667afe9ca 100644 --- a/src/cryptenroll/cryptenroll-fido2.h +++ b/src/cryptenroll/cryptenroll-fido2.h @@ -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."); } diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 2e11ffe291e..045adf871a1 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -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: diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c index d0457d8e29f..61f0d081a37 100644 --- a/src/home/homectl-fido2.c +++ b/src/home/homectl-fido2.c @@ -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, diff --git a/src/home/homectl-fido2.h b/src/home/homectl-fido2.h index 5087069c3c1..558c6747d9e 100644 --- a/src/home/homectl-fido2.h +++ b/src/home/homectl-fido2.h @@ -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); diff --git a/src/home/homectl.c b/src/home/homectl.c index f0d1dac6ab0..56f6096769e 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -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(); diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 8c9fa88e321..e70a8c863af 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -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 diff --git a/src/shared/libfido2-util.h b/src/shared/libfido2-util.h index c9cd505f34a..a04a3768a5b 100644 --- a/src/shared/libfido2-util.h +++ b/src/shared/libfido2-util.h @@ -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);