diff options
-rw-r--r-- | PROTOCOL.u2f | 21 | ||||
-rw-r--r-- | sk-usbhid.c | 1 | ||||
-rw-r--r-- | ssh-keygen.1 | 16 | ||||
-rw-r--r-- | ssh-keygen.c | 36 | ||||
-rw-r--r-- | ssh-sk.c | 10 |
5 files changed, 65 insertions, 19 deletions
diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f index 58f75ba28..748111d56 100644 --- a/PROTOCOL.u2f +++ b/PROTOCOL.u2f | |||
@@ -141,17 +141,20 @@ least manufacturer and batch number granularity. For this reason, we | |||
141 | choose not to include this information in the public key or save it by | 141 | choose not to include this information in the public key or save it by |
142 | default. | 142 | default. |
143 | 143 | ||
144 | Attestation information is very useful however in an organisational | 144 | Attestation information is useful for out-of-band key and certificate |
145 | context, where it may be used by a CA as part of certificate | 145 | registration worksflows, e.g. proving to a CA that a key is backed |
146 | issuance. In this case, exposure to the CA of hardware identity is | 146 | by trusted hardware before it will issue a certificate. To support this |
147 | desirable. To support this case, OpenSSH optionally allows retaining the | 147 | case, OpenSSH optionally allows retaining the attestation information |
148 | attestation information at the time of key generation. It will take the | 148 | at the time of key generation. It will take the following format: |
149 | following format: | 149 | |
150 | 150 | string "ssh-sk-attest-v00" | |
151 | string "sk-attest-v00" | ||
152 | uint32 version (1 for U2F, 2 for FIDO2 in future) | ||
153 | string attestation certificate | 151 | string attestation certificate |
154 | string enrollment signature | 152 | string enrollment signature |
153 | uint32 reserved flags | ||
154 | string reserved string | ||
155 | |||
156 | OpenSSH treats the attestation certificate and enrollment signatures as | ||
157 | opaque objects and does no interpretation of them itself. | ||
155 | 158 | ||
156 | SSH U2F signatures | 159 | SSH U2F signatures |
157 | ------------------ | 160 | ------------------ |
diff --git a/sk-usbhid.c b/sk-usbhid.c index 2148e1d79..ad83054ad 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c | |||
@@ -570,6 +570,7 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
570 | } | 570 | } |
571 | if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { | 571 | if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { |
572 | len = fido_cred_x5c_len(cred); | 572 | len = fido_cred_x5c_len(cred); |
573 | debug3("%s: attestation cert len=%zu", __func__, len); | ||
573 | if ((response->attestation_cert = calloc(1, len)) == NULL) { | 574 | if ((response->attestation_cert = calloc(1, len)) == NULL) { |
574 | skdebug(__func__, "calloc attestation cert failed"); | 575 | skdebug(__func__, "calloc attestation cert failed"); |
575 | goto out; | 576 | goto out; |
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index b4a873920..c6a976183 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .\" $OpenBSD: ssh-keygen.1,v 1.196 2020/01/23 23:31:52 djm Exp $ | 1 | .\" $OpenBSD: ssh-keygen.1,v 1.197 2020/01/28 08:01:34 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: January 23 2020 $ | 38 | .Dd $Mdocdate: January 28 2020 $ |
39 | .Dt SSH-KEYGEN 1 | 39 | .Dt SSH-KEYGEN 1 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -483,6 +483,14 @@ Note that | |||
483 | .Xr sshd 8 | 483 | .Xr sshd 8 |
484 | will refuse such signatures by default, unless overridden via | 484 | will refuse such signatures by default, unless overridden via |
485 | an authorized_keys option. | 485 | an authorized_keys option. |
486 | .It Cm challenge=path | ||
487 | Specifies a path to a challenge string that will be passed to the | ||
488 | FIDO token during key generation. | ||
489 | The challenge string is optional, but may be used as part of an out-of-band | ||
490 | protocol for key enrollment. | ||
491 | If no | ||
492 | .Cm challenge | ||
493 | is specified, a random challenge is used. | ||
486 | .It Cm resident | 494 | .It Cm resident |
487 | Indicate that the key should be stored on the FIDO authenticator itself. | 495 | Indicate that the key should be stored on the FIDO authenticator itself. |
488 | Resident keys may be supported on FIDO2 tokens and typically require that | 496 | Resident keys may be supported on FIDO2 tokens and typically require that |
@@ -494,6 +502,10 @@ A username to be associated with a resident key, | |||
494 | overriding the empty default username. | 502 | overriding the empty default username. |
495 | Specifying a username may be useful when generating multiple resident keys | 503 | Specifying a username may be useful when generating multiple resident keys |
496 | for the same application name. | 504 | for the same application name. |
505 | .It Cm write-attestation=path | ||
506 | May be used at key generation time to record the attestation certificate | ||
507 | returned from FIDO tokens during key generation. | ||
508 | By default this information is discarded. | ||
497 | .El | 509 | .El |
498 | .Pp | 510 | .Pp |
499 | The | 511 | The |
diff --git a/ssh-keygen.c b/ssh-keygen.c index 8df55f2c2..4ee43ab98 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.394 2020/01/25 23:13:09 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.395 2020/01/28 08:01:34 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 |
@@ -3114,6 +3114,8 @@ main(int argc, char **argv) | |||
3114 | unsigned long long cert_serial = 0; | 3114 | unsigned long long cert_serial = 0; |
3115 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; | 3115 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
3116 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; | 3116 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; |
3117 | char *sk_attestaion_path = NULL; | ||
3118 | struct sshbuf *challenge = NULL, *attest = NULL; | ||
3117 | size_t i, nopts = 0; | 3119 | size_t i, nopts = 0; |
3118 | u_int32_t bits = 0; | 3120 | u_int32_t bits = 0; |
3119 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; | 3121 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; |
@@ -3557,6 +3559,16 @@ main(int argc, char **argv) | |||
3557 | sk_device = xstrdup(opts[i] + 7); | 3559 | sk_device = xstrdup(opts[i] + 7); |
3558 | } else if (strncasecmp(opts[i], "user=", 5) == 0) { | 3560 | } else if (strncasecmp(opts[i], "user=", 5) == 0) { |
3559 | sk_user = xstrdup(opts[i] + 5); | 3561 | sk_user = xstrdup(opts[i] + 5); |
3562 | } else if (strncasecmp(opts[i], "challenge=", 10) == 0) { | ||
3563 | if ((r = sshbuf_load_file(opts[i] + 10, | ||
3564 | &challenge)) != 0) { | ||
3565 | fatal("Unable to load FIDO enrollment " | ||
3566 | "challenge \"%s\": %s", | ||
3567 | opts[i] + 10, ssh_err(r)); | ||
3568 | } | ||
3569 | } else if (strncasecmp(opts[i], | ||
3570 | "write-attestation=", 18) == 0) { | ||
3571 | sk_attestaion_path = opts[i] + 18; | ||
3560 | } else if (strncasecmp(opts[i], | 3572 | } else if (strncasecmp(opts[i], |
3561 | "application=", 12) == 0) { | 3573 | "application=", 12) == 0) { |
3562 | sk_application = xstrdup(opts[i] + 12); | 3574 | sk_application = xstrdup(opts[i] + 12); |
@@ -3570,12 +3582,14 @@ main(int argc, char **argv) | |||
3570 | "to authorize key generation.\n"); | 3582 | "to authorize key generation.\n"); |
3571 | } | 3583 | } |
3572 | passphrase = NULL; | 3584 | passphrase = NULL; |
3585 | if ((attest = sshbuf_new()) == NULL) | ||
3586 | fatal("sshbuf_new failed"); | ||
3573 | for (i = 0 ; i < 3; i++) { | 3587 | for (i = 0 ; i < 3; i++) { |
3574 | fflush(stdout); | 3588 | fflush(stdout); |
3575 | r = sshsk_enroll(type, sk_provider, sk_device, | 3589 | r = sshsk_enroll(type, sk_provider, sk_device, |
3576 | sk_application == NULL ? "ssh:" : sk_application, | 3590 | sk_application == NULL ? "ssh:" : sk_application, |
3577 | sk_user, sk_flags, passphrase, NULL, | 3591 | sk_user, sk_flags, passphrase, challenge, |
3578 | &private, NULL); | 3592 | &private, attest); |
3579 | if (r == 0) | 3593 | if (r == 0) |
3580 | break; | 3594 | break; |
3581 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | 3595 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) |
@@ -3668,6 +3682,22 @@ main(int argc, char **argv) | |||
3668 | free(fp); | 3682 | free(fp); |
3669 | } | 3683 | } |
3670 | 3684 | ||
3685 | if (sk_attestaion_path != NULL) { | ||
3686 | if (attest == NULL || sshbuf_len(attest) == 0) { | ||
3687 | fatal("Enrollment did not return attestation " | ||
3688 | "certificate"); | ||
3689 | } | ||
3690 | if ((r = sshbuf_write_file(sk_attestaion_path, attest)) != 0) { | ||
3691 | fatal("Unable to write attestation certificate " | ||
3692 | "\"%s\": %s", sk_attestaion_path, ssh_err(r)); | ||
3693 | } | ||
3694 | if (!quiet) { | ||
3695 | printf("Your FIDO attestation certificate has been " | ||
3696 | "saved in %s\n", sk_attestaion_path); | ||
3697 | } | ||
3698 | } | ||
3699 | sshbuf_free(attest); | ||
3671 | sshkey_free(public); | 3700 | sshkey_free(public); |
3701 | |||
3672 | exit(0); | 3702 | exit(0); |
3673 | } | 3703 | } |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.25 2020/01/25 23:13:09 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.26 2020/01/28 08:01:34 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -504,14 +504,14 @@ sshsk_enroll(int type, const char *provider_path, const char *device, | |||
504 | 504 | ||
505 | /* Optionally fill in the attestation information */ | 505 | /* Optionally fill in the attestation information */ |
506 | if (attest != NULL) { | 506 | if (attest != NULL) { |
507 | if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 || | 507 | if ((r = sshbuf_put_cstring(attest, |
508 | (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */ | 508 | "ssh-sk-attest-v00")) != 0 || |
509 | (r = sshbuf_put_string(attest, | 509 | (r = sshbuf_put_string(attest, |
510 | resp->attestation_cert, resp->attestation_cert_len)) != 0 || | 510 | resp->attestation_cert, resp->attestation_cert_len)) != 0 || |
511 | (r = sshbuf_put_string(attest, | 511 | (r = sshbuf_put_string(attest, |
512 | resp->signature, resp->signature_len)) != 0 || | 512 | resp->signature, resp->signature_len)) != 0 || |
513 | (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */ | 513 | (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ |
514 | (r = sshbuf_put_string(attest, NULL, 0)) != 0) { | 514 | (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { |
515 | error("%s: buffer error: %s", __func__, ssh_err(r)); | 515 | error("%s: buffer error: %s", __func__, ssh_err(r)); |
516 | goto out; | 516 | goto out; |
517 | } | 517 | } |