From 0dc8d139222e34f6202a6f21361037a8aa61606b Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 18 Nov 2015 13:20:41 +0100 Subject: [PATCH 1/7] add f9 slot for attestation --- lib/ykpiv.h | 2 ++ tool/cmdline.ggo | 2 +- tool/util.c | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index ffbee83..c0aa0ac 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -139,6 +139,8 @@ extern "C" #define YKPIV_OBJ_RETIRED19 0x5fc11f #define YKPIV_OBJ_RETIRED20 0x5fc120 +#define YKPIV_OBJ_ATTESTATION 0x5fff01 + #define YKPIV_INS_VERIFY 0x20 #define YKPIV_INS_CHANGE_REFERENCE 0x24 #define YKPIV_INS_RESET_RETRY 0x2c diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 4d9ab43..85ddfdb 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -36,7 +36,7 @@ option "action" a "Action to take" values="version","generate","set-mgm-key", text " Multiple actions may be given at once and will be executed in order for example --action=verify-pin --action=request-certificate\n" -option "slot" s "What key slot to operate on" values="9a","9c","9d","9e","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95" enum optional +option "slot" s "What key slot to operate on" values="9a","9c","9d","9e","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","f9" enum optional text " 9a is for PIV Authentication 9c is for Digital Signature (PIN always checked) diff --git a/tool/util.c b/tool/util.c index 17c4b12..4e2a719 100644 --- a/tool/util.c +++ b/tool/util.c @@ -262,6 +262,9 @@ int get_object_id(enum enum_slot slot) { case slot_arg_95: object = YKPIV_OBJ_RETIRED20; break; + case slot_arg_f9: + object = YKPIV_OBJ_ATTESTATION; + break; case slot__NULL: default: object = 0; From 32e66f4fc698f9b1d360b8c2bb19c456dfb2ba03 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 18 Nov 2015 13:42:11 +0100 Subject: [PATCH 2/7] add attest action --- lib/ykpiv.h | 1 + tool/cmdline.ggo | 2 +- tool/yubico-piv-tool.c | 73 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index c0aa0ac..530c59c 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -155,6 +155,7 @@ extern "C" #define YKPIV_INS_GET_VERSION 0xfd #define YKPIV_INS_RESET 0xfb #define YKPIV_INS_SET_PIN_RETRIES 0xfa +#define YKPIV_INS_ATTEST 0xf9 #define YKPIV_PINPOLICY_TAG 0xaa #define YKPIV_PINPOLICY_NEVER 1 diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 85ddfdb..498861d 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -32,7 +32,7 @@ option "action" a "Action to take" values="version","generate","set-mgm-key", "reset","pin-retries","import-key","import-certificate","set-chuid", "request-certificate","verify-pin","change-pin","change-puk","unblock-pin", "selfsign-certificate","delete-certificate","read-certificate","status", - "test-signature","test-decipher","list-readers" enum multiple + "test-signature","test-decipher","list-readers","attest" enum multiple text " Multiple actions may be given at once and will be executed in order for example --action=verify-pin --action=request-certificate\n" diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 01a6690..02191c8 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1599,6 +1599,71 @@ static bool list_readers(ykpiv_state *state) { return true; } +static bool attest(ykpiv_state *state, const char *slot, + enum enum_key_format key_format, const char *output_file_name) { + FILE *output_file; + unsigned char data[2048]; + unsigned long len = sizeof(data); + bool ret = false; + X509 *x509 = NULL; + unsigned char templ[] = {0, YKPIV_INS_ATTEST, 0, 0}; + int key; + int sw; + + sscanf(slot, "%2x", &key); + templ[2] = key; + + if(key_format != key_format_arg_PEM && key_format != key_format_arg_DER) { + fprintf(stderr, "Only PEM and DER format are supported for attest..\n"); + return false; + } + + output_file = open_file(output_file_name, OUTPUT); + if(!output_file) { + return false; + } + + if(ykpiv_transfer_data(state, templ, NULL, 0, data, &len, &sw) != YKPIV_OK) { + fprintf(stderr, "Failed to communicate.\n"); + goto attest_out; + } else if(sw != 0x9000) { + fprintf(stderr, "Failed to attest key.\n"); + goto attest_out; + } + + if(data[0] == 0x30) { + if(key_format == key_format_arg_PEM) { + const unsigned char *ptr = data; + int len2 = len; + x509 = X509_new(); + if(!x509) { + fprintf(stderr, "Failed allocating x509 structure.\n"); + goto attest_out; + } + x509 = d2i_X509(NULL, &ptr, len2); + if(!x509) { + fprintf(stderr, "Failed parsing x509 information.\n"); + goto attest_out; + } + PEM_write_X509(output_file, x509); + ret = true; + } else { + fwrite(data, len, 1, output_file); + } + ret = true; + } + +attest_out: + if(output_file != stdout) { + fclose(output_file); + } + if(x509) { + X509_free(x509); + } + + return ret; +} + int main(int argc, char *argv[]) { struct gengetopt_args_info args_info; ykpiv_state *state; @@ -1630,6 +1695,7 @@ int main(int argc, char *argv[]) { case action_arg_readMINUS_certificate: case action_arg_testMINUS_signature: case action_arg_testMINUS_decipher: + case action_arg_attest: if(args_info.slot_arg == slot__NULL) { fprintf(stderr, "The '%s' action needs a slot (-s) to operate on.\n", cmdline_parser_action_values[action]); @@ -1698,6 +1764,7 @@ int main(int argc, char *argv[]) { case action_arg_testMINUS_signature: case action_arg_testMINUS_decipher: case action_arg_listMINUS_readers: + case action_arg_attest: case action__NULL: default: if(verbosity) { @@ -1897,6 +1964,12 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } break; + case action_arg_attest: + if(attest(state, args_info.slot_orig, args_info.key_format_arg, + args_info.output_arg) == false) { + ret = EXIT_FAILURE; + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action); From 4c74ebdc5655e9c0c4ddc9352cb3503da8fc815c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 17 Mar 2016 10:21:18 +0100 Subject: [PATCH 3/7] actually open output_file in attest() --- tool/yubico-piv-tool.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index f3b6465..6b43ebe 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1648,7 +1648,6 @@ static bool list_readers(ykpiv_state *state) { static bool attest(ykpiv_state *state, const char *slot, enum enum_key_format key_format, const char *output_file_name) { - FILE *output_file; unsigned char data[2048]; unsigned long len = sizeof(data); bool ret = false; @@ -1656,6 +1655,10 @@ static bool attest(ykpiv_state *state, const char *slot, unsigned char templ[] = {0, YKPIV_INS_ATTEST, 0, 0}; int key; int sw; + FILE *output_file = open_file(output_file_name, OUTPUT); + if(!output_file) { + return false; + } sscanf(slot, "%2x", &key); templ[2] = key; From 7aa6ac93e6f4d4835971bc17e4afc747e526c4e5 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 17 Mar 2016 10:52:03 +0100 Subject: [PATCH 4/7] add touch-policy cached --- lib/ykpiv.h | 1 + tool/cmdline.ggo | 2 +- tool/util.c | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 3358e49..9670dee 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -205,6 +205,7 @@ extern "C" #define YKPIV_TOUCHPOLICY_DEFAULT 0 #define YKPIV_TOUCHPOLICY_NEVER 1 #define YKPIV_TOUCHPOLICY_ALWAYS 2 +#define YKPIV_TOUCHPOLICY_CACHED 3 #define YKPIV_IS_EC(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) #define YKPIV_IS_RSA(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index fb503b2..4d8cd36 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -62,7 +62,7 @@ option "valid-days" - "Time (in days) until the self-signed certificate expires" option "pin" P "Pin/puk code for verification" string optional option "new-pin" N "New pin/puk code for changing" string optional dependon="pin" option "pin-policy" - "Set pin policy for action generate or import-key" values="never","once","always" enum optional -option "touch-policy" - "Set touch policy for action generate, import-key or set-mgm-key" values="never","always" enum optional +option "touch-policy" - "Set touch policy for action generate, import-key or set-mgm-key" values="never","always","cached" enum optional option "id" - "Id of object for write/read object" int optional option "format" f "Format of data for write/read object" values="hex","base64","binary" enum optional default="hex" option "sign" - "Sign data" flag off hidden diff --git a/tool/util.c b/tool/util.c index 17f4af9..48434e3 100644 --- a/tool/util.c +++ b/tool/util.c @@ -604,6 +604,8 @@ unsigned char get_touch_policy(enum enum_touch_policy policy) { return YKPIV_TOUCHPOLICY_NEVER; case touch_policy_arg_always: return YKPIV_TOUCHPOLICY_ALWAYS; + case touch_policy_arg_cached: + return YKPIV_TOUCHPOLICY_CACHED; case touch_policy__NULL: default: return 0; From 189fe723f1d8b5c991152c758bbb1679024e9bcc Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 17 Mar 2016 12:29:13 +0100 Subject: [PATCH 5/7] add YKPIV_KEY_ATTESTATION to ykpiv_import_key() --- lib/ykpiv.c | 2 +- lib/ykpiv.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 1a76965..ab58f94 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -873,7 +873,7 @@ ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, u if (key == YKPIV_KEY_CARDMGM || key < YKPIV_KEY_RETIRED1 || (key > YKPIV_KEY_RETIRED20 && key < YKPIV_KEY_AUTHENTICATION) || - key > YKPIV_KEY_CARDAUTH) { + (key > YKPIV_KEY_CARDAUTH && key != YKPIV_KEY_ATTESTATION)) { return YKPIV_KEY_ERROR; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 9670dee..f567d0e 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -141,6 +141,7 @@ extern "C" #define YKPIV_KEY_RETIRED18 0x93 #define YKPIV_KEY_RETIRED19 0x94 #define YKPIV_KEY_RETIRED20 0x95 +#define YKPIV_KEY_ATTESTATION 0xf9 #define YKPIV_OBJ_CAPABILITY 0x5fc107 #define YKPIV_OBJ_CHUID 0x5fc102 From f5fab7e96286343d1bcdac7f63814af5c2ce5229 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 17 Mar 2016 12:29:25 +0100 Subject: [PATCH 6/7] add ykpiv touchpolicy to ykpiv --- lib/ykpiv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index ab58f94..01c6b9b 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -885,7 +885,8 @@ ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, u if (touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && touch_policy != YKPIV_TOUCHPOLICY_NEVER && - touch_policy != YKPIV_TOUCHPOLICY_ALWAYS) + touch_policy != YKPIV_TOUCHPOLICY_ALWAYS && + touch_policy != YKPIV_TOUCHPOLICY_CACHED) return YKPIV_GENERIC_ERROR; if (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { From c28d88a1d95989017acd64a1ecf78e0bfd1334a4 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 31 Mar 2016 13:34:54 +0200 Subject: [PATCH 7/7] add some documentation for attestation --- doc/Attestation.adoc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 doc/Attestation.adoc diff --git a/doc/Attestation.adoc b/doc/Attestation.adoc new file mode 100644 index 0000000..818d457 --- /dev/null +++ b/doc/Attestation.adoc @@ -0,0 +1,20 @@ +Using Attestation +----------------- + +Attestation works through a special key slot called “f9” this comes +pre-loaded from factory with a key and cert signed by Yubico, but can be +overwritten. +After a key has been generated in a normal slot it can be attested by this +special key, this can be realised by using the yubico-piv-tool action attest: + + $ yubico-piv-tool --action=generate --slot=9a + ... + $ yubico-piv-tool --action=attest --slot=9a + +The output of this is a PEM encoded certificate, signed by the key in slot f9. There are a couple of special extensions on this certificate: + +* +1.3.6.1.4.1.41482.3.3+: Firmware version, encoded as 3 bytes, like: 040300 for 4.3.0 +* +1.3.6.1.4.1.41482.3.7+: Serial number, encoded as an integer. +* +1.3.6.1.4.1.41482.3.8+: Two bytes, the first encoding pin policy and the second touch policy +** Pin policy: 01 - never, 02 - once per session, 03 - always +** Touch policy: 01 - never, 02 - always, 03 - cached for 15s