From 89e929682565dc11cfe8cc22a90add8ecc6f5119 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 13 Apr 2019 20:43:26 -0400 Subject: [PATCH 1/6] Add test --- tools/testing/tests/fido2.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/testing/tests/fido2.py b/tools/testing/tests/fido2.py index 60fdb05..097f1fd 100644 --- a/tools/testing/tests/fido2.py +++ b/tools/testing/tests/fido2.py @@ -14,6 +14,7 @@ from fido2.attestation import Attestation from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from .u2f import U2FTests from .tester import Tester, Test from .util import shannon_entropy @@ -880,6 +881,25 @@ class FIDO2Tests(Tester): allow_list + [{"type": b"public-key"}], ) + appid = sha256(rp["id"].encode("utf8")) + chal = sha256(challenge.encode("utf8")) + with Test("Send CTAP1 register request"): + u2f = U2FTests(self) + reg = u2f.register(chal, appid) + reg.verify(appid, chal) + + with Test("Authenticate CTAP1"): + auth = u2f.authenticate(chal, appid, reg.key_handle) + auth.verify(appid, chal, reg.public_key) + + self.testGA( + "Authenticate CTAP1 registration with CTAP2", + rp["id"], + cdh, + allow_list + [{"type": b"public-key", "id": reg.key_handle}], + expectedError=CtapError.ERR.SUCCESS, + ) + def test_rk(self, pin_code=None): pin_auth = None From 44fa3bbb8e0bbafb1eb9a765e0514e11dbd33231 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 13 Apr 2019 22:37:31 -0400 Subject: [PATCH 2/6] Add checks to use U2F key if necessary --- fido2/ctap.c | 33 +++++++++++++++++++++++++++------ fido2/ctap.h | 1 + fido2/ctap_parse.c | 18 ++++++++++++++---- fido2/u2f.c | 12 ++++++------ fido2/u2f.h | 1 + 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/fido2/ctap.c b/fido2/ctap.c index c7f2d55..1214b1e 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -11,6 +11,7 @@ #include "cbor.h" #include "ctap.h" +#include "u2f.h" #include "ctaphid.h" #include "ctap_parse.h" #include "ctap_errors.h" @@ -431,6 +432,12 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf return 0; } +static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred) +{ + if (cred->type == PUB_KEY_CRED_CTAP1) + return U2F_KEY_HANDLE_SIZE; + return sizeof(CredentialId); +} static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo) { @@ -655,11 +662,25 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len) // Return 1 if credential belongs to this token int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc) { + uint8_t rpIdHash[32]; uint8_t tag[16]; - make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag); + switch(desc->type) + { + case PUB_KEY_CRED_PUB_KEY: + make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag); + return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0); + break; + case PUB_KEY_CRED_CTAP1: + printf1(TAG_CTAP,"PUB_KEY_CRED_CTAP1\r\n"); + crypto_sha256_init(); + crypto_sha256_update(rp->id, rp->size); + crypto_sha256_final(rpIdHash); + return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, rpIdHash); + break; + } - return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0); + return 0; } @@ -806,7 +827,8 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder * map, CTAP_credential ret = cbor_encode_text_string(&desc, "id", 2); check_ret(ret); - ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id, sizeof(CredentialId)); + ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id, + get_credential_id_size(cred)); check_ret(ret); } @@ -1021,7 +1043,8 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr check_ret(ret); } - crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0); + unsigned int cred_size = get_credential_id_size(cred); + crypto_ecc256_load_key((uint8_t*)&cred->credential.id, cred_size, NULL, 0); #ifdef ENABLE_U2F_EXTENSIONS if ( extend_fido2(&cred->credential.id, sigder) ) @@ -1170,8 +1193,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.id.count); } - - CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1]; GA.extensions.hmac_secret.credential = &cred->credential; diff --git a/fido2/ctap.h b/fido2/ctap.h index 53a5d80..33f9edb 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -112,6 +112,7 @@ #define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes #define PUB_KEY_CRED_PUB_KEY 0x01 +#define PUB_KEY_CRED_CTAP1 0x41 #define PUB_KEY_CRED_UNKNOWN 0x3F #define CREDENTIAL_IS_SUPPORTED 1 diff --git a/fido2/ctap_parse.c b/fido2/ctap_parse.c index 64cd2f6..fd92e85 100644 --- a/fido2/ctap_parse.c +++ b/fido2/ctap_parse.c @@ -9,6 +9,7 @@ #include "cbor.h" #include "ctap.h" +#include "u2f.h" #include "ctap_parse.h" #include "ctap_errors.h" #include "cose_key.h" @@ -890,10 +891,15 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * buflen = sizeof(CredentialId); cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL); - if (buflen != sizeof(CredentialId)) + + if (buflen == U2F_KEY_HANDLE_SIZE) + { + printf2(TAG_PARSE,"CTAP1 credential\n"); + cred->type = PUB_KEY_CRED_CTAP1; + } + else if (buflen != sizeof(CredentialId)) { printf2(TAG_ERR,"Ignoring credential is incorrect length\n"); - //return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // maybe just skip it instead of fail? } ret = cbor_value_map_find_value(arr, "type", &val); @@ -906,11 +912,15 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * } buflen = sizeof(type); - cbor_value_copy_text_string(&val, type, &buflen, NULL); + ret = cbor_value_copy_text_string(&val, type, &buflen, NULL); + check_ret(ret); if (strncmp(type, "public-key",11) == 0) { - cred->type = PUB_KEY_CRED_PUB_KEY; + if (PUB_KEY_CRED_CTAP1 != cred->type) + { + cred->type = PUB_KEY_CRED_PUB_KEY; + } } else { diff --git a/fido2/u2f.c b/fido2/u2f.c index 677e04a..e711ea1 100644 --- a/fido2/u2f.c +++ b/fido2/u2f.c @@ -183,21 +183,21 @@ int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pu } - -static int8_t u2f_appid_eq(struct u2f_key_handle * kh, uint8_t * appid) +// Return 1 if authenticate, 0 if not. +int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid) { uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE]; u2f_make_auth_tag(kh, appid, tag); if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0) { - return 0; + return 1; } else { printf1(TAG_U2F, "key handle + appid not authentic\n"); printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE); printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE); - return -1; + return 0; } } @@ -214,7 +214,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c if (control == U2F_AUTHENTICATE_CHECK) { printf1(TAG_U2F, "CHECK-ONLY\r\n"); - if (u2f_appid_eq(&req->kh, req->app) == 0) + if (u2f_authenticate_credential(&req->kh, req->app)) { return U2F_SW_CONDITIONS_NOT_SATISFIED; } @@ -226,7 +226,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c if ( (control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) || req->khl != U2F_KEY_HANDLE_SIZE || - u2f_appid_eq(&req->kh, req->app) != 0 || // Order of checks is important + (!u2f_authenticate_credential(&req->kh, req->app)) || // Order of checks is important u2f_load_key(&req->kh, req->app) != 0 ) diff --git a/fido2/u2f.h b/fido2/u2f.h index 1fe7910..9055b36 100644 --- a/fido2/u2f.h +++ b/fido2/u2f.h @@ -103,6 +103,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp); // @len data length void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp); +int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid); int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len); void u2f_reset_response(); From 88a759566df67db72076c76223e86ddbb5594302 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 13 Apr 2019 22:37:47 -0400 Subject: [PATCH 3/6] Improve testing --- tools/testing/tests/fido2.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tools/testing/tests/fido2.py b/tools/testing/tests/fido2.py index 097f1fd..d5df640 100644 --- a/tools/testing/tests/fido2.py +++ b/tools/testing/tests/fido2.py @@ -7,7 +7,7 @@ from functools import cmp_to_key from fido2 import cbor from fido2.ctap import CtapError -from fido2.ctap2 import ES256, PinProtocolV1 +from fido2.ctap2 import ES256, PinProtocolV1, AttestedCredentialData from fido2.utils import sha256, hmac_sha256 from fido2.attestation import Attestation @@ -881,6 +881,8 @@ class FIDO2Tests(Tester): allow_list + [{"type": b"public-key"}], ) + self.testReset() + appid = sha256(rp["id"].encode("utf8")) chal = sha256(challenge.encode("utf8")) with Test("Send CTAP1 register request"): @@ -892,14 +894,25 @@ class FIDO2Tests(Tester): auth = u2f.authenticate(chal, appid, reg.key_handle) auth.verify(appid, chal, reg.public_key) - self.testGA( + auth = self.testGA( "Authenticate CTAP1 registration with CTAP2", rp["id"], cdh, - allow_list + [{"type": b"public-key", "id": reg.key_handle}], + [{"id": reg.key_handle, "type": "public-key"}], expectedError=CtapError.ERR.SUCCESS, ) + with Test("Check assertion is correct"): + credential_data = AttestedCredentialData.from_ctap1( + reg.key_handle, reg.public_key + ) + auth.verify(cdh, credential_data.public_key) + assert auth.credential["id"] == reg.key_handle + + import sys + + sys.exit(1) + def test_rk(self, pin_code=None): pin_auth = None From 9bb706987f94bce80d8a9cef7c97aac24be62514 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 13 Apr 2019 22:42:05 -0400 Subject: [PATCH 4/6] solo ext bugfix --- fido2/ctap.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fido2/ctap.c b/fido2/ctap.c index 1214b1e..4e7b465 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -1202,8 +1202,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) #ifdef ENABLE_U2F_EXTENSIONS if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) ) { - ret = cbor_encode_int(&map,RESP_authData); // 2 - check_ret(ret); memset(auth_data_buf,0,sizeof(CTAP_authDataHeader)); auth_data_buf_sz = sizeof(CTAP_authDataHeader); } From 7e490f17fc51c8c7a0e48fdfbf5cc593973bc315 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sat, 13 Apr 2019 22:42:16 -0400 Subject: [PATCH 5/6] delete --- tools/testing/tests/fido2.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/testing/tests/fido2.py b/tools/testing/tests/fido2.py index d5df640..cbb4de7 100644 --- a/tools/testing/tests/fido2.py +++ b/tools/testing/tests/fido2.py @@ -909,10 +909,6 @@ class FIDO2Tests(Tester): auth.verify(cdh, credential_data.public_key) assert auth.credential["id"] == reg.key_handle - import sys - - sys.exit(1) - def test_rk(self, pin_code=None): pin_auth = None From 74cbe00e3b760602a9c03bc2669ec6eeb784edd1 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sun, 14 Apr 2019 15:13:39 -0400 Subject: [PATCH 6/6] Enable debug logs for >0 --- targets/stm32l432/src/redirect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/stm32l432/src/redirect.c b/targets/stm32l432/src/redirect.c index 3eaf046..ff30d40 100644 --- a/targets/stm32l432/src/redirect.c +++ b/targets/stm32l432/src/redirect.c @@ -27,7 +27,7 @@ void _putchar(char c) int _write (int fd, const void *buf, unsigned long int len) { uint8_t * data = (uint8_t *) buf; -#if DEBUG_LEVEL>1 +#if DEBUG_LEVEL>0 // static uint8_t logbuf[1000] = {0}; // static int logbuflen = 0; // if (logbuflen + len > sizeof(logbuf)) {