diff options
-rw-r--r-- | ssh-keygen.1 | 11 | ||||
-rw-r--r-- | ssh-keygen.c | 224 |
2 files changed, 172 insertions, 63 deletions
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index f0d70adec..569a46b19 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .\" $OpenBSD: ssh-keygen.1,v 1.186 2019/12/30 16:10:00 jmc Exp $ | 1 | .\" $OpenBSD: ssh-keygen.1,v 1.187 2020/01/02 22:40:09 djm Exp $ |
2 | .\" | 2 | .\" |
3 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -35,7 +35,7 @@ | |||
35 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 35 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
36 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 36 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
37 | .\" | 37 | .\" |
38 | .Dd $Mdocdate: December 30 2019 $ | 38 | .Dd $Mdocdate: January 2 2020 $ |
39 | .Dt SSH-KEYGEN 1 | 39 | .Dt SSH-KEYGEN 1 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -92,6 +92,9 @@ | |||
92 | .Fl H | 92 | .Fl H |
93 | .Op Fl f Ar known_hosts_file | 93 | .Op Fl f Ar known_hosts_file |
94 | .Nm ssh-keygen | 94 | .Nm ssh-keygen |
95 | .Fl K | ||
96 | .Op Fl w Ar provider | ||
97 | .Nm ssh-keygen | ||
95 | .Fl R Ar hostname | 98 | .Fl R Ar hostname |
96 | .Op Fl f Ar known_hosts_file | 99 | .Op Fl f Ar known_hosts_file |
97 | .Nm ssh-keygen | 100 | .Nm ssh-keygen |
@@ -363,6 +366,10 @@ commercial SSH implementations. | |||
363 | The default import format is | 366 | The default import format is |
364 | .Dq RFC4716 . | 367 | .Dq RFC4716 . |
365 | .It Fl k | 368 | .It Fl k |
369 | Download resident keys from a FIDO authenticator. | ||
370 | Public and private key files will be written to the current directory for | ||
371 | each downloaded key. | ||
372 | .It Fl k | ||
366 | Generate a KRL file. | 373 | Generate a KRL file. |
367 | In this mode, | 374 | In this mode, |
368 | .Nm | 375 | .Nm |
diff --git a/ssh-keygen.c b/ssh-keygen.c index 3640a3c37..7731339f7 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.380 2019/12/30 09:49:52 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.381 2020/01/02 22:40:09 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -2871,6 +2871,137 @@ do_moduli_screen(const char *out_file, char **opts, size_t nopts) | |||
2871 | #endif /* WITH_OPENSSL */ | 2871 | #endif /* WITH_OPENSSL */ |
2872 | } | 2872 | } |
2873 | 2873 | ||
2874 | static char * | ||
2875 | private_key_passphrase(void) | ||
2876 | { | ||
2877 | char *passphrase1, *passphrase2; | ||
2878 | |||
2879 | /* Ask for a passphrase (twice). */ | ||
2880 | if (identity_passphrase) | ||
2881 | passphrase1 = xstrdup(identity_passphrase); | ||
2882 | else if (identity_new_passphrase) | ||
2883 | passphrase1 = xstrdup(identity_new_passphrase); | ||
2884 | else { | ||
2885 | passphrase_again: | ||
2886 | passphrase1 = | ||
2887 | read_passphrase("Enter passphrase (empty for no " | ||
2888 | "passphrase): ", RP_ALLOW_STDIN); | ||
2889 | passphrase2 = read_passphrase("Enter same passphrase again: ", | ||
2890 | RP_ALLOW_STDIN); | ||
2891 | if (strcmp(passphrase1, passphrase2) != 0) { | ||
2892 | /* | ||
2893 | * The passphrases do not match. Clear them and | ||
2894 | * retry. | ||
2895 | */ | ||
2896 | freezero(passphrase1, strlen(passphrase1)); | ||
2897 | freezero(passphrase2, strlen(passphrase2)); | ||
2898 | printf("Passphrases do not match. Try again.\n"); | ||
2899 | goto passphrase_again; | ||
2900 | } | ||
2901 | /* Clear the other copy of the passphrase. */ | ||
2902 | freezero(passphrase2, strlen(passphrase2)); | ||
2903 | } | ||
2904 | return passphrase1; | ||
2905 | } | ||
2906 | |||
2907 | static const char * | ||
2908 | skip_ssh_url_preamble(const char *s) | ||
2909 | { | ||
2910 | if (strncmp(s, "ssh://", 6) == 0) | ||
2911 | return s + 6; | ||
2912 | else if (strncmp(s, "ssh:", 4) == 0) | ||
2913 | return s + 4; | ||
2914 | return s; | ||
2915 | } | ||
2916 | |||
2917 | static int | ||
2918 | do_download_sk(const char *skprovider) | ||
2919 | { | ||
2920 | struct sshkey **keys; | ||
2921 | size_t nkeys, i; | ||
2922 | int r, ok = -1; | ||
2923 | char *fp, *pin, *pass = NULL, *path, *pubpath; | ||
2924 | const char *ext; | ||
2925 | |||
2926 | if (skprovider == NULL) | ||
2927 | fatal("Cannot download keys without provider"); | ||
2928 | |||
2929 | pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); | ||
2930 | if ((r = sshsk_load_resident(skprovider, pin, &keys, &nkeys)) != 0) { | ||
2931 | freezero(pin, strlen(pin)); | ||
2932 | error("Unable to load resident keys: %s", ssh_err(r)); | ||
2933 | return -1; | ||
2934 | } | ||
2935 | if (nkeys == 0) | ||
2936 | logit("No keys to download"); | ||
2937 | freezero(pin, strlen(pin)); | ||
2938 | |||
2939 | for (i = 0; i < nkeys; i++) { | ||
2940 | if (keys[i]->type != KEY_ECDSA_SK && | ||
2941 | keys[i]->type != KEY_ED25519_SK) { | ||
2942 | error("Unsupported key type %s (%d)", | ||
2943 | sshkey_type(keys[i]), keys[i]->type); | ||
2944 | continue; | ||
2945 | } | ||
2946 | if ((fp = sshkey_fingerprint(keys[i], | ||
2947 | fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2948 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2949 | debug("%s: key %zu: %s %s %s (flags 0x%02x)", __func__, i, | ||
2950 | sshkey_type(keys[i]), fp, keys[i]->sk_application, | ||
2951 | keys[i]->sk_flags); | ||
2952 | ext = skip_ssh_url_preamble(keys[i]->sk_application); | ||
2953 | xasprintf(&path, "id_%s_rk%s%s", | ||
2954 | keys[i]->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk", | ||
2955 | *ext == '\0' ? "" : "_", ext); | ||
2956 | |||
2957 | /* If the file already exists, ask the user to confirm. */ | ||
2958 | if (!confirm_overwrite(path)) { | ||
2959 | free(path); | ||
2960 | break; | ||
2961 | } | ||
2962 | |||
2963 | /* Save the key with the application string as the comment */ | ||
2964 | if (pass == NULL) | ||
2965 | pass = private_key_passphrase(); | ||
2966 | if ((r = sshkey_save_private(keys[i], path, pass, | ||
2967 | keys[i]->sk_application, private_key_format, | ||
2968 | openssh_format_cipher, rounds)) != 0) { | ||
2969 | error("Saving key \"%s\" failed: %s", | ||
2970 | path, ssh_err(r)); | ||
2971 | free(path); | ||
2972 | break; | ||
2973 | } | ||
2974 | if (!quiet) { | ||
2975 | printf("Saved %s key%s%s to %s\n", | ||
2976 | sshkey_type(keys[i]), | ||
2977 | *ext != '\0' ? " " : "", | ||
2978 | *ext != '\0' ? keys[i]->sk_application : "", | ||
2979 | path); | ||
2980 | } | ||
2981 | |||
2982 | /* Save public key too */ | ||
2983 | xasprintf(&pubpath, "%s.pub", path); | ||
2984 | free(path); | ||
2985 | if ((r = sshkey_save_public(keys[i], pubpath, | ||
2986 | keys[i]->sk_application)) != 0) { | ||
2987 | free(pubpath); | ||
2988 | error("Saving public key \"%s\" failed: %s", | ||
2989 | pubpath, ssh_err(r)); | ||
2990 | break; | ||
2991 | } | ||
2992 | free(pubpath); | ||
2993 | } | ||
2994 | |||
2995 | if (i >= nkeys) | ||
2996 | ok = 0; /* success */ | ||
2997 | if (pass != NULL) | ||
2998 | freezero(pass, strlen(pass)); | ||
2999 | for (i = 0; i < nkeys; i++) | ||
3000 | sshkey_free(keys[i]); | ||
3001 | free(keys); | ||
3002 | return ok ? 0 : -1; | ||
3003 | } | ||
3004 | |||
2874 | static void | 3005 | static void |
2875 | usage(void) | 3006 | usage(void) |
2876 | { | 3007 | { |
@@ -2891,6 +3022,8 @@ usage(void) | |||
2891 | " ssh-keygen -D pkcs11\n"); | 3022 | " ssh-keygen -D pkcs11\n"); |
2892 | #endif | 3023 | #endif |
2893 | fprintf(stderr, | 3024 | fprintf(stderr, |
3025 | " ssh-keygen -K path [-w sk_provider]\n"); | ||
3026 | fprintf(stderr, | ||
2894 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" | 3027 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" |
2895 | " ssh-keygen -H [-f known_hosts_file]\n" | 3028 | " ssh-keygen -H [-f known_hosts_file]\n" |
2896 | " ssh-keygen -R hostname [-f known_hosts_file]\n" | 3029 | " ssh-keygen -R hostname [-f known_hosts_file]\n" |
@@ -2920,24 +3053,23 @@ usage(void) | |||
2920 | int | 3053 | int |
2921 | main(int argc, char **argv) | 3054 | main(int argc, char **argv) |
2922 | { | 3055 | { |
2923 | char dotsshdir[PATH_MAX], comment[1024], *passphrase1, *passphrase2; | 3056 | char dotsshdir[PATH_MAX], comment[1024], *passphrase; |
2924 | char *rr_hostname = NULL, *ep, *fp, *ra; | 3057 | char *rr_hostname = NULL, *ep, *fp, *ra; |
2925 | struct sshkey *private, *public; | 3058 | struct sshkey *private, *public; |
2926 | struct passwd *pw; | 3059 | struct passwd *pw; |
2927 | struct stat st; | 3060 | struct stat st; |
2928 | int r, opt, type, fd; | 3061 | int r, opt, type; |
2929 | int change_passphrase = 0, change_comment = 0, show_cert = 0; | 3062 | int change_passphrase = 0, change_comment = 0, show_cert = 0; |
2930 | int find_host = 0, delete_host = 0, hash_hosts = 0; | 3063 | int find_host = 0, delete_host = 0, hash_hosts = 0; |
2931 | int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; | 3064 | int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; |
2932 | int prefer_agent = 0, convert_to = 0, convert_from = 0; | 3065 | int prefer_agent = 0, convert_to = 0, convert_from = 0; |
2933 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; | 3066 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; |
2934 | int do_gen_candidates = 0, do_screen_candidates = 0; | 3067 | int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; |
2935 | unsigned long long cert_serial = 0; | 3068 | unsigned long long cert_serial = 0; |
2936 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; | 3069 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
2937 | size_t i, nopts = 0; | 3070 | size_t i, nopts = 0; |
2938 | u_int32_t bits = 0; | 3071 | u_int32_t bits = 0; |
2939 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; | 3072 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; |
2940 | FILE *f; | ||
2941 | const char *errstr; | 3073 | const char *errstr; |
2942 | int log_level = SYSLOG_LEVEL_INFO; | 3074 | int log_level = SYSLOG_LEVEL_INFO; |
2943 | char *sign_op = NULL; | 3075 | char *sign_op = NULL; |
@@ -2965,8 +3097,8 @@ main(int argc, char **argv) | |||
2965 | 3097 | ||
2966 | sk_provider = getenv("SSH_SK_PROVIDER"); | 3098 | sk_provider = getenv("SSH_SK_PROVIDER"); |
2967 | 3099 | ||
2968 | /* Remaining characters: dGjJKSTWx */ | 3100 | /* Remaining characters: dGjJSTWx */ |
2969 | while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvy" | 3101 | while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy" |
2970 | "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" | 3102 | "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" |
2971 | "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { | 3103 | "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { |
2972 | switch (opt) { | 3104 | switch (opt) { |
@@ -3046,6 +3178,9 @@ main(int argc, char **argv) | |||
3046 | case 'g': | 3178 | case 'g': |
3047 | print_generic = 1; | 3179 | print_generic = 1; |
3048 | break; | 3180 | break; |
3181 | case 'K': | ||
3182 | download_sk = 1; | ||
3183 | break; | ||
3049 | case 'P': | 3184 | case 'P': |
3050 | identity_passphrase = optarg; | 3185 | identity_passphrase = optarg; |
3051 | break; | 3186 | break; |
@@ -3261,6 +3396,8 @@ main(int argc, char **argv) | |||
3261 | } | 3396 | } |
3262 | if (pkcs11provider != NULL) | 3397 | if (pkcs11provider != NULL) |
3263 | do_download(pw); | 3398 | do_download(pw); |
3399 | if (download_sk) | ||
3400 | return do_download_sk(sk_provider); | ||
3264 | if (print_fingerprint || print_bubblebabble) | 3401 | if (print_fingerprint || print_bubblebabble) |
3265 | do_fingerprint(pw); | 3402 | do_fingerprint(pw); |
3266 | if (change_passphrase) | 3403 | if (change_passphrase) |
@@ -3356,7 +3493,7 @@ main(int argc, char **argv) | |||
3356 | printf("You may need to touch your security key " | 3493 | printf("You may need to touch your security key " |
3357 | "to authorize key generation.\n"); | 3494 | "to authorize key generation.\n"); |
3358 | } | 3495 | } |
3359 | passphrase1 = NULL; | 3496 | passphrase = NULL; |
3360 | for (i = 0 ; i < 3; i++) { | 3497 | for (i = 0 ; i < 3; i++) { |
3361 | if (!quiet) { | 3498 | if (!quiet) { |
3362 | printf("You may need to touch your security " | 3499 | printf("You may need to touch your security " |
@@ -3365,21 +3502,21 @@ main(int argc, char **argv) | |||
3365 | fflush(stdout); | 3502 | fflush(stdout); |
3366 | r = sshsk_enroll(type, sk_provider, | 3503 | r = sshsk_enroll(type, sk_provider, |
3367 | cert_key_id == NULL ? "ssh:" : cert_key_id, | 3504 | cert_key_id == NULL ? "ssh:" : cert_key_id, |
3368 | sk_flags, passphrase1, NULL, &private, NULL); | 3505 | sk_flags, passphrase, NULL, &private, NULL); |
3369 | if (r == 0) | 3506 | if (r == 0) |
3370 | break; | 3507 | break; |
3371 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | 3508 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) |
3372 | exit(1); /* error message already printed */ | 3509 | exit(1); /* error message already printed */ |
3373 | if (passphrase1 != NULL) | 3510 | if (passphrase != NULL) |
3374 | freezero(passphrase1, strlen(passphrase1)); | 3511 | freezero(passphrase, strlen(passphrase)); |
3375 | passphrase1 = read_passphrase("Enter PIN for security " | 3512 | passphrase = read_passphrase("Enter PIN for security " |
3376 | "key: ", RP_ALLOW_STDIN); | 3513 | "key: ", RP_ALLOW_STDIN); |
3377 | } | 3514 | } |
3378 | if (passphrase1 != NULL) | 3515 | if (passphrase != NULL) |
3379 | freezero(passphrase1, strlen(passphrase1)); | 3516 | freezero(passphrase, strlen(passphrase)); |
3380 | if (i > 3) | 3517 | if (i > 3) |
3381 | fatal("Too many incorrect PINs"); | 3518 | fatal("Too many incorrect PINs"); |
3382 | break; | 3519 | break; |
3383 | default: | 3520 | default: |
3384 | if ((r = sshkey_generate(type, bits, &private)) != 0) | 3521 | if ((r = sshkey_generate(type, bits, &private)) != 0) |
3385 | fatal("sshkey_generate failed"); | 3522 | fatal("sshkey_generate failed"); |
@@ -3409,35 +3546,9 @@ main(int argc, char **argv) | |||
3409 | /* If the file already exists, ask the user to confirm. */ | 3546 | /* If the file already exists, ask the user to confirm. */ |
3410 | if (!confirm_overwrite(identity_file)) | 3547 | if (!confirm_overwrite(identity_file)) |
3411 | exit(1); | 3548 | exit(1); |
3412 | /* Ask for a passphrase (twice). */ | ||
3413 | if (identity_passphrase) | ||
3414 | passphrase1 = xstrdup(identity_passphrase); | ||
3415 | else if (identity_new_passphrase) | ||
3416 | passphrase1 = xstrdup(identity_new_passphrase); | ||
3417 | else { | ||
3418 | passphrase_again: | ||
3419 | passphrase1 = | ||
3420 | read_passphrase("Enter passphrase (empty for no " | ||
3421 | "passphrase): ", RP_ALLOW_STDIN); | ||
3422 | passphrase2 = read_passphrase("Enter same passphrase again: ", | ||
3423 | RP_ALLOW_STDIN); | ||
3424 | if (strcmp(passphrase1, passphrase2) != 0) { | ||
3425 | /* | ||
3426 | * The passphrases do not match. Clear them and | ||
3427 | * retry. | ||
3428 | */ | ||
3429 | explicit_bzero(passphrase1, strlen(passphrase1)); | ||
3430 | explicit_bzero(passphrase2, strlen(passphrase2)); | ||
3431 | free(passphrase1); | ||
3432 | free(passphrase2); | ||
3433 | printf("Passphrases do not match. Try again.\n"); | ||
3434 | goto passphrase_again; | ||
3435 | } | ||
3436 | /* Clear the other copy of the passphrase. */ | ||
3437 | explicit_bzero(passphrase2, strlen(passphrase2)); | ||
3438 | free(passphrase2); | ||
3439 | } | ||
3440 | 3549 | ||
3550 | /* Determine the passphrase for the private key */ | ||
3551 | passphrase = private_key_passphrase(); | ||
3441 | if (identity_comment) { | 3552 | if (identity_comment) { |
3442 | strlcpy(comment, identity_comment, sizeof(comment)); | 3553 | strlcpy(comment, identity_comment, sizeof(comment)); |
3443 | } else { | 3554 | } else { |
@@ -3446,35 +3557,26 @@ passphrase_again: | |||
3446 | } | 3557 | } |
3447 | 3558 | ||
3448 | /* Save the key with the given passphrase and comment. */ | 3559 | /* Save the key with the given passphrase and comment. */ |
3449 | if ((r = sshkey_save_private(private, identity_file, passphrase1, | 3560 | if ((r = sshkey_save_private(private, identity_file, passphrase, |
3450 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { | 3561 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { |
3451 | error("Saving key \"%s\" failed: %s", | 3562 | error("Saving key \"%s\" failed: %s", |
3452 | identity_file, ssh_err(r)); | 3563 | identity_file, ssh_err(r)); |
3453 | explicit_bzero(passphrase1, strlen(passphrase1)); | 3564 | freezero(passphrase, strlen(passphrase)); |
3454 | free(passphrase1); | ||
3455 | exit(1); | 3565 | exit(1); |
3456 | } | 3566 | } |
3457 | /* Clear the passphrase. */ | 3567 | freezero(passphrase, strlen(passphrase)); |
3458 | explicit_bzero(passphrase1, strlen(passphrase1)); | ||
3459 | free(passphrase1); | ||
3460 | |||
3461 | /* Clear the private key and the random number generator. */ | ||
3462 | sshkey_free(private); | 3568 | sshkey_free(private); |
3463 | 3569 | ||
3464 | if (!quiet) | 3570 | if (!quiet) { |
3465 | printf("Your identification has been saved in %s.\n", identity_file); | 3571 | printf("Your identification has been saved in %s.\n", |
3572 | identity_file); | ||
3573 | } | ||
3466 | 3574 | ||
3467 | strlcat(identity_file, ".pub", sizeof(identity_file)); | 3575 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
3468 | if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) | 3576 | if ((r = sshkey_save_public(public, identity_file, comment)) != 0) { |
3469 | fatal("Unable to save public key to %s: %s", | 3577 | fatal("Unable to save public key to %s: %s", |
3470 | identity_file, strerror(errno)); | 3578 | identity_file, strerror(errno)); |
3471 | if ((f = fdopen(fd, "w")) == NULL) | 3579 | } |
3472 | fatal("fdopen %s failed: %s", identity_file, strerror(errno)); | ||
3473 | if ((r = sshkey_write(public, f)) != 0) | ||
3474 | error("write key failed: %s", ssh_err(r)); | ||
3475 | fprintf(f, " %s\n", comment); | ||
3476 | if (ferror(f) || fclose(f) != 0) | ||
3477 | fatal("write public failed: %s", strerror(errno)); | ||
3478 | 3580 | ||
3479 | if (!quiet) { | 3581 | if (!quiet) { |
3480 | fp = sshkey_fingerprint(public, fingerprint_hash, | 3582 | fp = sshkey_fingerprint(public, fingerprint_hash, |