From 24bdc75813704aa54ed01bf11474b12c75a3a722 Mon Sep 17 00:00:00 2001 From: Per Nilsson Date: Wed, 1 Dec 2021 11:36:06 +0100 Subject: [PATCH] Verify PIN before every sign operation to accomodate always-auth keys --- common/util.c | 20 ++++----- tool/yubico-piv-tool.c | 97 +++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/common/util.c b/common/util.c index 6f9cf480..4519e4e5 100644 --- a/common/util.c +++ b/common/util.c @@ -406,33 +406,33 @@ bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigne bool read_pw(const char *name, char *pwbuf, size_t pwbuflen, int verify, int stdin_input) { #define READ_PW_PROMPT_BASE "Enter %s: " char prompt[sizeof(READ_PW_PROMPT_BASE) + 32] = {0}; - int ret; if (pwbuflen < 1) { fprintf(stderr, "Failed to read %s: buffer too small.", name); return false; } - if(stdin_input) { - fprintf(stdout, "%s\n", name); + int ret = snprintf(prompt, sizeof(prompt), READ_PW_PROMPT_BASE, name); + if (ret < 0 || ret >= sizeof(prompt)) { + fprintf(stderr, "Failed to read %s: snprintf failed.\n", name); + return false; + } + + if (stdin_input) { + fprintf(stdout, "%s\n", prompt); if(fgets(pwbuf, pwbuflen, stdin)) { if(pwbuf[strlen(pwbuf) - 1] == '\n') { pwbuf[strlen(pwbuf) - 1] = '\0'; } return true; } else { + fprintf(stderr, "Failed to read %s: fgets failed.\n", name); return false; } } - ret = snprintf(prompt, sizeof(prompt), READ_PW_PROMPT_BASE, name); - if (ret < 0 || ret >= sizeof(prompt)) { - fprintf(stderr, "Failed to read %s: snprintf failed.\n", name); - return false; - } - if (0 != EVP_read_pw_string(pwbuf, pwbuflen-1, prompt, verify)) { - fprintf(stderr, "Retrieving %s failed.\n", name); + fprintf(stderr, "Failed to read %s: EVP_read_pw_string failed.\n", name); return false; } return true; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 9fde8f51..64232634 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -69,6 +69,8 @@ #define YKPIV_ATTESTATION_OID "1.3.6.1.4.1.41482.3" +static bool verify_pin(ykpiv_state *state); + static enum file_mode key_file_mode(enum enum_key_format fmt, bool output) { if (fmt == key_format_arg_PEM) { if (output) { @@ -126,10 +128,16 @@ static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, u in = signinput; len = padlen; } - if(ykpiv_sign_data(state, in, len, out, out_len, algorithm, key) == YKPIV_OK) { - return true; + if(!verify_pin(state)) { + return false; } - return false; + ykpiv_rc res = ykpiv_sign_data(state, in, len, out, out_len, algorithm, key); + if(res != YKPIV_OK) + { + fprintf(stderr, "Signing data failed: '%s'\n", ykpiv_strerror(res)); + return false; + } + return true; } #if !((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)) @@ -870,7 +878,6 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for unsigned char signature[1024] = {0}; size_t sig_len = sizeof(signature); if(!sign_data(state, signinput, len, signature, &sig_len, algorithm, key)) { - fprintf(stderr, "Failed signing request.\n"); goto request_out; } ASN1_STRING_set(req->signature, signature, sig_len); @@ -1124,7 +1131,6 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo unsigned char signature[1024] = {0}; size_t sig_len = sizeof(signature); if(!sign_data(state, signinput, len, signature, &sig_len, algorithm, key)) { - fprintf(stderr, "Failed signing certificate.\n"); goto selfsign_out; } ASN1_STRING_set(x509->signature, signature, sig_len); @@ -1186,31 +1192,6 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo return ret; } -static bool verify_pin(ykpiv_state *state, const char *pin) { - int tries = -1; - ykpiv_rc res; - int len; - len = strlen(pin); - - if(len > 8) { - fprintf(stderr, "Maximum 8 digits of PIN supported.\n"); - } - - res = ykpiv_verify(state, pin, &tries); - if(res == YKPIV_OK) { - return true; - } else if(res == YKPIV_WRONG_PIN || res == YKPIV_PIN_LOCKED) { - if(tries > 0) { - fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", tries); - } else { - fprintf(stderr, "Pin code blocked, use unblock-pin action to unblock.\n"); - } - } else { - fprintf(stderr, "Pin code verification failed: '%s'\n", ykpiv_strerror(res)); - } - return false; -} - /* this function is called for all three of change-pin, change-puk and unblock pin * since they're very similar in what data they use. */ static bool change_pin(ykpiv_state *state, enum enum_action action, const char *pin, @@ -1416,7 +1397,6 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, unsigned char buf[1024] = {0}; size_t len = sizeof(buf); if(!sign_data(state, hashed, hash_len, buf, &len, algo, key)) { - fprintf(stderr, "failed signing file\n"); goto out; } @@ -1720,7 +1700,6 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, enc_len = data_len; } if(!sign_data(state, ptr, enc_len, signature, &sig_len, algorithm, key)) { - fprintf(stderr, "Failed signing test data.\n"); goto test_out; } @@ -2060,8 +2039,44 @@ static bool read_object(ykpiv_state *state, int id, const char *output_file_name return ret; } +static struct gengetopt_args_info args_info; + +static bool verify_pin(ykpiv_state *state) +{ + if (!args_info.pin_arg) { + args_info.pin_arg = calloc(1, 8 + 2); + if (!read_pw("PIN", args_info.pin_arg, 8 + 2, false, args_info.stdin_input_flag)) { + free(args_info.pin_arg); + args_info.pin_arg = NULL; + return false; + } + } + + if (strlen(args_info.pin_arg) > 8) { + fprintf(stderr, "Maximum 8 digits of PIN supported.\n"); + } + + int tries = -1; + ykpiv_rc res = ykpiv_verify(state, args_info.pin_arg, &tries); + if (res == YKPIV_OK) { + fprintf(stderr, "Successfully verified PIN.\n"); + return true; + } + else if (res == YKPIV_WRONG_PIN || res == YKPIV_PIN_LOCKED) { + if (tries > 0) { + fprintf(stderr, "Pin verification failed, %d tries left before pin is blocked.\n", tries); + } + else { + fprintf(stderr, "Pin code blocked, use unblock-pin action to unblock.\n"); + } + } + else { + fprintf(stderr, "Pin code verification failed: '%s'\n", ykpiv_strerror(res)); + } + return false; +} + int main(int argc, char *argv[]) { - struct gengetopt_args_info args_info; ykpiv_state *state; int verbosity; enum enum_action action; @@ -2352,21 +2367,7 @@ int main(int argc, char *argv[]) { } break; case action_arg_verifyMINUS_pin: { - char pinbuf[8+2] = {0}; - char *pin = args_info.pin_arg; - - if(!pin) { - if (!read_pw("PIN", pinbuf, sizeof(pinbuf), false, args_info.stdin_input_flag)) { - fprintf(stderr, "Failed to get PIN.\n"); - ykpiv_done(state); - cmdline_parser_free(&args_info); - return EXIT_FAILURE; - } - pin = pinbuf; - } - if(verify_pin(state, pin)) { - fprintf(stderr, "Successfully verified PIN.\n"); - } else { + if(!verify_pin(state)) { ret = EXIT_FAILURE; } break;