diff options
-rw-r--r-- | PROTOCOL.u2f | 98 | ||||
-rw-r--r-- | sk-api.h | 6 | ||||
-rw-r--r-- | sk-usbhid.c | 13 | ||||
-rw-r--r-- | ssh-keygen.1 | 9 | ||||
-rw-r--r-- | ssh-keygen.c | 44 | ||||
-rw-r--r-- | ssh-sk.c | 44 |
6 files changed, 96 insertions, 118 deletions
diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f index 5b8a06277..f8ca56b11 100644 --- a/PROTOCOL.u2f +++ b/PROTOCOL.u2f | |||
@@ -154,6 +154,16 @@ by trusted hardware before it will issue a certificate. To support this | |||
154 | case, OpenSSH optionally allows retaining the attestation information | 154 | case, OpenSSH optionally allows retaining the attestation information |
155 | at the time of key generation. It will take the following format: | 155 | at the time of key generation. It will take the following format: |
156 | 156 | ||
157 | string "ssh-sk-attest-v01" | ||
158 | string attestation certificate | ||
159 | string enrollment signature | ||
160 | string authenticator data (CBOR encoded) | ||
161 | uint32 reserved flags | ||
162 | string reserved string | ||
163 | |||
164 | A previous version of this format, emitted prior to OpenSSH 8.4 omitted | ||
165 | the authenticator data. | ||
166 | |||
157 | string "ssh-sk-attest-v00" | 167 | string "ssh-sk-attest-v00" |
158 | string attestation certificate | 168 | string attestation certificate |
159 | string enrollment signature | 169 | string enrollment signature |
@@ -267,87 +277,15 @@ regress testing. For this reason, OpenSSH shall support a dynamically- | |||
267 | loaded middleware libraries to communicate with security keys, but offer | 277 | loaded middleware libraries to communicate with security keys, but offer |
268 | support for the common case of USB HID security keys internally. | 278 | support for the common case of USB HID security keys internally. |
269 | 279 | ||
270 | The middleware library need only expose a handful of functions: | 280 | The middleware library need only expose a handful of functions and |
271 | 281 | numbers listed in sk-api.h. Included in the defined numbers is a | |
272 | #define SSH_SK_VERSION_MAJOR 0x00050000 /* API version */ | 282 | SSH_SK_VERSION_MAJOR that should be incremented for each incompatible |
273 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | ||
274 | |||
275 | /* Flags */ | ||
276 | #define SSH_SK_USER_PRESENCE_REQD 0x01 | ||
277 | #define SSH_SK_USER_VERIFICATION_REQD 0x04 | ||
278 | #define SSH_SK_RESIDENT_KEY 0x20 | ||
279 | |||
280 | /* Algs */ | ||
281 | #define SSH_SK_ECDSA 0x00 | ||
282 | #define SSH_SK_ED25519 0x01 | ||
283 | |||
284 | /* Error codes */ | ||
285 | #define SSH_SK_ERR_GENERAL -1 | ||
286 | #define SSH_SK_ERR_UNSUPPORTED -2 | ||
287 | #define SSH_SK_ERR_PIN_REQUIRED -3 | ||
288 | #define SSH_SK_ERR_DEVICE_NOT_FOUND -4 | ||
289 | |||
290 | struct sk_enroll_response { | ||
291 | uint8_t *public_key; | ||
292 | size_t public_key_len; | ||
293 | uint8_t *key_handle; | ||
294 | size_t key_handle_len; | ||
295 | uint8_t *signature; | ||
296 | size_t signature_len; | ||
297 | uint8_t *attestation_cert; | ||
298 | size_t attestation_cert_len; | ||
299 | }; | ||
300 | |||
301 | struct sk_sign_response { | ||
302 | uint8_t flags; | ||
303 | uint32_t counter; | ||
304 | uint8_t *sig_r; | ||
305 | size_t sig_r_len; | ||
306 | uint8_t *sig_s; | ||
307 | size_t sig_s_len; | ||
308 | }; | ||
309 | |||
310 | struct sk_resident_key { | ||
311 | uint32_t alg; | ||
312 | size_t slot; | ||
313 | char *application; | ||
314 | struct sk_enroll_response key; | ||
315 | }; | ||
316 | |||
317 | struct sk_option { | ||
318 | char *name; | ||
319 | char *value; | ||
320 | uint8_t important; | ||
321 | }; | ||
322 | |||
323 | /* Return the version of the middleware API */ | ||
324 | uint32_t sk_api_version(void); | ||
325 | |||
326 | /* Enroll a U2F key (private key generation) */ | ||
327 | int sk_enroll(uint32_t alg, | ||
328 | const uint8_t *challenge, size_t challenge_len, | ||
329 | const char *application, uint8_t flags, const char *pin, | ||
330 | struct sk_option **options, | ||
331 | struct sk_enroll_response **enroll_response); | ||
332 | |||
333 | /* Sign a challenge */ | ||
334 | int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, | ||
335 | const char *application, | ||
336 | const uint8_t *key_handle, size_t key_handle_len, | ||
337 | uint8_t flags, const char *pin, struct sk_option **options, | ||
338 | struct sk_sign_response **sign_response); | ||
339 | |||
340 | /* Enumerate all resident keys */ | ||
341 | int sk_load_resident_keys(const char *pin, struct sk_option **options, | ||
342 | struct sk_resident_key ***rks, size_t *nrks); | ||
343 | |||
344 | The SSH_SK_VERSION_MAJOR should be incremented for each incompatible | ||
345 | API change. | 283 | API change. |
346 | 284 | ||
347 | The options may be used to pass miscellaneous options to the middleware | 285 | miscellaneous options may be passed to the middleware as a NULL- |
348 | as a NULL-terminated array of pointers to struct sk_option. The middleware | 286 | terminated array of pointers to struct sk_option. The middleware may |
349 | may ignore unsupported or unknown options unless the "important" flag is | 287 | ignore unsupported or unknown options unless the "required" flag is set, |
350 | set, in which case it should return failure if an unsupported option is | 288 | in which case it should return failure if an unsupported option is |
351 | requested. | 289 | requested. |
352 | 290 | ||
353 | At present the following options names are supported: | 291 | At present the following options names are supported: |
@@ -368,4 +306,4 @@ In OpenSSH, the middleware will be invoked by using a similar mechanism to | |||
368 | ssh-pkcs11-helper to provide address-space containment of the | 306 | ssh-pkcs11-helper to provide address-space containment of the |
369 | middleware from ssh-agent. | 307 | middleware from ssh-agent. |
370 | 308 | ||
371 | $OpenBSD: PROTOCOL.u2f,v 1.25 2020/08/31 00:17:41 djm Exp $ | 309 | $OpenBSD: PROTOCOL.u2f,v 1.26 2020/09/09 03:08:01 djm Exp $ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sk-api.h,v 1.10 2020/08/27 01:08:19 djm Exp $ */ | 1 | /* $OpenBSD: sk-api.h,v 1.11 2020/09/09 03:08:01 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -47,6 +47,8 @@ struct sk_enroll_response { | |||
47 | size_t signature_len; | 47 | size_t signature_len; |
48 | uint8_t *attestation_cert; | 48 | uint8_t *attestation_cert; |
49 | size_t attestation_cert_len; | 49 | size_t attestation_cert_len; |
50 | uint8_t *authdata; | ||
51 | size_t authdata_len; | ||
50 | }; | 52 | }; |
51 | 53 | ||
52 | struct sk_sign_response { | 54 | struct sk_sign_response { |
@@ -72,7 +74,7 @@ struct sk_option { | |||
72 | uint8_t required; | 74 | uint8_t required; |
73 | }; | 75 | }; |
74 | 76 | ||
75 | #define SSH_SK_VERSION_MAJOR 0x00060000 /* current API version */ | 77 | #define SSH_SK_VERSION_MAJOR 0x00070000 /* current API version */ |
76 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | 78 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 |
77 | 79 | ||
78 | /* Return the version of the middleware API */ | 80 | /* Return the version of the middleware API */ |
diff --git a/sk-usbhid.c b/sk-usbhid.c index de85b2cb3..007c59644 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sk-usbhid.c,v 1.25 2020/08/31 00:17:41 djm Exp $ */ | 1 | /* $OpenBSD: sk-usbhid.c,v 1.26 2020/09/09 03:08:01 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Markus Friedl | 3 | * Copyright (c) 2019 Markus Friedl |
4 | * Copyright (c) 2020 Pedro Martelletto | 4 | * Copyright (c) 2020 Pedro Martelletto |
@@ -822,6 +822,16 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
822 | memcpy(response->attestation_cert, ptr, len); | 822 | memcpy(response->attestation_cert, ptr, len); |
823 | response->attestation_cert_len = len; | 823 | response->attestation_cert_len = len; |
824 | } | 824 | } |
825 | if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) { | ||
826 | len = fido_cred_authdata_len(cred); | ||
827 | debug3("%s: authdata len=%zu", __func__, len); | ||
828 | if ((response->authdata = calloc(1, len)) == NULL) { | ||
829 | skdebug(__func__, "calloc authdata failed"); | ||
830 | goto out; | ||
831 | } | ||
832 | memcpy(response->authdata, ptr, len); | ||
833 | response->authdata_len = len; | ||
834 | } | ||
825 | *enroll_response = response; | 835 | *enroll_response = response; |
826 | response = NULL; | 836 | response = NULL; |
827 | ret = 0; | 837 | ret = 0; |
@@ -832,6 +842,7 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
832 | free(response->key_handle); | 842 | free(response->key_handle); |
833 | free(response->signature); | 843 | free(response->signature); |
834 | free(response->attestation_cert); | 844 | free(response->attestation_cert); |
845 | free(response->authdata); | ||
835 | free(response); | 846 | free(response); |
836 | } | 847 | } |
837 | sk_close(sk); | 848 | sk_close(sk); |
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index c51a0d9c8..3ae596caa 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .\" $OpenBSD: ssh-keygen.1,v 1.208 2020/08/27 06:15:22 jmc Exp $ | 1 | .\" $OpenBSD: ssh-keygen.1,v 1.209 2020/09/09 03:08:01 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: August 27 2020 $ | 38 | .Dd $Mdocdate: September 9 2020 $ |
39 | .Dt SSH-KEYGEN 1 | 39 | .Dt SSH-KEYGEN 1 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -520,9 +520,10 @@ Not all FIDO tokens support this option. | |||
520 | Currently PIN authentication is the only supported verification method, | 520 | Currently PIN authentication is the only supported verification method, |
521 | but other methods may be supported in the future. | 521 | but other methods may be supported in the future. |
522 | .It Cm write-attestation Ns = Ns Ar path | 522 | .It Cm write-attestation Ns = Ns Ar path |
523 | May be used at key generation time to record the attestation certificate | 523 | May be used at key generation time to record the attestation data |
524 | returned from FIDO tokens during key generation. | 524 | returned from FIDO tokens during key generation. |
525 | By default this information is discarded. | 525 | Please note that this information is potentially sensitive. |
526 | By default, this information is discarded. | ||
526 | .El | 527 | .El |
527 | .Pp | 528 | .Pp |
528 | The | 529 | The |
diff --git a/ssh-keygen.c b/ssh-keygen.c index 64cee4de0..a12b79a56 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.419 2020/08/27 09:46:04 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.420 2020/09/09 03:08:01 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 |
@@ -3072,6 +3072,27 @@ do_download_sk(const char *skprovider, const char *device) | |||
3072 | } | 3072 | } |
3073 | 3073 | ||
3074 | static void | 3074 | static void |
3075 | save_attestation(struct sshbuf *attest, const char *path) | ||
3076 | { | ||
3077 | mode_t omask; | ||
3078 | int r; | ||
3079 | |||
3080 | if (path == NULL) | ||
3081 | return; /* nothing to do */ | ||
3082 | if (attest == NULL || sshbuf_len(attest) == 0) | ||
3083 | fatal("Enrollment did not return attestation data"); | ||
3084 | omask = umask(077); | ||
3085 | r = sshbuf_write_file(path, attest); | ||
3086 | umask(omask); | ||
3087 | if (r != 0) | ||
3088 | fatal("Unable to write attestation data \"%s\": %s", path, | ||
3089 | ssh_err(r)); | ||
3090 | if (!quiet) | ||
3091 | printf("Your FIDO attestation certificate has been saved in " | ||
3092 | "%s\n", path); | ||
3093 | } | ||
3094 | |||
3095 | static void | ||
3075 | usage(void) | 3096 | usage(void) |
3076 | { | 3097 | { |
3077 | fprintf(stderr, | 3098 | fprintf(stderr, |
@@ -3137,7 +3158,7 @@ main(int argc, char **argv) | |||
3137 | unsigned long long cert_serial = 0; | 3158 | unsigned long long cert_serial = 0; |
3138 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; | 3159 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
3139 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; | 3160 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; |
3140 | char *sk_attestaion_path = NULL; | 3161 | char *sk_attestation_path = NULL; |
3141 | struct sshbuf *challenge = NULL, *attest = NULL; | 3162 | struct sshbuf *challenge = NULL, *attest = NULL; |
3142 | size_t i, nopts = 0; | 3163 | size_t i, nopts = 0; |
3143 | u_int32_t bits = 0; | 3164 | u_int32_t bits = 0; |
@@ -3593,7 +3614,7 @@ main(int argc, char **argv) | |||
3593 | } | 3614 | } |
3594 | } else if (strncasecmp(opts[i], | 3615 | } else if (strncasecmp(opts[i], |
3595 | "write-attestation=", 18) == 0) { | 3616 | "write-attestation=", 18) == 0) { |
3596 | sk_attestaion_path = opts[i] + 18; | 3617 | sk_attestation_path = opts[i] + 18; |
3597 | } else if (strncasecmp(opts[i], | 3618 | } else if (strncasecmp(opts[i], |
3598 | "application=", 12) == 0) { | 3619 | "application=", 12) == 0) { |
3599 | sk_application = xstrdup(opts[i] + 12); | 3620 | sk_application = xstrdup(opts[i] + 12); |
@@ -3715,20 +3736,9 @@ main(int argc, char **argv) | |||
3715 | free(fp); | 3736 | free(fp); |
3716 | } | 3737 | } |
3717 | 3738 | ||
3718 | if (sk_attestaion_path != NULL) { | 3739 | if (sk_attestation_path != NULL) |
3719 | if (attest == NULL || sshbuf_len(attest) == 0) { | 3740 | save_attestation(attest, sk_attestation_path); |
3720 | fatal("Enrollment did not return attestation " | 3741 | |
3721 | "certificate"); | ||
3722 | } | ||
3723 | if ((r = sshbuf_write_file(sk_attestaion_path, attest)) != 0) { | ||
3724 | fatal("Unable to write attestation certificate " | ||
3725 | "\"%s\": %s", sk_attestaion_path, ssh_err(r)); | ||
3726 | } | ||
3727 | if (!quiet) { | ||
3728 | printf("Your FIDO attestation certificate has been " | ||
3729 | "saved in %s\n", sk_attestaion_path); | ||
3730 | } | ||
3731 | } | ||
3732 | sshbuf_free(attest); | 3742 | sshbuf_free(attest); |
3733 | sshkey_free(public); | 3743 | sshkey_free(public); |
3734 | 3744 | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.31 2020/08/27 01:08:19 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.32 2020/09/09 03:08:02 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -174,6 +174,7 @@ sshsk_free_enroll_response(struct sk_enroll_response *r) | |||
174 | freezero(r->public_key, r->public_key_len); | 174 | freezero(r->public_key, r->public_key_len); |
175 | freezero(r->signature, r->signature_len); | 175 | freezero(r->signature, r->signature_len); |
176 | freezero(r->attestation_cert, r->attestation_cert_len); | 176 | freezero(r->attestation_cert, r->attestation_cert_len); |
177 | freezero(r->authdata, r->authdata_len); | ||
177 | freezero(r, sizeof(*r)); | 178 | freezero(r, sizeof(*r)); |
178 | } | 179 | } |
179 | 180 | ||
@@ -419,6 +420,31 @@ make_options(const char *device, const char *user_id, | |||
419 | return ret; | 420 | return ret; |
420 | } | 421 | } |
421 | 422 | ||
423 | |||
424 | static int | ||
425 | fill_attestation_blob(const struct sk_enroll_response *resp, | ||
426 | struct sshbuf *attest) | ||
427 | { | ||
428 | int r; | ||
429 | |||
430 | if (attest == NULL) | ||
431 | return 0; /* nothing to do */ | ||
432 | if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || | ||
433 | (r = sshbuf_put_string(attest, | ||
434 | resp->attestation_cert, resp->attestation_cert_len)) != 0 || | ||
435 | (r = sshbuf_put_string(attest, | ||
436 | resp->signature, resp->signature_len)) != 0 || | ||
437 | (r = sshbuf_put_string(attest, | ||
438 | resp->authdata, resp->authdata_len)) != 0 || | ||
439 | (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ | ||
440 | (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { | ||
441 | error("%s: buffer error: %s", __func__, ssh_err(r)); | ||
442 | return r; | ||
443 | } | ||
444 | /* success */ | ||
445 | return 0; | ||
446 | } | ||
447 | |||
422 | int | 448 | int |
423 | sshsk_enroll(int type, const char *provider_path, const char *device, | 449 | sshsk_enroll(int type, const char *provider_path, const char *device, |
424 | const char *application, const char *userid, uint8_t flags, | 450 | const char *application, const char *userid, uint8_t flags, |
@@ -506,19 +532,9 @@ sshsk_enroll(int type, const char *provider_path, const char *device, | |||
506 | goto out; | 532 | goto out; |
507 | 533 | ||
508 | /* Optionally fill in the attestation information */ | 534 | /* Optionally fill in the attestation information */ |
509 | if (attest != NULL) { | 535 | if ((r = fill_attestation_blob(resp, attest)) != 0) |
510 | if ((r = sshbuf_put_cstring(attest, | 536 | goto out; |
511 | "ssh-sk-attest-v00")) != 0 || | 537 | |
512 | (r = sshbuf_put_string(attest, | ||
513 | resp->attestation_cert, resp->attestation_cert_len)) != 0 || | ||
514 | (r = sshbuf_put_string(attest, | ||
515 | resp->signature, resp->signature_len)) != 0 || | ||
516 | (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ | ||
517 | (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { | ||
518 | error("%s: buffer error: %s", __func__, ssh_err(r)); | ||
519 | goto out; | ||
520 | } | ||
521 | } | ||
522 | /* success */ | 538 | /* success */ |
523 | *keyp = key; | 539 | *keyp = key; |
524 | key = NULL; /* transferred */ | 540 | key = NULL; /* transferred */ |