passing certification and interop with rk

This commit is contained in:
Conor Patrick 2018-11-13 01:45:59 -05:00 committed by Emanuele Cesena
parent fc8bc892c1
commit feceeb0a22
6 changed files with 229 additions and 21 deletions

View File

@ -302,10 +302,10 @@ static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
} }
uint8_t * byte = (uint8_t*) &authData->signCount; uint8_t * byte = (uint8_t*) &authData->signCount;
*byte++ = (count >> 0) & 0xff;
*byte++ = (count >> 8) & 0xff;
*byte++ = (count >> 16) & 0xff;
*byte++ = (count >> 24) & 0xff; *byte++ = (count >> 24) & 0xff;
*byte++ = (count >> 16) & 0xff;
*byte++ = (count >> 8) & 0xff;
*byte++ = (count >> 0) & 0xff;
return count; return count;
} }
@ -344,6 +344,9 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
crypto_sha256_update(rp->id, rp->size); crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(authData->head.rpIdHash); crypto_sha256_final(authData->head.rpIdHash);
printf1(TAG_RED, "rpId: "); dump_hex1(TAG_RED, rp->id, rp->size);
printf1(TAG_RED, "hash: "); dump_hex1(TAG_RED, authData->head.rpIdHash, 32);
count = auth_data_update_count(&authData->head); count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED); device_set_status(CTAPHID_STATUS_UPNEEDED);
@ -691,10 +694,10 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
int ret = cbor_encode_int(map, RESP_publicKeyCredentialUserEntity); int ret = cbor_encode_int(map, RESP_publicKeyCredentialUserEntity);
check_ret(ret); check_ret(ret);
int dispname = (user->name[0] != 0); int dispname = (user->name[0] != 0) && getAssertionState.user_verified;
if (dispname) if (dispname)
ret = cbor_encoder_create_map(map, &entity, 2); ret = cbor_encoder_create_map(map, &entity, 4);
else else
ret = cbor_encoder_create_map(map, &entity, 1); ret = cbor_encoder_create_map(map, &entity, 1);
check_ret(ret); check_ret(ret);
@ -715,6 +718,20 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
ret = cbor_encode_text_stringz(&entity, (const char *)user->name); ret = cbor_encode_text_stringz(&entity, (const char *)user->name);
check_ret(ret); check_ret(ret);
ret = cbor_encode_text_string(&entity, "displayName", 11);
check_ret(ret);
ret = cbor_encode_text_stringz(&entity, (const char *)user->displayName);
check_ret(ret);
ret = cbor_encode_text_string(&entity, "icon", 4);
check_ret(ret);
ret = cbor_encode_text_stringz(&entity, (const char *)user->icon);
check_ret(ret);
} }
ret = cbor_encoder_close_container(map, &entity); ret = cbor_encoder_close_container(map, &entity);
@ -871,7 +888,9 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
{ {
int ret; int ret;
CborEncoder map; CborEncoder map;
CTAP_authDataHeader * authData = &getAssertionState.authData; CTAP_authDataHeader authData;
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
// CTAP_authDataHeader * authData = &getAssertionState.authData;
CTAP_credentialDescriptor * cred = pop_credential(); CTAP_credentialDescriptor * cred = pop_credential();
@ -880,10 +899,10 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
return CTAP2_ERR_NOT_ALLOWED; return CTAP2_ERR_NOT_ALLOWED;
} }
auth_data_update_count(authData); auth_data_update_count(&authData);
int add_user_info = cred->credential.user.id_size && getAssertionState.user_verified; int add_user_info = cred->credential.user.id_size;
if (getAssertionState.user_verified) if (add_user_info)
{ {
printf1(TAG_GREEN, "adding user info to assertion response\r\n"); printf1(TAG_GREEN, "adding user info to assertion response\r\n");
ret = cbor_encoder_create_map(encoder, &map, 4); ret = cbor_encoder_create_map(encoder, &map, 4);
@ -895,15 +914,24 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
} }
check_ret(ret); check_ret(ret);
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
{ {
ret = cbor_encode_int(&map,RESP_authData); ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret); check_ret(ret);
ret = cbor_encode_byte_string(&map, (uint8_t *)authData, sizeof(CTAP_authDataHeader)); ret = cbor_encode_byte_string(&map, (uint8_t *)&authData, sizeof(CTAP_authDataHeader));
check_ret(ret); check_ret(ret);
} }
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)authData, getAssertionState.clientDataHash, add_user_info); // if only one account for this RP, null out the user details
if (!getAssertionState.user_verified)
{
printf1(TAG_GREEN, "Not verified, nulling out user details on response\r\n");
memset(cred->credential.user.name, 0, USER_NAME_LIMIT);
}
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)&authData, getAssertionState.clientDataHash, add_user_info);
check_retr(ret); check_retr(ret);
ret = cbor_encoder_close_container(encoder, &map); ret = cbor_encoder_close_container(encoder, &map);
@ -950,7 +978,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen); printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
int validCredCount = ctap_filter_invalid_credentials(&GA); int validCredCount = ctap_filter_invalid_credentials(&GA);
int add_user_info = GA.creds[validCredCount - 1].credential.user.id_size && getAssertionState.user_verified; int add_user_info = GA.creds[validCredCount - 1].credential.user.id_size;
if (validCredCount > 1) if (validCredCount > 1)
{ {
map_size += 1; map_size += 1;
@ -995,7 +1023,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
} }
// if only one account for this RP, null out the user details // if only one account for this RP, null out the user details
if (validCredCount < 2) if (validCredCount < 2 || !getAssertionState.user_verified)
{ {
printf1(TAG_GREEN, "Only one account, nulling out user details on response\r\n"); printf1(TAG_GREEN, "Only one account, nulling out user details on response\r\n");
memset(&GA.creds[0].credential.user.name, 0, USER_NAME_LIMIT); memset(&GA.creds[0].credential.user.name, 0, USER_NAME_LIMIT);
@ -1340,11 +1368,13 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
length--; length--;
uint8_t * buf = resp->data; uint8_t * buf = resp->data;
printf1(TAG_GREEN, "lastcmd0 = 0x%02x\r\n", getAssertionState.lastcmd);
cbor_encoder_init(&encoder, buf, resp->data_size, 0); cbor_encoder_init(&encoder, buf, resp->data_size, 0);
printf1(TAG_CTAP,"cbor input structure: %d bytes\n", length); printf1(TAG_CTAP,"cbor input structure: %d bytes\n", length);
printf1(TAG_DUMP,"cbor req: "); dump_hex1(TAG_DUMP, pkt_raw, length); printf1(TAG_DUMP,"cbor req: "); dump_hex1(TAG_DUMP, pkt_raw, length);
printf1(TAG_GREEN, "lastcmd1 = 0x%02x\r\n", getAssertionState.lastcmd);
switch(cmd) switch(cmd)
{ {
@ -1434,7 +1464,8 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
} }
else else
{ {
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION\n"); printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
dump_hex1(TAG_GREEN, &getAssertionState, sizeof(getAssertionState));
status = CTAP2_ERR_NOT_ALLOWED; status = CTAP2_ERR_NOT_ALLOWED;
} }
break; break;
@ -1446,6 +1477,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
done: done:
device_set_status(CTAPHID_STATUS_IDLE); device_set_status(CTAPHID_STATUS_IDLE);
getAssertionState.lastcmd = cmd; getAssertionState.lastcmd = cmd;
printf1(TAG_GREEN, "lastcmd = 0x%02x\r\n", getAssertionState.lastcmd);
if (status != CTAP1_ERR_SUCCESS) if (status != CTAP1_ERR_SUCCESS)
{ {

View File

@ -109,6 +109,8 @@
#define RP_NAME_LIMIT 32 // application limit, name parameter isn't needed. #define RP_NAME_LIMIT 32 // application limit, name parameter isn't needed.
#define USER_ID_MAX_SIZE 64 #define USER_ID_MAX_SIZE 64
#define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more. #define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more.
#define DISPLAY_NAME_LIMIT 32 // Must be minimum of 64 bytes but can be more.
#define ICON_LIMIT 128 // Must be minimum of 64 bytes but can be more.
#define CTAP_MAX_MESSAGE_SIZE 1200 #define CTAP_MAX_MESSAGE_SIZE 1200
#define CREDENTIAL_RK_FLASH_PAD 2 // size of RK should be 8-byte aligned to store in flash easily. #define CREDENTIAL_RK_FLASH_PAD 2 // size of RK should be 8-byte aligned to store in flash easily.
@ -140,6 +142,8 @@ typedef struct
uint8_t id[USER_ID_MAX_SIZE]; uint8_t id[USER_ID_MAX_SIZE];
uint8_t id_size; uint8_t id_size;
uint8_t name[USER_NAME_LIMIT]; uint8_t name[USER_NAME_LIMIT];
uint8_t displayName[DISPLAY_NAME_LIMIT];
uint8_t icon[ICON_LIMIT];
}__attribute__((packed)) CTAP_userEntity; }__attribute__((packed)) CTAP_userEntity;
typedef struct { typedef struct {

View File

@ -175,6 +175,13 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
printf2(TAG_ERR,"Error, expecting text string type for user.displayName value\n"); printf2(TAG_ERR,"Error, expecting text string type for user.displayName value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE; return CTAP2_ERR_INVALID_CBOR_TYPE;
} }
sz = DISPLAY_NAME_LIMIT;
ret = cbor_value_copy_text_string(&map, (char *)MC->user.displayName, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
} }
else if (strcmp((const char *)key, "icon") == 0) else if (strcmp((const char *)key, "icon") == 0)
{ {
@ -183,6 +190,14 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
printf2(TAG_ERR,"Error, expecting text string type for user.icon value\n"); printf2(TAG_ERR,"Error, expecting text string type for user.icon value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE; return CTAP2_ERR_INVALID_CBOR_TYPE;
} }
sz = ICON_LIMIT;
ret = cbor_value_copy_text_string(&map, (char *)MC->user.icon, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.icon[ICON_LIMIT - 1] = 0;
} }
else else
{ {

View File

@ -97,7 +97,7 @@ void ctaphid_init()
{ {
state = IDLE; state = IDLE;
buffer_reset(); buffer_reset();
ctap_reset_state(); //ctap_reset_state();
} }
static uint32_t get_new_cid() static uint32_t get_new_cid()

View File

@ -4,7 +4,7 @@
#define DEBUG_UART USART1 #define DEBUG_UART USART1
#define DEBUG_LEVEL 1 #define DEBUG_LEVEL 0
#define NON_BLOCK_PRINTING 0 #define NON_BLOCK_PRINTING 0
@ -33,7 +33,7 @@ void hw_init(void);
#define SOLO_BUTTON_PORT GPIOA #define SOLO_BUTTON_PORT GPIOA
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0 #define SOLO_BUTTON_PIN LL_GPIO_PIN_0
#define SKIP_BUTTON_CHECK_WITH_DELAY 1 #define SKIP_BUTTON_CHECK_WITH_DELAY 0
#define SKIP_BUTTON_CHECK_FAST 0 #define SKIP_BUTTON_CHECK_FAST 0
#endif #endif

View File

@ -390,7 +390,7 @@ class Tester():
def test_fido2_simple(self, pin_token=None): def test_fido2_simple(self, pin_token=None):
creds = [] creds = []
exclude_list = [] exclude_list = []
rp = {'id': 'examplo.org', 'name': 'ExaRP'} rp = {'id': self.origin, 'name': 'ExaRP'}
user = {'id': b'usee_od', 'name': 'AB User'} user = {'id': b'usee_od', 'name': 'AB User'}
challenge = 'Y2hhbGxlbmdl' challenge = 'Y2hhbGxlbmdl'
PIN = pin_token PIN = pin_token
@ -432,7 +432,6 @@ class Tester():
for i in range(0,2048**2): for i in range(0,2048**2):
creds = [] creds = []
print(i)
challenge = ''.join([abc[randint(0,len(abc)-1)] for x in range(0,32)]) challenge = ''.join([abc[randint(0,len(abc)-1)] for x in range(0,32)])
fake_id1 = array.array('B',[randint(0,255) for i in range(0,150)]).tostring() fake_id1 = array.array('B',[randint(0,255) for i in range(0,150)]).tostring()
@ -445,6 +444,7 @@ class Tester():
for i in range(0,1): for i in range(0,1):
t1 = time.time() * 1000 t1 = time.time() * 1000
attest, data = self.client.make_credential(rp, user, challenge, pin = PIN, exclude_list = []) attest, data = self.client.make_credential(rp, user, challenge, pin = PIN, exclude_list = [])
print(attest.auth_data.counter)
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) attest.verify(data.hash)
print('Register valid (%d ms)' % (t2-t1)) print('Register valid (%d ms)' % (t2-t1))
@ -460,6 +460,7 @@ class Tester():
assertions, client_data = self.client.get_assertion(rp['id'], challenge, allow_list, pin = PIN) assertions, client_data = self.client.get_assertion(rp['id'], challenge, allow_list, pin = PIN)
t2 = time.time() * 1000 t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key) assertions[0].verify(client_data.hash, creds[0].public_key)
print(assertions[0].auth_data.counter)
print('Assertion valid (%d ms)' % (t2-t1)) print('Assertion valid (%d ms)' % (t2-t1))
sys.stdout.flush() sys.stdout.flush()
@ -607,6 +608,160 @@ class Tester():
print('Warning, reset failed: ', e) print('Warning, reset failed: ', e)
print('PASS') print('PASS')
def test_rk(self, ):
creds = []
rp = {'id': 'examplo.org', 'name': 'ExaRP'}
user0 = {'id': b'first one', 'name': 'single User'}
users = [{'id': b'user' + os.urandom(16), 'name': 'AB User'} for i in range(0,2)]
challenge = 'Y2hhbGxlbmdl'
PIN = None
print('reset')
self.ctap.reset()
#if PIN: self.client.pin_protocol.set_pin(PIN)
print('registering 1 user with RK')
t1 = time.time() * 1000
attest, data = self.client.make_credential(rp, user0, challenge, pin = PIN, exclude_list = [], rk = True)
t2 = time.time() * 1000
attest.verify(data.hash)
creds.append(attest.auth_data.credential_data)
print('Register valid (%d ms)' % (t2-t1))
print('1 assertion')
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(rp['id'], challenge, pin = PIN)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print('Assertion valid (%d ms)' % (t2-t1))
print(assertions[0], client_data)
print('registering %d users with RK' % len(users))
for i in range(0,len(users)):
t1 = time.time() * 1000
attest, data = self.client.make_credential(rp, users[i], challenge, pin = PIN, exclude_list = [], rk = True)
t2 = time.time() * 1000
attest.verify(data.hash)
print('Register valid (%d ms)' % (t2-t1))
creds.append(attest.auth_data.credential_data)
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(rp['id'], challenge, pin = PIN)
t2 = time.time() * 1000
for x,y in zip(assertions, creds):
x.verify(client_data.hash,y.public_key)
print('Assertion(s) valid (%d ms)' % (t2-t1))
print('registering a duplicate user ')
t1 = time.time() * 1000
attest, data = self.client.make_credential(rp, users[1], challenge, pin = PIN, exclude_list = [], rk = True)
t2 = time.time() * 1000
attest.verify(data.hash)
creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data]
print('Register valid (%d ms)' % (t2-t1))
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(rp['id'], challenge, pin = PIN)
t2 = time.time() * 1000
assert(len(assertions) == len(users) +1)
for x,y in zip(assertions, creds):
x.verify(client_data.hash,y.public_key)
print('Assertion(s) valid (%d ms)' % (t2-t1))
def test_responses(self,):
PIN = '1234'
RPID = 'examplo2.org'
for dev in (CtapHidDevice.list_devices()):
print('dev',dev)
client = Fido2Client(dev, RPID)
ctap = client.ctap2
# ctap.reset()
try:
if PIN: client.pin_protocol.set_pin(PIN)
except:pass
inf = ctap.get_info()
#print (inf)
print('versions: ',inf.versions)
print('aaguid: ',inf.aaguid)
print('rk: ',inf.options['rk'])
print('clientPin: ',inf.options['clientPin'])
print('max_message_size: ',inf.max_msg_size)
#rp = {'id': 'SelectDevice', 'name': 'SelectDevice'}
rp = {'id': RPID, 'name': 'ExaRP'}
user = {'id': os.urandom(10), 'name': 'SelectDevice'}
user = {'id': b'21first one', 'name': 'single User'}
challenge = 'Y2hhbGxlbmdl'
if 1:
attest, data = client.make_credential(rp,
user, challenge, exclude_list = [], pin = PIN, rk=True)
cred = attest.auth_data.credential_data
creds = [cred]
allow_list = [{'id':creds[0].credential_id, 'type': 'public-key'}]
allow_list = []
assertions, client_data = client.get_assertion(rp['id'], challenge, pin = PIN)
assertions[0].verify(client_data.hash, creds[0].public_key)
if 0:
print('registering 1 user with RK')
t1 = time.time() * 1000
attest, data = client.make_credential(rp, user, challenge, pin = PIN, exclude_list = [], rk = True)
t2 = time.time() * 1000
attest.verify(data.hash)
creds = [attest.auth_data.credential_data]
print('Register valid (%d ms)' % (t2-t1))
print('1 assertion')
t1 = time.time() * 1000
assertions, client_data = client.get_assertion(rp['id'], challenge, pin = PIN)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print('Assertion valid (%d ms)' % (t2-t1))
#print('fmt:',attest.fmt)
#print('rp_id_hash',attest.auth_data.rp_id_hash)
#print('flags:', hex(attest.auth_data.flags))
#print('count:', hex(attest.auth_data.counter))
print('flags MC:',attest.auth_data)
print('flags GA:',assertions[0].auth_data)
#print('cred_id:',attest.auth_data.credential_data.credential_id)
#print('pubkey:',attest.auth_data.credential_data.public_key)
#print('aaguid:',attest.auth_data.credential_data.aaguid)
# print('cred data:',attest.auth_data.credential_data)
# print('auth_data:',attest.auth_data)
#print('auth_data:',attest.auth_data)
#print('alg:',attest.att_statement['alg'])
#print('sig:',attest.att_statement['sig'])
#print('x5c:',attest.att_statement['x5c'])
#print('data:',data)
print('assertion:', assertions[0])
print('clientData:', client_data)
print()
#break
def test_find_brute_force(): def test_find_brute_force():
i = 0 i = 0
while 1: while 1:
@ -621,10 +776,12 @@ def test_find_brute_force():
if __name__ == '__main__': if __name__ == '__main__':
t = Tester() t = Tester()
t.find_device() #t.find_device()
# t.test_hid() # t.test_hid()
# t.test_long_ping() # t.test_long_ping()
t.test_fido2() #t.test_fido2()
#t.test_rk()
t.test_responses()
# test_find_brute_force() # test_find_brute_force()
#t.test_fido2_simple() #t.test_fido2_simple()
#t.test_fido2_brute_force() #t.test_fido2_brute_force()