summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-09-09 03:08:01 +0000
committerDamien Miller <djm@mindrot.org>2020-09-09 13:11:34 +1000
commitc76773524179cb654ff838dd43ba1ddb155bafaa (patch)
tree0e3079b760a58a670a5a5bbdca0e8eb184e34173
parentc1c44eeecddf093a7983bd91e70b446de789b363 (diff)
upstream: when writing an attestation blob for a FIDO key, record all
the data needed to verify the attestation. Previously we were missing the "authenticator data" that is included in the signature. spotted by Ian Haken feedback Pedro Martelletto and Ian Haken; ok markus@ OpenBSD-Commit-ID: 8439896e63792b2db99c6065dd9a45eabbdb7e0a
-rw-r--r--PROTOCOL.u2f98
-rw-r--r--sk-api.h6
-rw-r--r--sk-usbhid.c13
-rw-r--r--ssh-keygen.19
-rw-r--r--ssh-keygen.c44
-rw-r--r--ssh-sk.c44
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
154case, OpenSSH optionally allows retaining the attestation information 154case, OpenSSH optionally allows retaining the attestation information
155at the time of key generation. It will take the following format: 155at 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
164A previous version of this format, emitted prior to OpenSSH 8.4 omitted
165the 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-
267loaded middleware libraries to communicate with security keys, but offer 277loaded middleware libraries to communicate with security keys, but offer
268support for the common case of USB HID security keys internally. 278support for the common case of USB HID security keys internally.
269 279
270The middleware library need only expose a handful of functions: 280The middleware library need only expose a handful of functions and
271 281numbers listed in sk-api.h. Included in the defined numbers is a
272 #define SSH_SK_VERSION_MAJOR 0x00050000 /* API version */ 282SSH_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
344The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
345API change. 283API change.
346 284
347The options may be used to pass miscellaneous options to the middleware 285miscellaneous options may be passed to the middleware as a NULL-
348as a NULL-terminated array of pointers to struct sk_option. The middleware 286terminated array of pointers to struct sk_option. The middleware may
349may ignore unsupported or unknown options unless the "important" flag is 287ignore unsupported or unknown options unless the "required" flag is set,
350set, in which case it should return failure if an unsupported option is 288in which case it should return failure if an unsupported option is
351requested. 289requested.
352 290
353At present the following options names are supported: 291At present the following options names are supported:
@@ -368,4 +306,4 @@ In OpenSSH, the middleware will be invoked by using a similar mechanism to
368ssh-pkcs11-helper to provide address-space containment of the 306ssh-pkcs11-helper to provide address-space containment of the
369middleware from ssh-agent. 307middleware 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 $
diff --git a/sk-api.h b/sk-api.h
index cc32cd4cc..df17ca540 100644
--- a/sk-api.h
+++ b/sk-api.h
@@ -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
52struct sk_sign_response { 54struct 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.
520Currently PIN authentication is the only supported verification method, 520Currently PIN authentication is the only supported verification method,
521but other methods may be supported in the future. 521but 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
523May be used at key generation time to record the attestation certificate 523May be used at key generation time to record the attestation data
524returned from FIDO tokens during key generation. 524returned from FIDO tokens during key generation.
525By default this information is discarded. 525Please note that this information is potentially sensitive.
526By default, this information is discarded.
526.El 527.El
527.Pp 528.Pp
528The 529The
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
3074static void 3074static void
3075save_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
3095static void
3075usage(void) 3096usage(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
diff --git a/ssh-sk.c b/ssh-sk.c
index 89478aff0..1455df635 100644
--- a/ssh-sk.c
+++ b/ssh-sk.c
@@ -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
424static int
425fill_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
422int 448int
423sshsk_enroll(int type, const char *provider_path, const char *device, 449sshsk_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 */