Compare commits

...

12 Commits

Author SHA1 Message Date
Conor Patrick
690d7c716a move CTAPHID_STATUS_PROCESSING to after UP 2019-07-29 12:39:59 -04:00
Conor Patrick
78e3b291c2 make sure device status is set in all user presence tests 2019-07-28 22:10:56 -04:00
Conor Patrick
b47854c335 use error code PIN_AUTH_INVALID 2019-07-28 21:41:11 -04:00
Conor Patrick
2af747ddaa Merge pull request #229 from solokeys/fix-hmac-secret
Fix hmac secret
2019-07-27 12:49:30 -04:00
Conor Patrick
9ead11de8d Merge pull request #224 from solokeys/fault_tolerance
limit length of wLength
2019-07-27 12:47:28 -04:00
Conor Patrick
f17faca689 use correct size for auth_data for signature 2019-07-26 23:53:20 -04:00
Conor Patrick
ca66b6e43b verify signature for hmac-secret 2019-07-26 23:51:39 -04:00
Conor Patrick
1cd1b3c295 check attestation signature on all MC requests 2019-07-26 23:50:23 -04:00
Conor Patrick
df2cff2350 patch hmac final to use correct key 2019-07-26 23:49:55 -04:00
Conor Patrick
f5d50e001d test assertions work post reboot 2019-07-26 19:00:07 -04:00
Nicolas Stalder
235785b225 Bump stable version to 2.4.0 2019-07-17 23:42:56 +02:00
Conor Patrick
303c42901a limit length of wLength 2019-07-15 11:32:02 -04:00
8 changed files with 103 additions and 27 deletions

View File

@@ -1 +1 @@
2.3.0 2.4.0

View File

@@ -38,6 +38,7 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey); void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret); void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1) #define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
#define CRYPTO_MASTER_KEY ((uint8_t*)0) #define CRYPTO_MASTER_KEY ((uint8_t*)0)

View File

