Merge branch 'polhenarejos:main' into main
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# Pico FIDO
|
# Pico FIDO
|
||||||
This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
|
This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
|
||||||
|
|
||||||
If you are looking for a Fido + OpenPGP, see: https://github.com/polhenarejos/pico-fido2
|
If you are looking for a OpenPGP + Fido, see: https://github.com/polhenarejos/pico-fido2. Available through [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
Pico FIDO includes the following features:
|
Pico FIDO includes the following features:
|
||||||
@@ -36,12 +36,13 @@ Pico FIDO includes the following features:
|
|||||||
- Challenge-response generation
|
- Challenge-response generation
|
||||||
- Emulated keyboard interface
|
- Emulated keyboard interface
|
||||||
- Button press generates an OTP that is directly typed
|
- Button press generates an OTP that is directly typed
|
||||||
|
- Yubico Authenticator app compatible
|
||||||
- Yubico YKMAN compatible
|
- Yubico YKMAN compatible
|
||||||
- Nitrokey nitropy and nitroapp compatible
|
- Nitrokey nitropy and nitroapp compatible
|
||||||
- Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs
|
- Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs
|
||||||
- One Time Programming to store the master key that encrypts all resident keys and seeds.
|
- One Time Programming to store the master key that encrypts all resident keys and seeds.
|
||||||
- Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable.
|
- Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable.
|
||||||
- LED customization with Pico Commissioner.
|
- LED customization with PicoKey App.
|
||||||
|
|
||||||
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
|
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
|
||||||
|
|
||||||
@@ -55,11 +56,11 @@ Microcontrollers RP2350 and ESP32-S3 are designed to support secure environments
|
|||||||
|
|
||||||
If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-fido/releases/) and download the UF2 file for your board.
|
If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-fido/releases/) and download the UF2 file for your board.
|
||||||
|
|
||||||
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with other proprietary tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner").
|
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
|
||||||
|
|
||||||
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
|
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
|
||||||
|
|
||||||
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") is the most recommended.
|
Note that the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") is the most recommended.
|
||||||
|
|
||||||
## Build for Raspberry Pico
|
## Build for Raspberry Pico
|
||||||
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
VERSION_MAJOR="7"
|
VERSION_MAJOR="7"
|
||||||
VERSION_MINOR="0"
|
VERSION_MINOR="2"
|
||||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||||
|
|||||||
Submodule pico-keys-sdk updated: 7e6e3c8f3c...8a0ef0b30c
@@ -41,6 +41,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len);
|
|||||||
int cbor_config(const uint8_t *data, size_t len);
|
int cbor_config(const uint8_t *data, size_t len);
|
||||||
int cbor_vendor(const uint8_t *data, size_t len);
|
int cbor_vendor(const uint8_t *data, size_t len);
|
||||||
int cbor_large_blobs(const uint8_t *data, size_t len);
|
int cbor_large_blobs(const uint8_t *data, size_t len);
|
||||||
|
extern void reset_gna_state();
|
||||||
|
|
||||||
extern int cmd_read_config();
|
extern int cmd_read_config();
|
||||||
|
|
||||||
@@ -59,6 +60,9 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
if (cap_supported(CAP_FIDO2)) {
|
if (cap_supported(CAP_FIDO2)) {
|
||||||
if (cmd == CTAPHID_CBOR) {
|
if (cmd == CTAPHID_CBOR) {
|
||||||
|
if (data[0] != CTAP_GET_NEXT_ASSERTION) {
|
||||||
|
reset_gna_state();
|
||||||
|
}
|
||||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||||
return cbor_make_credential(data + 1, len - 1);
|
return cbor_make_credential(data + 1, len - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,22 @@ uint32_t timerx = 0;
|
|||||||
uint8_t *datax = NULL;
|
uint8_t *datax = NULL;
|
||||||
size_t lenx = 0;
|
size_t lenx = 0;
|
||||||
|
|
||||||
|
void reset_gna_state() {
|
||||||
|
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||||
|
credential_free(&credsx[i]);
|
||||||
|
}
|
||||||
|
if (datax) {
|
||||||
|
free(datax);
|
||||||
|
datax = NULL;
|
||||||
|
}
|
||||||
|
lenx = 0;
|
||||||
|
residentx = false;
|
||||||
|
timerx = 0;
|
||||||
|
flagsx = 0;
|
||||||
|
credentialCounter = 0;
|
||||||
|
numberOfCredentialsx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||||
(void) data;
|
(void) data;
|
||||||
(void) len;
|
(void) len;
|
||||||
@@ -57,19 +73,7 @@ int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
|||||||
credentialCounter++;
|
credentialCounter++;
|
||||||
err:
|
err:
|
||||||
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
||||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
reset_gna_state();
|
||||||
credential_free(&credsx[i]);
|
|
||||||
}
|
|
||||||
if (datax) {
|
|
||||||
free(datax);
|
|
||||||
datax = NULL;
|
|
||||||
}
|
|
||||||
lenx = 0;
|
|
||||||
residentx = false;
|
|
||||||
timerx = 0;
|
|
||||||
flagsx = 0;
|
|
||||||
credentialCounter = 0;
|
|
||||||
numberOfCredentialsx = 0;
|
|
||||||
if (error == CborErrorImproperValue) {
|
if (error == CborErrorImproperValue) {
|
||||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,24 +613,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
|||||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.id.len > 0 && user.parent.name.len > 0 && user.displayName.len > 0) {
|
|
||||||
if (memcmp(user.parent.name.data, "+pico", 5) == 0) {
|
|
||||||
options.rk = pfalse;
|
|
||||||
#ifndef ENABLE_EMULATION
|
|
||||||
uint8_t *p = (uint8_t *)user.parent.name.data + 5;
|
|
||||||
if (memcmp(p, "CommissionProfile", 17) == 0) {
|
|
||||||
ret = phy_unserialize_data(user.id.data, (uint16_t)user.id.len, &phy_data);
|
|
||||||
if (ret == PICOKEY_OK) {
|
|
||||||
ret = phy_save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ret != PICOKEY_OK) {
|
|
||||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t largeBlobKey[32] = {0};
|
uint8_t largeBlobKey[32] = {0};
|
||||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||||
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
|
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
|||||||
credential_free(&rcred);
|
credential_free(&rcred);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (memcmp(rcred.userId.data, cred.userId.data, MIN(rcred.userId.len, cred.userId.len)) == 0) {
|
if (rcred.userId.len == cred.userId.len && memcmp(rcred.userId.data, cred.userId.data, rcred.userId.len) == 0) {
|
||||||
sloti = i;
|
sloti = i;
|
||||||
credential_free(&rcred);
|
credential_free(&rcred);
|
||||||
new_record = false;
|
new_record = false;
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
#define EF_OATH_CODE 0xBAFF
|
#define EF_OATH_CODE 0xBAFF
|
||||||
#define EF_OTP_SLOT1 0xBB00
|
#define EF_OTP_SLOT1 0xBB00
|
||||||
#define EF_OTP_SLOT2 0xBB01
|
#define EF_OTP_SLOT2 0xBB01
|
||||||
|
#define EF_OTP_SLOT3 0xBB02
|
||||||
|
#define EF_OTP_SLOT4 0xBB03
|
||||||
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
|
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
|
||||||
|
|
||||||
extern file_t *ef_keydev;
|
extern file_t *ef_keydev;
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ void append_keyboard_buffer(const uint8_t *buf, size_t len) {}
|
|||||||
#define CONFIG_LED_INV 0x10
|
#define CONFIG_LED_INV 0x10
|
||||||
#define CONFIG_STATUS_MASK 0x1f
|
#define CONFIG_STATUS_MASK 0x1f
|
||||||
|
|
||||||
|
#define CONFIG3_VALID 0x01
|
||||||
|
#define CONFIG4_VALID 0x02
|
||||||
|
#define CONFIG3_TOUCH 0x04
|
||||||
|
#define CONFIG4_TOUCH 0x08
|
||||||
|
|
||||||
/* EXT Flags */
|
/* EXT Flags */
|
||||||
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
|
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
|
||||||
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
|
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
|
||||||
@@ -161,7 +166,7 @@ extern void scan_all();
|
|||||||
void init_otp() {
|
void init_otp() {
|
||||||
if (scanned == false) {
|
if (scanned == false) {
|
||||||
scan_all();
|
scan_all();
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
|
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
|
||||||
uint8_t *data = file_get_data(ef);
|
uint8_t *data = file_get_data(ef);
|
||||||
otp_config_t *otp_config = (otp_config_t *) data;
|
otp_config_t *otp_config = (otp_config_t *) data;
|
||||||
@@ -207,7 +212,8 @@ int otp_button_pressed(uint8_t slot) {
|
|||||||
if (!cap_supported(CAP_OTP)) {
|
if (!cap_supported(CAP_OTP)) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
uint16_t slot_ef = EF_OTP_SLOT1 + slot - 1;
|
||||||
|
file_t *ef = search_dynamic_file(slot_ef);
|
||||||
const uint8_t *data = file_get_data(ef);
|
const uint8_t *data = file_get_data(ef);
|
||||||
otp_config_t *otp_config = (otp_config_t *) data;
|
otp_config_t *otp_config = (otp_config_t *) data;
|
||||||
if (file_has_data(ef) == false) {
|
if (file_has_data(ef) == false) {
|
||||||
@@ -333,6 +339,39 @@ int otp_unload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t status_byte = 0x0;
|
uint8_t status_byte = 0x0;
|
||||||
|
uint16_t otp_status_ext() {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
|
||||||
|
if (file_has_data(ef)) {
|
||||||
|
res_APDU[res_APDU_size++] = 0xB0 + i;
|
||||||
|
res_APDU[res_APDU_size++] = 0; // Filled later
|
||||||
|
uint8_t *p = res_APDU + res_APDU_size;
|
||||||
|
otp_config_t *otp_config = (otp_config_t *)file_get_data(ef);
|
||||||
|
*p++ = 0xA0;
|
||||||
|
*p++ = 2;
|
||||||
|
*p++ = otp_config->tkt_flags;
|
||||||
|
*p++ = otp_config->cfg_flags;
|
||||||
|
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (otp_config->tkt_flags & OATH_HOTP) {
|
||||||
|
}
|
||||||
|
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*p++ = 0xC0;
|
||||||
|
*p++ = 6;
|
||||||
|
memcpy(p, otp_config->fixed_data, 6);
|
||||||
|
p += 6;
|
||||||
|
}
|
||||||
|
uint8_t len = p - (res_APDU + res_APDU_size);
|
||||||
|
res_APDU[res_APDU_size - 1] = len;
|
||||||
|
res_APDU_size += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SW_OK();
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t otp_status(bool is_otp) {
|
uint16_t otp_status(bool is_otp) {
|
||||||
if (scanned == false) {
|
if (scanned == false) {
|
||||||
scan_all();
|
scan_all();
|
||||||
@@ -384,12 +423,13 @@ bool check_crc(const otp_config_t *data) {
|
|||||||
bool _is_otp = false;
|
bool _is_otp = false;
|
||||||
int cmd_otp() {
|
int cmd_otp() {
|
||||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||||
if (p2 != 0x00) {
|
|
||||||
return SW_INCORRECT_P1P2();
|
|
||||||
}
|
|
||||||
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
||||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||||
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
if (p1 == 0x03 && p2 != 0x0) {
|
||||||
|
return SW_INCORRECT_P1P2();
|
||||||
|
}
|
||||||
|
uint16_t slot = (p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
|
||||||
|
file_t *ef = file_new(slot);
|
||||||
if (file_has_data(ef)) {
|
if (file_has_data(ef)) {
|
||||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||||
@@ -415,10 +455,14 @@ int cmd_otp() {
|
|||||||
}
|
}
|
||||||
else if (p1 == 0x04 || p1 == 0x05) { // Update slot
|
else if (p1 == 0x04 || p1 == 0x05) { // Update slot
|
||||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||||
|
if (p1 == 0x05 && p2 != 0x0) {
|
||||||
|
return SW_INCORRECT_P1P2();
|
||||||
|
}
|
||||||
|
uint16_t slot = (p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
|
||||||
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
||||||
return SW_WRONG_DATA();
|
return SW_WRONG_DATA();
|
||||||
}
|
}
|
||||||
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
file_t *ef = search_dynamic_file(slot);
|
||||||
if (file_has_data(ef)) {
|
if (file_has_data(ef)) {
|
||||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||||
@@ -446,8 +490,16 @@ int cmd_otp() {
|
|||||||
else if (p1 == 0x06) { // Swap slots
|
else if (p1 == 0x06) { // Swap slots
|
||||||
uint8_t tmp[otp_config_size + 8];
|
uint8_t tmp[otp_config_size + 8];
|
||||||
bool ef1_data = false;
|
bool ef1_data = false;
|
||||||
file_t *ef1 = file_new(EF_OTP_SLOT1);
|
uint16_t slot1 = EF_OTP_SLOT1, slot2 = EF_OTP_SLOT2;
|
||||||
file_t *ef2 = file_new(EF_OTP_SLOT2);
|
if (apdu.ne > 0) {
|
||||||
|
if (apdu.ne != 2) {
|
||||||
|
return SW_WRONG_LENGTH();
|
||||||
|
}
|
||||||
|
slot1 += apdu.data[0];
|
||||||
|
slot2 += apdu.data[1];
|
||||||
|
}
|
||||||
|
file_t *ef1 = file_new(slot1);
|
||||||
|
file_t *ef2 = file_new(slot2);
|
||||||
if (file_has_data(ef1)) {
|
if (file_has_data(ef1)) {
|
||||||
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
|
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
|
||||||
ef1_data = true;
|
ef1_data = true;
|
||||||
@@ -458,7 +510,7 @@ int cmd_otp() {
|
|||||||
else {
|
else {
|
||||||
delete_file(ef1);
|
delete_file(ef1);
|
||||||
// When a dynamic file is deleted, existing referenes are invalidated
|
// When a dynamic file is deleted, existing referenes are invalidated
|
||||||
ef2 = file_new(EF_OTP_SLOT2);
|
ef2 = file_new(slot2);
|
||||||
}
|
}
|
||||||
if (ef1_data) {
|
if (ef1_data) {
|
||||||
file_put_data(ef2, tmp, sizeof(tmp));
|
file_put_data(ef2, tmp, sizeof(tmp));
|
||||||
@@ -478,8 +530,15 @@ int cmd_otp() {
|
|||||||
else if (p1 == 0x13) { // Get config
|
else if (p1 == 0x13) { // Get config
|
||||||
man_get_config();
|
man_get_config();
|
||||||
}
|
}
|
||||||
|
else if (p1 == 0x14) {
|
||||||
|
otp_status_ext();
|
||||||
|
}
|
||||||
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) { // Calculate OTP
|
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) { // Calculate OTP
|
||||||
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
if ((p1 == 0x38 || p1 == 0x28) && p2 != 0x0) {
|
||||||
|
return SW_INCORRECT_P1P2();
|
||||||
|
}
|
||||||
|
uint16_t slot = (p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
|
||||||
|
file_t *ef = search_dynamic_file(slot);
|
||||||
if (file_has_data(ef)) {
|
if (file_has_data(ef)) {
|
||||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||||
if (!(otp_config->tkt_flags & CHAL_RESP)) {
|
if (!(otp_config->tkt_flags & CHAL_RESP)) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#ifndef __VERSION_H_
|
#ifndef __VERSION_H_
|
||||||
#define __VERSION_H_
|
#define __VERSION_H_
|
||||||
|
|
||||||
#define PICO_FIDO_VERSION 0x0700
|
#define PICO_FIDO_VERSION 0x0702
|
||||||
|
|
||||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||||
|
|||||||
@@ -202,10 +202,29 @@ def test_rk_with_allowlist_of_different_rp(resetdevice):
|
|||||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||||
|
|
||||||
|
|
||||||
|
def test_same_prefix_userId(device):
|
||||||
|
"""
|
||||||
|
A make credential request with two different UserIds that share the same prefix should NOT overwrite.
|
||||||
|
"""
|
||||||
|
rp = {"id": "sameprefix.org", "name": "Example"}
|
||||||
|
user1 = {"id": b"user_12", "name": "A fixed name", "displayName": "A fixed display name"}
|
||||||
|
user2 = {"id": b"user_123", "name": "A fixed name", "displayName": "A fixed display name"}
|
||||||
|
|
||||||
|
mc_res1 = device.MC(rp = rp, options={"rk":True}, user = user1)
|
||||||
|
|
||||||
|
# Should not overwrite the first credential.
|
||||||
|
mc_res2 = device.MC(rp = rp, options={"rk":True}, user = user2)
|
||||||
|
|
||||||
|
ga_res = device.GA(rp_id=rp['id'])['res']
|
||||||
|
|
||||||
|
assert ga_res.number_of_credentials == 2
|
||||||
|
|
||||||
|
|
||||||
def test_same_userId_overwrites_rk(resetdevice):
|
def test_same_userId_overwrites_rk(resetdevice):
|
||||||
"""
|
"""
|
||||||
A make credential request with a UserId & Rp that is the same as an existing one should overwrite.
|
A make credential request with a UserId & Rp that is the same as an existing one should overwrite.
|
||||||
"""
|
"""
|
||||||
|
resetdevice.reset()
|
||||||
rp = {"id": "overwrite.org", "name": "Example"}
|
rp = {"id": "overwrite.org", "name": "Example"}
|
||||||
user = generate_random_user()
|
user = generate_random_user()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user