commit
9b356076c5
@ -820,7 +820,7 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
|
|||||||
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
|
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
|
||||||
if (count == ALLOW_LIST_MAX_SIZE-1)
|
if (count == ALLOW_LIST_MAX_SIZE-1)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d)\r\n", count);
|
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d). Skipping.\r\n", count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
|
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
|
||||||
|
88
pc/device.c
88
pc/device.c
@ -22,6 +22,11 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ctaphid.h"
|
#include "ctaphid.h"
|
||||||
|
|
||||||
|
#define RK_NUM 50
|
||||||
|
|
||||||
|
struct ResidentKeyStore {
|
||||||
|
CTAP_residentKey rks[RK_NUM];
|
||||||
|
} RK_STORE;
|
||||||
|
|
||||||
void authenticator_initialize();
|
void authenticator_initialize();
|
||||||
|
|
||||||
@ -251,6 +256,7 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
|
|||||||
|
|
||||||
const char * state_file = "authenticator_state.bin";
|
const char * state_file = "authenticator_state.bin";
|
||||||
const char * backup_file = "authenticator_state2.bin";
|
const char * backup_file = "authenticator_state2.bin";
|
||||||
|
const char * rk_file = "resident_keys.bin";
|
||||||
|
|
||||||
void authenticator_read_state(AuthenticatorState * state)
|
void authenticator_read_state(AuthenticatorState * state)
|
||||||
{
|
{
|
||||||
@ -370,6 +376,24 @@ int authenticator_is_backup_initialized()
|
|||||||
|
|
||||||
/*}*/
|
/*}*/
|
||||||
|
|
||||||
|
static void sync_rk()
|
||||||
|
{
|
||||||
|
FILE * f = fopen(rk_file, "wb+");
|
||||||
|
if (f== NULL)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = fwrite(&RK_STORE, 1, sizeof(RK_STORE), f);
|
||||||
|
fclose(f);
|
||||||
|
if (ret != sizeof(RK_STORE))
|
||||||
|
{
|
||||||
|
perror("fwrite");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void authenticator_initialize()
|
void authenticator_initialize()
|
||||||
{
|
{
|
||||||
uint8_t header[16];
|
uint8_t header[16];
|
||||||
@ -393,6 +417,22 @@ void authenticator_initialize()
|
|||||||
perror("fwrite");
|
perror("fwrite");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resident_keys
|
||||||
|
f = fopen(rk_file, "rb");
|
||||||
|
if (f== NULL)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ret = fread(&RK_STORE, 1, sizeof(RK_STORE), f);
|
||||||
|
fclose(f);
|
||||||
|
if(ret != sizeof(RK_STORE))
|
||||||
|
{
|
||||||
|
perror("fwrite");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -431,6 +471,12 @@ void authenticator_initialize()
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resident_keys
|
||||||
|
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||||
|
sync_rk();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,26 +485,52 @@ void device_manage()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ctap_reset_rk()
|
void ctap_reset_rk()
|
||||||
{
|
{
|
||||||
|
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||||
|
sync_rk();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ctap_rk_size()
|
uint32_t ctap_rk_size()
|
||||||
{
|
{
|
||||||
printf("Warning: rk not implemented\n");
|
return RK_NUM;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
void ctap_store_rk(int index,CTAP_residentKey * rk)
|
|
||||||
|
|
||||||
|
void ctap_store_rk(int index, CTAP_residentKey * rk)
|
||||||
{
|
{
|
||||||
printf("Warning: rk not implemented\n");
|
if (index < RK_NUM)
|
||||||
|
{
|
||||||
|
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||||
|
sync_rk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void ctap_load_rk(int index,CTAP_residentKey * rk)
|
|
||||||
|
|
||||||
|
void ctap_load_rk(int index, CTAP_residentKey * rk)
|
||||||
{
|
{
|
||||||
printf("Warning: rk not implemented\n");
|
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
|
||||||
}
|
}
|
||||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
|
||||||
|
void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
|
||||||
{
|
{
|
||||||
printf("Warning: rk not implemented\n");
|
if (index < RK_NUM)
|
||||||
|
{
|
||||||
|
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||||
|
sync_rk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_wink()
|
void device_wink()
|
||||||
|
@ -596,7 +596,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
|||||||
|
|
||||||
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
||||||
flash_erase_page(page);
|
flash_erase_page(page);
|
||||||
flash_write(flash_addr(page), tmppage, ((sizeof(CTAP_residentKey) * (index + 1)) % PAGE_SIZE) );
|
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ import array, struct, socket
|
|||||||
from fido2.hid import CtapHidDevice, CTAPHID
|
from fido2.hid import CtapHidDevice, CTAPHID
|
||||||
from fido2.client import Fido2Client, ClientError
|
from fido2.client import Fido2Client, ClientError
|
||||||
from fido2.ctap import CtapError
|
from fido2.ctap import CtapError
|
||||||
from fido2.ctap1 import CTAP1
|
from fido2.ctap1 import CTAP1, ApduError, APDU
|
||||||
from fido2.ctap2 import *
|
from fido2.ctap2 import *
|
||||||
from fido2.cose import *
|
from fido2.cose import *
|
||||||
from fido2.utils import Timeout, sha256
|
from fido2.utils import Timeout, sha256
|
||||||
@ -61,6 +61,7 @@ class Tester:
|
|||||||
def __init__(self,):
|
def __init__(self,):
|
||||||
self.origin = "https://examplo.org"
|
self.origin = "https://examplo.org"
|
||||||
self.host = "examplo.org"
|
self.host = "examplo.org"
|
||||||
|
self.user_count = 10
|
||||||
|
|
||||||
def find_device(self,):
|
def find_device(self,):
|
||||||
print(list(CtapHidDevice.list_devices()))
|
print(list(CtapHidDevice.list_devices()))
|
||||||
@ -75,6 +76,9 @@ class Tester:
|
|||||||
# consume timeout error
|
# consume timeout error
|
||||||
# cmd,resp = self.recv_raw()
|
# cmd,resp = self.recv_raw()
|
||||||
|
|
||||||
|
def set_user_count(self, count):
|
||||||
|
self.user_count = count
|
||||||
|
|
||||||
def send_data(self, cmd, data):
|
def send_data(self, cmd, data):
|
||||||
if type(data) != type(b""):
|
if type(data) != type(b""):
|
||||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||||
@ -393,16 +397,101 @@ class Tester:
|
|||||||
chal = sha256(b"AAA")
|
chal = sha256(b"AAA")
|
||||||
appid = sha256(b"BBB")
|
appid = sha256(b"BBB")
|
||||||
lastc = 0
|
lastc = 0
|
||||||
for i in range(0, 5):
|
|
||||||
|
regs = []
|
||||||
|
|
||||||
|
print("Check version")
|
||||||
|
assert self.ctap1.get_version() == "U2F_V2"
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
print("Check bad INS")
|
||||||
|
try:
|
||||||
|
res = self.ctap1.send_apdu(0, 0, 0, 0, b"")
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == 0x6D00
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
print("Check bad CLA")
|
||||||
|
try:
|
||||||
|
res = self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == 0x6E00
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
for i in range(0, self.user_count):
|
||||||
reg = self.ctap1.register(chal, appid)
|
reg = self.ctap1.register(chal, appid)
|
||||||
reg.verify(appid, chal)
|
reg.verify(appid, chal)
|
||||||
auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
|
auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
|
||||||
|
auth.verify(appid, chal, reg.public_key)
|
||||||
|
|
||||||
|
regs.append(reg)
|
||||||
# check endianness
|
# check endianness
|
||||||
if lastc:
|
if lastc:
|
||||||
assert (auth.counter - lastc) < 10
|
assert (auth.counter - lastc) < 10
|
||||||
lastc = auth.counter
|
lastc = auth.counter
|
||||||
print(hex(lastc))
|
if lastc > 0x100000:
|
||||||
print("U2F reg + auth pass %d/5" % (i + 1))
|
print("WARNING: counter is unusually high: %04x" % lastc)
|
||||||
|
assert 0
|
||||||
|
|
||||||
|
print(
|
||||||
|
"U2F reg + auth pass %d/%d (count: %02x)"
|
||||||
|
% (i + 1, self.user_count, lastc)
|
||||||
|
)
|
||||||
|
|
||||||
|
print("Checking previous registrations...")
|
||||||
|
for i in range(0, self.user_count):
|
||||||
|
auth = self.ctap1.authenticate(chal, appid, regs[i].key_handle)
|
||||||
|
auth.verify(appid, chal, regs[i].public_key)
|
||||||
|
print("Auth pass %d/%d" % (i + 1, self.user_count))
|
||||||
|
|
||||||
|
print("Check that all previous credentials are registered...")
|
||||||
|
for i in range(0, self.user_count):
|
||||||
|
try:
|
||||||
|
auth = self.ctap1.authenticate(
|
||||||
|
chal, appid, regs[i].key_handle, check_only=True
|
||||||
|
)
|
||||||
|
except ApduError as e:
|
||||||
|
# Indicates that key handle is registered
|
||||||
|
assert e.code == APDU.USE_NOT_SATISFIED
|
||||||
|
|
||||||
|
print("Check pass %d/%d" % (i + 1, self.user_count))
|
||||||
|
|
||||||
|
print("Check an incorrect key handle is not registered")
|
||||||
|
kh = bytearray(regs[0].key_handle)
|
||||||
|
kh[0] = kh[0] ^ (0x40)
|
||||||
|
try:
|
||||||
|
self.ctap1.authenticate(chal, appid, kh, check_only=True)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
print("Try to sign with incorrect key handle")
|
||||||
|
try:
|
||||||
|
self.ctap1.authenticate(chal, appid, kh)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
print("Try to sign using an incorrect keyhandle length")
|
||||||
|
try:
|
||||||
|
kh = regs[0].key_handle
|
||||||
|
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
|
print("Try to sign using an incorrect appid")
|
||||||
|
badid = bytearray(appid)
|
||||||
|
badid[0] = badid[0] ^ (0x40)
|
||||||
|
try:
|
||||||
|
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
print("Pass")
|
||||||
|
|
||||||
def test_fido2_simple(self, pin_token=None):
|
def test_fido2_simple(self, pin_token=None):
|
||||||
creds = []
|
creds = []
|
||||||
@ -515,8 +604,8 @@ class Tester:
|
|||||||
exclude_list.append({"id": fake_id2, "type": "public-key"})
|
exclude_list.append({"id": fake_id2, "type": "public-key"})
|
||||||
|
|
||||||
# test make credential
|
# test make credential
|
||||||
print("make 3 credentials")
|
print("make %d credentials" % self.user_count)
|
||||||
for i in range(0, 3):
|
for i in range(0, self.user_count):
|
||||||
attest, data = self.client.make_credential(
|
attest, data = self.client.make_credential(
|
||||||
rp, user, challenge, pin=PIN, exclude_list=[]
|
rp, user, challenge, pin=PIN, exclude_list=[]
|
||||||
)
|
)
|
||||||
@ -657,10 +746,10 @@ class Tester:
|
|||||||
def test_rk(self,):
|
def test_rk(self,):
|
||||||
creds = []
|
creds = []
|
||||||
rp = {"id": self.host, "name": "ExaRP"}
|
rp = {"id": self.host, "name": "ExaRP"}
|
||||||
user0 = {"id": b"first one", "name": "single User"}
|
|
||||||
|
|
||||||
users = [
|
users = [
|
||||||
{"id": b"user" + os.urandom(16), "name": "AB User"} for i in range(0, 2)
|
{"id": b"user" + os.urandom(16), "name": "Username%d" % i}
|
||||||
|
for i in range(0, self.user_count)
|
||||||
]
|
]
|
||||||
challenge = "Y2hhbGxlbmdl"
|
challenge = "Y2hhbGxlbmdl"
|
||||||
PIN = None
|
PIN = None
|
||||||
@ -671,7 +760,7 @@ class Tester:
|
|||||||
print("registering 1 user with RK")
|
print("registering 1 user with RK")
|
||||||
t1 = time.time() * 1000
|
t1 = time.time() * 1000
|
||||||
attest, data = self.client.make_credential(
|
attest, data = self.client.make_credential(
|
||||||
rp, user0, challenge, pin=PIN, exclude_list=[], rk=True
|
rp, users[-1], challenge, pin=PIN, exclude_list=[], rk=True
|
||||||
)
|
)
|
||||||
t2 = time.time() * 1000
|
t2 = time.time() * 1000
|
||||||
VerifyAttestation(attest, data)
|
VerifyAttestation(attest, data)
|
||||||
@ -690,7 +779,7 @@ class Tester:
|
|||||||
print(assertions[0], client_data)
|
print(assertions[0], client_data)
|
||||||
|
|
||||||
print("registering %d users with RK" % len(users))
|
print("registering %d users with RK" % len(users))
|
||||||
for i in range(0, len(users)):
|
for i in range(0, len(users) - 1):
|
||||||
t1 = time.time() * 1000
|
t1 = time.time() * 1000
|
||||||
attest, data = self.client.make_credential(
|
attest, data = self.client.make_credential(
|
||||||
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
|
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
|
||||||
@ -707,6 +796,9 @@ class Tester:
|
|||||||
)
|
)
|
||||||
t2 = time.time() * 1000
|
t2 = time.time() * 1000
|
||||||
|
|
||||||
|
print("Got %d assertions for %d users" % (len(assertions), len(users)))
|
||||||
|
assert len(assertions) == len(users)
|
||||||
|
|
||||||
for x, y in zip(assertions, creds):
|
for x, y in zip(assertions, creds):
|
||||||
x.verify(client_data.hash, y.public_key)
|
x.verify(client_data.hash, y.public_key)
|
||||||
|
|
||||||
@ -728,7 +820,8 @@ class Tester:
|
|||||||
rp["id"], challenge, pin=PIN
|
rp["id"], challenge, pin=PIN
|
||||||
)
|
)
|
||||||
t2 = time.time() * 1000
|
t2 = time.time() * 1000
|
||||||
assert len(assertions) == len(users) + 1
|
print("Assertions: %d, users: %d" % (len(assertions), len(users)))
|
||||||
|
assert len(assertions) == len(users)
|
||||||
for x, y in zip(assertions, creds):
|
for x, y in zip(assertions, creds):
|
||||||
x.verify(client_data.hash, y.public_key)
|
x.verify(client_data.hash, y.public_key)
|
||||||
|
|
||||||
@ -834,18 +927,35 @@ def test_find_brute_force():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == "sim":
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if "sim" in sys.argv:
|
||||||
print("Using UDP backend.")
|
print("Using UDP backend.")
|
||||||
force_udp_backend()
|
force_udp_backend()
|
||||||
|
|
||||||
t = Tester()
|
t = Tester()
|
||||||
t.find_device()
|
t.find_device()
|
||||||
# t.test_hid()
|
t.set_user_count(15)
|
||||||
# t.test_long_ping()
|
|
||||||
# t.test_fido2()
|
if "u2f" in sys.argv:
|
||||||
t.test_u2f()
|
t.test_u2f()
|
||||||
# t.test_rk()
|
|
||||||
|
if "fido2" in sys.argv:
|
||||||
|
t.test_fido2()
|
||||||
|
t.test_fido2_simple()
|
||||||
|
|
||||||
|
if "rk" in sys.argv:
|
||||||
|
t.test_rk()
|
||||||
|
|
||||||
|
if "ping" in sys.argv:
|
||||||
|
t.test_long_ping()
|
||||||
|
|
||||||
|
# hid tests are a bit invasive and should be done last
|
||||||
|
if "hid" in sys.argv:
|
||||||
|
t.test_hid()
|
||||||
|
|
||||||
# t.test_responses()
|
# t.test_responses()
|
||||||
# test_find_brute_force()
|
# test_find_brute_force()
|
||||||
# t.test_fido2_simple()
|
|
||||||
# t.test_fido2_brute_force()
|
# t.test_fido2_brute_force()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user