@@ -355,9 +355,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
} }
// Generate credRandom // Generate credRandom
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom); crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId)); crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom); crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
// Decrypt saltEnc // Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL); crypto_aes256_init(shared_secret, NULL);
@@ -432,6 +432,12 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
return sizeof(CredentialId); return sizeof(CredentialId);
} }
static int ctap2_user_presence_test()
{
device_set_status(CTAPHID_STATUS_UPNEEDED);
return ctap_user_presence_test(CTAP2_UP_DELAY_MS);
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo) static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
{ {
CborEncoder cose_key; CborEncoder cose_key;
@@ -459,11 +465,9 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
count = auth_data_update_count(&authData->head); count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED);
int but; int but;
but = ctap_user_presence_test(CTAP2_UP_DELAY_MS); but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
if (!but) if (!but)
{ {
@@ -473,6 +477,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
{ {
return CTAP2_ERR_KEEPALIVE_CANCEL; return CTAP2_ERR_KEEPALIVE_CANCEL;
} }
device_set_status(CTAPHID_STATUS_PROCESSING); device_set_status(CTAPHID_STATUS_PROCESSING);
authData->head.flags = (but << 0); authData->head.flags = (but << 0);
@@ -605,7 +610,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_final(hashbuf); crypto_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf); crypto_ecc256_sign(hashbuf, 32, sigbuf);
return ctap_encode_der_sig(sigbuf,sigder); return ctap_encode_der_sig(sigbuf,sigder);
} }
@@ -701,11 +705,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
} }
if (MC.pinAuthEmpty) if (MC.pinAuthEmpty)
{ {
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS)) if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{ {
return CTAP2_ERR_OPERATION_DENIED; return CTAP2_ERR_OPERATION_DENIED;
} }
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET; return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
} }
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask) if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
{ {
@@ -1056,7 +1060,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
else else
#endif #endif
{ {
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder); sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
} }
{ {
@@ -1137,11 +1141,11 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
if (GA.pinAuthEmpty) if (GA.pinAuthEmpty)
{ {
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS)) if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{ {
return CTAP2_ERR_OPERATION_DENIED; return CTAP2_ERR_OPERATION_DENIED;
} }
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET; return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
} }
if (GA.pinAuthPresent) if (GA.pinAuthPresent)
{ {
@@ -1604,7 +1608,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
switch(cmd) switch(cmd)
{ {
case CTAP_MAKE_CREDENTIAL: case CTAP_MAKE_CREDENTIAL:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n"); printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
timestamp(); timestamp();
status = ctap_make_credential(&encoder, pkt_raw, length); status = ctap_make_credential(&encoder, pkt_raw, length);
@@ -1615,7 +1618,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break; break;
case CTAP_GET_ASSERTION: case CTAP_GET_ASSERTION:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n"); printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
timestamp(); timestamp();
status = ctap_get_assertion(&encoder, pkt_raw, length); status = ctap_get_assertion(&encoder, pkt_raw, length);
@@ -1647,7 +1649,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break; break;
case CTAP_RESET: case CTAP_RESET:
printf1(TAG_CTAP,"CTAP_RESET\n"); printf1(TAG_CTAP,"CTAP_RESET\n");
if (ctap_user_presence_test(CTAP2_UP_DELAY_MS)) if (ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{ {
ctap_reset(); ctap_reset();
} }

View File

@@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL; uint8_t *pbuf = NULL;
uint16_t status_info = 0U; uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK; USBD_StatusTypeDef ret = USBD_OK;
req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK) switch (req->bmRequest & USB_REQ_TYPE_MASK)
{ {
@@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break; break;
case USB_REQ_GET_DESCRIPTOR: case USB_REQ_GET_DESCRIPTOR:
req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC) if(req->wValue >> 8 == HID_REPORT_DESC)
{ {
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength); len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);

View File

@@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret; key = master_secret;
klen = sizeof(master_secret)/2; klen = sizeof(master_secret)/2;
} }
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
}
if(klen > 64) if(klen > 64)

View File

@@ -211,7 +211,7 @@ class FIDO2Tests(Tester):
assert "hmac-secret" in reg.auth_data.extensions assert "hmac-secret" in reg.auth_data.extensions
assert reg.auth_data.extensions["hmac-secret"] == True assert reg.auth_data.extensions["hmac-secret"] == True
reg = self.testMC( self.testMC(
"Send MC with fake extension set to true, expect SUCCESS", "Send MC with fake extension set to true, expect SUCCESS",
cdh, cdh,
rp, rp,
@@ -278,6 +278,10 @@ class FIDO2Tests(Tester):
assert shannon_entropy(ext["hmac-secret"]) > 5.4 assert shannon_entropy(ext["hmac-secret"]) > 5.4
assert shannon_entropy(key) > 5.4 assert shannon_entropy(key) > 5.4
with Test("Check that the assertion is valid"):
credential_data = AttestedCredentialData(reg.auth_data.credential_data)
auth.verify(cdh, credential_data.public_key)
salt_enc, salt_auth = get_salt_params((salt3,)) salt_enc, salt_auth = get_salt_params((salt3,))
auth = self.testGA( auth = self.testGA(
@@ -743,6 +747,40 @@ class FIDO2Tests(Tester):
expectedError=CtapError.ERR.SUCCESS, expectedError=CtapError.ERR.SUCCESS,
) )
with Test("Check assertion is correct"):
credential_data = AttestedCredentialData(prev_reg.auth_data.credential_data)
prev_auth.verify(cdh, credential_data.public_key)
assert (
prev_auth.credential["id"]
== prev_reg.auth_data.credential_data.credential_id
)
self.reboot()
prev_auth = self.testGA(
"Send GA request after reboot, expect success",
rp["id"],
cdh,
allow_list,
expectedError=CtapError.ERR.SUCCESS,
)
with Test("Check assertion is correct"):
credential_data = AttestedCredentialData(prev_reg.auth_data.credential_data)
prev_auth.verify(cdh, credential_data.public_key)
assert (
prev_auth.credential["id"]
== prev_reg.auth_data.credential_data.credential_id
)
prev_auth = self.testGA(
"Send GA request, expect success",
rp["id"],
cdh,
allow_list,
expectedError=CtapError.ERR.SUCCESS,
)
with Test("Test auth_data is 37 bytes"): with Test("Test auth_data is 37 bytes"):
assert len(prev_auth.auth_data) == 37 assert len(prev_auth.auth_data) == 37
@@ -1096,7 +1134,10 @@ class FIDO2Tests(Tester):
rp["id"], rp["id"],
cdh, cdh,
other={"pin_auth": b"", "pin_protocol": pin_protocol}, other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_NOT_SET, expectedError=[
CtapError.ERR.PIN_AUTH_INVALID,
CtapError.ERR.NO_CREDENTIALS,
],
) )
with Test("Setting pin code, expect SUCCESS"): with Test("Setting pin code, expect SUCCESS"):
@@ -1110,14 +1151,17 @@ class FIDO2Tests(Tester):
user, user,
key_params, key_params,
other={"pin_auth": b"", "pin_protocol": pin_protocol}, other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_INVALID, expectedError=CtapError.ERR.PIN_AUTH_INVALID,
) )
self.testGA( self.testGA(
"Send MC request with new pin auth", "Send MC request with new pin auth",
rp["id"], rp["id"],
cdh, cdh,
other={"pin_auth": b"", "pin_protocol": pin_protocol}, other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_INVALID, expectedError=[
CtapError.ERR.PIN_AUTH_INVALID,
CtapError.ERR.NO_CREDENTIALS,
],
) )
self.testReset() self.testReset()
@@ -1273,13 +1317,13 @@ class FIDO2Tests(Tester):
self.testReset() self.testReset()
self.test_get_info() # self.test_get_info()
#
self.test_get_assertion() # self.test_get_assertion()
#
self.test_make_credential() # self.test_make_credential()
#
self.test_rk(None) # self.test_rk(None)
self.test_client_pin() self.test_client_pin()

View File

@@ -2,6 +2,7 @@ import time, struct
from fido2.hid import CtapHidDevice from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client from fido2.client import Fido2Client
from fido2.attestation import Attestation
from fido2.ctap1 import CTAP1 from fido2.ctap1 import CTAP1
from fido2.utils import Timeout from fido2.utils import Timeout
@@ -201,7 +202,18 @@ class Tester:
self.ctap.reset() self.ctap.reset()
def testMC(self, test, *args, **kwargs): def testMC(self, test, *args, **kwargs):
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs) attestation_object = self.testFunc(
self.ctap.make_credential, test, *args, **kwargs
)
if attestation_object:
verifier = Attestation.for_type(attestation_object.fmt)
client_data = args[0]
verifier().verify(
attestation_object.att_statement,
attestation_object.auth_data,
client_data,
)
return attestation_object
def testGA(self, test, *args, **kwargs): def testGA(self, test, *args, **kwargs):
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs) return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)

View File

@@ -78,6 +78,16 @@ class U2FTests(Tester):
auth = self.authenticate(chal, appid, regs[i].key_handle) auth = self.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key) auth.verify(appid, chal, regs[i].public_key)
self.reboot()
for i in range(0, self.user_count):
with Test(
"Post reboot, Checking previous registration %d/%d"
% (i + 1, self.user_count)
):
auth = self.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key)
print("Check that all previous credentials are registered...") print("Check that all previous credentials are registered...")
for i in range(0, self.user_count): for i in range(0, self.user_count):
with Test("Check that previous credential %d is registered" % i): with Test("Check that previous credential %d is registered" % i):