mirror of
https://github.com/polhenarejos/pico-fido.git
synced 2024-09-20 03:10:10 +00:00
Compare commits
42 Commits
7e2ecdbc56
...
5b95e35ca9
Author | SHA1 | Date | |
---|---|---|---|
|
5b95e35ca9 | ||
|
69ec242095 | ||
|
6eb6cd35d0 | ||
|
f21e203093 | ||
|
e96da09a84 | ||
|
6fe16a63e4 | ||
|
d5fe405a87 | ||
|
54bbc0e9ea | ||
|
b0b0187919 | ||
|
1f0e1fb8f4 | ||
|
f3f34cf66b | ||
|
82ed96b2e2 | ||
|
92d04f9131 | ||
|
7a71bf48fc | ||
|
20a8ef08f0 | ||
|
e757ad2945 | ||
|
1ce0d98c34 | ||
|
96de6efed6 | ||
|
195096ad52 | ||
|
1ee86f8634 | ||
|
ffb3beb84a | ||
|
d78d9d10aa | ||
|
f8d4f1d02e | ||
|
b493a81ddc | ||
|
5c20909b03 | ||
|
27b9e3954a | ||
|
440ec5c854 | ||
|
cb2744cab3 | ||
|
5db1014850 | ||
|
421bea6421 | ||
|
65039c0959 | ||
|
8e36b4c379 | ||
|
3652368542 | ||
|
e5d1ef29a4 | ||
|
0fd36806cc | ||
|
7bf26b28fc | ||
|
da94a82487 | ||
|
c24be5a631 | ||
|
46ce9390bf | ||
|
c1fd5736f9 | ||
|
b1c4ff877e | ||
|
6c85d57412 |
75
README.md
75
README.md
@ -1,8 +1,8 @@
|
||||
# Pico FIDO
|
||||
This project aims at transforming your Raspberry Pico into a FIDO key integrated. The Pico works as a FIDO key, like a normal USB key for authentication.
|
||||
This project transforms your Raspberry Pi Pico into an integrated FIDO key, functioning like a standard USB key for authentication.
|
||||
|
||||
## Features
|
||||
Pico FIDO has implemented the following features:
|
||||
Pico FIDO includes the following features:
|
||||
|
||||
- CTAP 2.1 / CTAP 1
|
||||
- WebAuthn
|
||||
@ -10,16 +10,16 @@ Pico FIDO has implemented the following features:
|
||||
- HMAC-Secret extension
|
||||
- CredProtect extension
|
||||
- User presence enforcement through physical button
|
||||
- User Verification with PIN
|
||||
- User verification with PIN
|
||||
- Discoverable credentials
|
||||
- Credential management
|
||||
- ECDSA authentication
|
||||
- Authentication with SECP256R1, SECP384R1, SECP521R1 and SECP256K1 curves.
|
||||
- Support for SECP256R1, SECP384R1, SECP521R1, and SECP256K1 curves
|
||||
- App registration and login
|
||||
- Device selection
|
||||
- Support for vendor Config
|
||||
- Support for vendor configuration
|
||||
- Backup with 24 words
|
||||
- Secure lock to protect the device from flash dumpings
|
||||
- Secure lock to protect the device from flash dumps
|
||||
- Permissions support (MC, GA, CM, ACFG, LBW)
|
||||
- Authenticator configuration
|
||||
- minPinLength extension
|
||||
@ -27,50 +27,54 @@ Pico FIDO has implemented the following features:
|
||||
- Enterprise attestation
|
||||
- credBlobs extension
|
||||
- largeBlobKey extension
|
||||
- largeBlobs support (2048 bytes máx.)
|
||||
- Large blobs support (2048 bytes max)
|
||||
- OATH (based on YKOATH protocol specification)
|
||||
- TOTP / HOTP
|
||||
- Yubikey OTP
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is written directly is it was typed
|
||||
- Button press generates an OTP that is directly typed
|
||||
- Yubico YKMAN compatible
|
||||
- Nitrokey nitropy and nitroapp compatible
|
||||
|
||||
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
|
||||
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
|
||||
|
||||
## Security considerations
|
||||
Pico FIDO is an open platform so be careful. The contents in the flash memory may be easily dumpled and obtain the private/master keys. Therefore, it is not possible to encrypt the content. At least, one key (the master, the supreme key) must be stored in clear text.
|
||||
## Security Considerations
|
||||
|
||||
If the Pico is stolen the contents of private and secret keys can be read.
|
||||
Pico FIDO is an open platform, so exercise caution. The flash memory contents can be easily dumped, potentially revealing private/master keys. It is not feasible to encrypt the content, meaning at least one key (the master key) must be stored in clear text.
|
||||
|
||||
If the Pico is stolen, the private and secret keys can be accessed.
|
||||
|
||||
## Download
|
||||
Please, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and download the UF2 file for your board.
|
||||
Please visit the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") to 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 are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
|
||||
Note that UF2 files are shipped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar software, you will need to modify the Info.plist of the CCID driver to add these VID/PID values or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
|
||||
|
||||
Alternatively you can use the legacy VID/PID patcher as follows:
|
||||
`./patch_vidpid.sh VID:PID input_hsm_file.uf2 output_hsm_file.uf2`
|
||||
Alternatively, you can use the legacy VID/PID patcher with the following command:
|
||||
```sh
|
||||
./patch_vidpid.sh VID:PID input_hsm_file.uf2 output_hsm_file.uf2
|
||||
```
|
||||
You can use any VID/PID (e.g., 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 Patcher tool](https://www.picokeys.com/pico-patcher/) is the most recommended.
|
||||
For ease of use, the pure-browser option [Pico Patcher tool](https://www.picokeys.com/pico-patcher/) is highly recommended.
|
||||
|
||||
## Build
|
||||
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
|
||||
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
||||
|
||||
git clone https://github.com/polhenarejos/pico-fido
|
||||
cd pico-fido
|
||||
mkdir build
|
||||
cd build
|
||||
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
|
||||
make
|
||||
```sh
|
||||
git clone https://github.com/polhenarejos/pico-fido
|
||||
cd pico-fido
|
||||
mkdir build
|
||||
cd build
|
||||
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
|
||||
make
|
||||
```
|
||||
|
||||
Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico board and VID/PID FEFF:FCFD will be used.
|
||||
Note that `PICO_BOARD`, `USB_VID`, and `USB_PID` are optional. If not provided, the default Pico board and VID/PID `FEFF:FCFD` will be used.
|
||||
|
||||
After make ends, the binary file pico_fido.uf2 will be generated. Put your pico board into loading mode, by pushing BOOTSEL button while pluging on, and copy the UF2 to the new fresh usb mass storage Pico device. Once copied, the pico mass storage will be disconnected automatically and the pico board will reset with the new firmware. A blinking led will indicate the device is ready to work.
|
||||
After `make` finishes, the binary file `pico_fido.uf2` will be generated. Put your Pico board into loading mode by holding the BOOTSEL button while plugging it in, then copy the UF2 file to the new USB mass storage Pico device. Once copied, the Pico mass storage will disconnect automatically, and the Pico board will reset with the new firmware. A blinking LED will indicate that the device is ready to work.
|
||||
|
||||
**Remark:** Pico Fido uses HID interface and thus, VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary value or the default ones.
|
||||
**Remark:** Pico FIDO uses the HID interface, so VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary values or the default ones.
|
||||
|
||||
## Led blink
|
||||
Pico FIDO uses the led to indicate the current status. Four states are available:
|
||||
@ -96,20 +100,21 @@ While processing, the Pico FIDO is busy and cannot receive additional commands u
|
||||
|
||||
## Driver
|
||||
|
||||
Pico FIDO uses the `HID` driver, present in all OS. It should be detected by all OS and browser/applications, like normal USB FIDO keys.
|
||||
Pico FIDO uses the `HID` driver, which is present in all operating systems. It should be detected by all OS and browser/applications just like normal USB FIDO keys.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests can be found at `tests` folder. It is based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys, but adapted to [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from previous 0.8 version and includes latests improvements from CTAP 2.1.
|
||||
Tests can be found in the `tests` folder. They are based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys but adapted to the [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from the previous 0.8 version and includes the latest improvements from CTAP 2.1.
|
||||
|
||||
All tests can be run by
|
||||
To run all tests, use:
|
||||
|
||||
```
|
||||
```sh
|
||||
pytest
|
||||
```
|
||||
|
||||
or by selecting a subset with `-k <test>` flag:
|
||||
```
|
||||
To run a subset of tests, use the `-k <test>` flag:
|
||||
|
||||
```sh
|
||||
pytest -k test_credprotect
|
||||
```
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="5"
|
||||
VERSION_MINOR="8"
|
||||
VERSION_MINOR="10"
|
||||
|
||||
rm -rf release/*
|
||||
cd build_release
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit f0687c1ef392c2bcb293ea554f1dd8b784484922
|
||||
Subproject commit f4ad8e1af2e2657f3900f1e01db031d7d73d623b
|
@ -169,7 +169,7 @@ int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
|
||||
int resetPinUvAuthToken() {
|
||||
uint8_t t[32];
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
file_put_data(ef_authtoken, t, sizeof(t));
|
||||
paut.permissions = 0;
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
@ -417,7 +417,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2 + 16);
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
low_flash_available();
|
||||
goto err; //No return
|
||||
}
|
||||
@ -464,7 +464,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64];
|
||||
@ -489,7 +489,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
new_pin_mismatches = 0;
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
@ -520,12 +520,12 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 16) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
flash_write_data_to_file(ef_pin, hsh, 2 + 16);
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmp, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
tmp[1] = 0;
|
||||
flash_write_data_to_file(ef_minpin, tmp, file_get_size(ef_minpin));
|
||||
file_put_data(ef_minpin, tmp, file_get_size(ef_minpin));
|
||||
free(tmp);
|
||||
}
|
||||
low_flash_available();
|
||||
@ -573,7 +573,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol - 1) * IV_SIZE;
|
||||
@ -599,7 +599,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
}
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
|
@ -142,9 +142,9 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
if (has_keydev_dec == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flash_write_data_to_file(ef_keydev, keydev_dec, sizeof(keydev_dec));
|
||||
file_put_data(ef_keydev, keydev_dec, sizeof(keydev_dec));
|
||||
mbedtls_platform_zeroize(keydev_dec, sizeof(keydev_dec));
|
||||
flash_write_data_to_file(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
|
||||
@ -178,10 +178,10 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
flash_write_data_to_file(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
|
||||
file_put_data(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
|
||||
mbedtls_platform_zeroize(key_dev_enc, sizeof(key_dev_enc));
|
||||
flash_write_data_to_file(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
else {
|
||||
@ -216,7 +216,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
data + 2 + m * 32,
|
||||
0);
|
||||
}
|
||||
flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
|
||||
file_put_data(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
|
||||
low_flash_available();
|
||||
goto err; //No return
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
delete_file(rp_ef);
|
||||
}
|
||||
else {
|
||||
flash_write_data_to_file(rp_ef, rp_data, file_get_size(rp_ef));
|
||||
file_put_data(rp_ef, rp_data, file_get_size(rp_ef));
|
||||
}
|
||||
free(rp_data);
|
||||
break;
|
||||
|
@ -640,7 +640,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
|
@ -155,7 +155,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
if (expectedLength > 17 && memcmp(sha, temp_lba + expectedLength - 16, 16) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
flash_write_data_to_file(ef_largeblob, temp_lba, expectedLength);
|
||||
file_put_data(ef_largeblob, temp_lba, expectedLength);
|
||||
low_flash_available();
|
||||
}
|
||||
goto err;
|
||||
|
@ -225,9 +225,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
else if (pubKeyCredParams[i].alg <= FIDO2_ALG_RS256 && pubKeyCredParams[i].alg >= FIDO2_ALG_RS512) {
|
||||
// pass
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
}
|
||||
//else {
|
||||
// CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
//}
|
||||
if (curve > 0 && alg == 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
}
|
||||
@ -313,9 +313,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
if (options.up == ptrue) {
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
}
|
||||
}
|
||||
|
||||
const known_app_t *ka = find_app_by_rp_id_hash(rp_id_hash);
|
||||
@ -518,7 +520,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
|
@ -121,9 +121,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
uint8_t zeros[32];
|
||||
memset(zeros, 0, sizeof(zeros));
|
||||
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
|
||||
flash_write_data_to_file(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev_enc, vendorParam.data, vendorParam.len);
|
||||
file_put_data(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
goto err;
|
||||
}
|
||||
@ -306,7 +306,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
|
||||
if (ef_ee_ea) {
|
||||
flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len);
|
||||
file_put_data(ef_ee_ea, vendorParam.data, vendorParam.len);
|
||||
}
|
||||
low_flash_available();
|
||||
goto err;
|
||||
|
@ -97,7 +97,7 @@ int cmd_authenticate() {
|
||||
res_APDU_size = 1 + 4 + olen;
|
||||
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
@ -306,7 +306,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
memcpy(data, rp_id_hash, 32);
|
||||
memcpy(data + 32, cred_id, cred_id_len);
|
||||
file_t *ef = file_new(EF_CRED + sloti);
|
||||
flash_write_data_to_file(ef, data, cred_id_len + 32);
|
||||
file_put_data(ef, data, cred_id_len + 32);
|
||||
free(data);
|
||||
|
||||
if (new_record == true) { //increase rps
|
||||
@ -332,7 +332,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
data = (uint8_t *) calloc(1, file_get_size(ef));
|
||||
memcpy(data, file_get_data(ef), file_get_size(ef));
|
||||
data[0] += 1;
|
||||
flash_write_data_to_file(ef, data, file_get_size(ef));
|
||||
file_put_data(ef, data, file_get_size(ef));
|
||||
free(data);
|
||||
}
|
||||
else {
|
||||
@ -341,7 +341,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
data[0] = 1;
|
||||
memcpy(data + 1, rp_id_hash, 32);
|
||||
memcpy(data + 1 + 32, cred.rpId.data, cred.rpId.len);
|
||||
flash_write_data_to_file(ef, data, 1 + 32 + cred.rpId.len);
|
||||
file_put_data(ef, data, 1 + 32 + cred.rpId.len);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,8 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
|
||||
}
|
||||
}
|
||||
uint8_t hmac[32], d[32];
|
||||
int ret = mbedtls_ecp_write_key(key, d, sizeof(d));
|
||||
size_t olen = 0;
|
||||
int ret = mbedtls_ecp_write_key_ext(key, &olen, d, sizeof(d));
|
||||
if (key == NULL) {
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
}
|
||||
@ -317,7 +318,7 @@ int scan_files() {
|
||||
uint8_t kdata[32];
|
||||
int key_size = mbedtls_mpi_size(&ecdsa.d);
|
||||
mbedtls_mpi_write_binary(&ecdsa.d, kdata, key_size);
|
||||
ret = flash_write_data_to_file(ef_keydev, kdata, key_size);
|
||||
ret = file_put_data(ef_keydev, kdata, key_size);
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
if (ret != CCID_OK) {
|
||||
@ -353,7 +354,7 @@ int scan_files() {
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
|
||||
file_put_data(ef_certdev, cert + sizeof(cert) - ret, ret);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -363,7 +364,7 @@ int scan_files() {
|
||||
if (ef_counter) {
|
||||
if (!file_has_data(ef_counter)) {
|
||||
uint32_t v = 0;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &v, sizeof(v));
|
||||
file_put_data(ef_counter, (uint8_t *) &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -375,7 +376,7 @@ int scan_files() {
|
||||
if (!file_has_data(ef_authtoken)) {
|
||||
uint8_t t[32];
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
file_put_data(ef_authtoken, t, sizeof(t));
|
||||
}
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
@ -385,7 +386,7 @@ int scan_files() {
|
||||
}
|
||||
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_largeblob)) {
|
||||
flash_write_data_to_file(ef_largeblob,
|
||||
file_put_data(ef_largeblob,
|
||||
(const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c",
|
||||
17);
|
||||
}
|
||||
@ -447,7 +448,7 @@ uint8_t get_opts() {
|
||||
|
||||
void set_opts(uint8_t opts) {
|
||||
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
|
||||
flash_write_data_to_file(ef, &opts, sizeof(uint8_t));
|
||||
file_put_data(ef, &opts, sizeof(uint8_t));
|
||||
low_flash_available();
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ file_t file_entries[] = {
|
||||
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL,
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
|
@ -54,10 +54,12 @@ int man_unload() {
|
||||
bool cap_supported(uint16_t cap) {
|
||||
file_t *ef = search_dynamic_file(EF_DEV_CONF);
|
||||
if (file_has_data(ef)) {
|
||||
uint16_t tag = 0x0, data_len = file_get_size(ef);
|
||||
uint8_t *tag_data = NULL, *p = NULL, *data = file_get_data(ef);
|
||||
size_t tag_len = 0;
|
||||
while (walk_tlv(data, data_len, &p, &tag, &tag_len, &tag_data)) {
|
||||
uint16_t tag = 0x0;
|
||||
uint8_t *tag_data = NULL, *p = NULL;
|
||||
uint16_t tag_len = 0;
|
||||
asn1_ctx_t ctxi;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
while (walk_tlv(&ctxi, &p, &tag, &tag_len, &tag_data)) {
|
||||
if (tag == TAG_USB_ENABLED) {
|
||||
uint16_t ecaps = tag_data[0];
|
||||
if (tag_len == 2) {
|
||||
@ -128,7 +130,7 @@ int cmd_write_config() {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = file_new(EF_DEV_CONF);
|
||||
flash_write_data_to_file(ef, apdu.data + 1, apdu.nc - 1);
|
||||
file_put_data(ef, apdu.data + 1, apdu.nc - 1);
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
250
src/fido/oath.c
250
src/fido/oath.c
@ -118,14 +118,11 @@ int oath_unload() {
|
||||
}
|
||||
|
||||
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
|
||||
size_t ef_tag_len = 0;
|
||||
uint8_t *ef_tag_data = NULL;
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef) &&
|
||||
asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_NAME, &ef_tag_len,
|
||||
&ef_tag_data) == true && ef_tag_len == name_len &&
|
||||
memcmp(ef_tag_data, name, name_len) == 0) {
|
||||
asn1_ctx_t ctxi, ef_tag = { 0 };
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
if (file_has_data(ef) && asn1_find_tag(&ctxi, TAG_NAME, &ef_tag) == true && ef_tag.len == name_len && memcmp(ef_tag.data, name, name_len) == 0) {
|
||||
return ef;
|
||||
}
|
||||
}
|
||||
@ -136,32 +133,32 @@ int cmd_put() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
size_t key_len = 0, imf_len = 0, name_len = 0;
|
||||
uint8_t *key = NULL, *imf = NULL, *name = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, name = { 0 }, imf = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_IMF, &imf_len, &imf) == false) {
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(&ctxi, TAG_IMF, &imf) == false) {
|
||||
memcpy(apdu.data + apdu.nc, "\x7a\x08\x00\x00\x00\x00\x00\x00\x00\x00", 10);
|
||||
apdu.nc += 10;
|
||||
}
|
||||
else { //prepend zero-valued bytes
|
||||
if (imf_len < 8) {
|
||||
memmove(imf + (8 - imf_len), imf, imf_len);
|
||||
memset(imf, 0, 8 - imf_len);
|
||||
*(imf - 1) = 8;
|
||||
apdu.nc += (8 - imf_len);
|
||||
if (imf.len < 8) {
|
||||
memmove(imf.data + (8 - imf.len), imf.data, imf.len);
|
||||
memset(imf.data, 0, 8 - imf.len);
|
||||
*(imf.data - 1) = 8;
|
||||
apdu.nc += (8 - imf.len);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef)) {
|
||||
flash_write_data_to_file(ef, apdu.data, apdu.nc);
|
||||
file_put_data(ef, apdu.data, apdu.nc);
|
||||
low_flash_available();
|
||||
}
|
||||
else {
|
||||
@ -169,7 +166,7 @@ int cmd_put() {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (!file_has_data(ef)) {
|
||||
ef = file_new(EF_OATH_CRED + i);
|
||||
flash_write_data_to_file(ef, apdu.data, apdu.nc);
|
||||
file_put_data(ef, apdu.data, apdu.nc);
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
@ -181,13 +178,13 @@ int cmd_put() {
|
||||
|
||||
|
||||
int cmd_delete() {
|
||||
size_t tag_len = 0;
|
||||
uint8_t *tag_data = NULL;
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &tag_len, &tag_data) == true) {
|
||||
file_t *ef = find_oath_cred(tag_data, tag_len);
|
||||
asn1_ctx_t ctxi, ctxo = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &ctxo) == true) {
|
||||
file_t *ef = find_oath_cred(ctxo.data, ctxo.len);
|
||||
if (ef) {
|
||||
delete_file(ef);
|
||||
return SW_OK();
|
||||
@ -219,38 +216,38 @@ int cmd_set_code() {
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
size_t key_len = 0, chal_len = 0, resp_len = 0;
|
||||
uint8_t *key = NULL, *chal = NULL, *resp = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (key_len == 0) {
|
||||
if (key.len == 0) {
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int r = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
|
||||
int r = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
|
||||
if (r != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp, resp_len) != 0) {
|
||||
if (memcmp(hmac, resp.data, resp.len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
random_gen(NULL, challenge, sizeof(challenge));
|
||||
file_t *ef = file_new(EF_OATH_CODE);
|
||||
flash_write_data_to_file(ef, key, key_len);
|
||||
file_put_data(ef, key.data, key.len);
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_OK();
|
||||
@ -274,23 +271,19 @@ int cmd_reset() {
|
||||
}
|
||||
|
||||
int cmd_list() {
|
||||
size_t name_len = 0, key_len = 0;
|
||||
uint8_t *name = NULL, *key = NULL;
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef)) {
|
||||
uint8_t *data = file_get_data(ef);
|
||||
size_t data_len = file_get_size(ef);
|
||||
if (asn1_find_tag(data, data_len, TAG_NAME, &name_len,
|
||||
&name) == true &&
|
||||
asn1_find_tag(data, data_len, TAG_KEY, &key_len, &key) == true) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, name = { 0 };
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == true && asn1_find_tag(&ctxi, TAG_KEY, &key) == true) {
|
||||
res_APDU[res_APDU_size++] = TAG_NAME_LIST;
|
||||
res_APDU[res_APDU_size++] = name_len + 1;
|
||||
res_APDU[res_APDU_size++] = key[0];
|
||||
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
|
||||
res_APDU[res_APDU_size++] = name.len + 1;
|
||||
res_APDU[res_APDU_size++] = key.data[0];
|
||||
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,12 +292,12 @@ int cmd_list() {
|
||||
}
|
||||
|
||||
int cmd_validate() {
|
||||
size_t chal_len = 0, resp_len = 0, key_len = 0;
|
||||
uint8_t *chal = NULL, *resp = NULL, *key = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CODE);
|
||||
@ -312,21 +305,21 @@ int cmd_validate() {
|
||||
validated = true;
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
key = file_get_data(ef);
|
||||
key_len = file_get_size(ef);
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
key.data = file_get_data(ef);
|
||||
key.len = file_get_size(ef);
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, challenge, sizeof(challenge), hmac);
|
||||
int ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, challenge, sizeof(challenge), hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp, resp_len) != 0) {
|
||||
if (memcmp(hmac, resp.data, resp.len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
|
||||
ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
@ -373,68 +366,70 @@ int calculate_oath(uint8_t truncate,
|
||||
}
|
||||
|
||||
int cmd_calculate() {
|
||||
size_t chal_len = 0, name_len = 0, key_len = 0;
|
||||
uint8_t *chal = NULL, *name = NULL, *key = NULL;
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
|
||||
&chal) == false) {
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
}
|
||||
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
|
||||
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
|
||||
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
|
||||
if (ret != CCID_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
uint64_t v =
|
||||
((uint64_t) chal[0] <<
|
||||
((uint64_t) chal.data[0] <<
|
||||
56) |
|
||||
((uint64_t) chal[1] <<
|
||||
((uint64_t) chal.data[1] <<
|
||||
48) |
|
||||
((uint64_t) chal[2] <<
|
||||
((uint64_t) chal.data[2] <<
|
||||
40) |
|
||||
((uint64_t) chal[3] <<
|
||||
((uint64_t) chal.data[3] <<
|
||||
32) |
|
||||
((uint64_t) chal[4] <<
|
||||
24) | ((uint64_t) chal[5] << 16) | ((uint64_t) chal[6] << 8) | (uint64_t) chal[7];
|
||||
((uint64_t) chal.data[4] <<
|
||||
24) | ((uint64_t) chal.data[5] << 16) | ((uint64_t) chal.data[6] << 8) | (uint64_t) chal.data[7];
|
||||
size_t ef_size = file_get_size(ef);
|
||||
v++;
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
|
||||
memcpy(tmp, file_get_data(ef), ef_size);
|
||||
asn1_find_tag(tmp, ef_size, TAG_IMF, &chal_len, &chal);
|
||||
chal[0] = v >> 56;
|
||||
chal[1] = v >> 48;
|
||||
chal[2] = v >> 40;
|
||||
chal[3] = v >> 32;
|
||||
chal[4] = v >> 24;
|
||||
chal[5] = v >> 16;
|
||||
chal[6] = v >> 8;
|
||||
chal[7] = v & 0xff;
|
||||
flash_write_data_to_file(ef, tmp, ef_size);
|
||||
asn1_ctx_t ctxt;
|
||||
asn1_ctx_init(tmp, ef_size, &ctxt);
|
||||
asn1_find_tag(&ctxt, TAG_IMF, &chal);
|
||||
chal.data[0] = v >> 56;
|
||||
chal.data[1] = v >> 48;
|
||||
chal.data[2] = v >> 40;
|
||||
chal.data[3] = v >> 32;
|
||||
chal.data[4] = v >> 24;
|
||||
chal.data[5] = v >> 16;
|
||||
chal.data[6] = v >> 8;
|
||||
chal.data[7] = v & 0xff;
|
||||
file_put_data(ef, tmp, ef_size);
|
||||
low_flash_available();
|
||||
free(tmp);
|
||||
}
|
||||
@ -443,15 +438,15 @@ int cmd_calculate() {
|
||||
}
|
||||
|
||||
int cmd_calculate_all() {
|
||||
size_t chal_len = 0, name_len = 0, key_len = 0, prop_len = 0;
|
||||
uint8_t *chal = NULL, *name = NULL, *key = NULL, *prop = NULL;
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, prop = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
@ -460,31 +455,30 @@ int cmd_calculate_all() {
|
||||
if (file_has_data(ef)) {
|
||||
const uint8_t *ef_data = file_get_data(ef);
|
||||
size_t ef_len = file_get_size(ef);
|
||||
if (asn1_find_tag(ef_data, ef_len, TAG_NAME, &name_len,
|
||||
&name) == false ||
|
||||
asn1_find_tag(ef_data, ef_len, TAG_KEY, &key_len, &key) == false) {
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init((uint8_t *)ef_data, ef_len, &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_NAME, &name) == false || asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
continue;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_NAME;
|
||||
res_APDU[res_APDU_size++] = name_len;
|
||||
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
res_APDU[res_APDU_size++] = name.len;
|
||||
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
res_APDU[res_APDU_size++] = TAG_NO_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
else if (asn1_find_tag(ef_data, ef_len, TAG_PROPERTY, &prop_len,
|
||||
&prop) == true && (prop[0] & PROP_TOUCH)) {
|
||||
else if (asn1_find_tag(&ctxe, TAG_PROPERTY, &prop) == true && (prop.data[0] & PROP_TOUCH)) {
|
||||
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
|
||||
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
|
||||
if (ret != CCID_OK) {
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -498,101 +492,105 @@ int cmd_send_remaining() {
|
||||
}
|
||||
|
||||
int cmd_set_otp_pin() {
|
||||
size_t pw_len = 0;
|
||||
uint8_t *pw = NULL, hsh[33] = { 0 };
|
||||
uint8_t hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
asn1_ctx_t ctxi, pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_change_otp_pin() {
|
||||
size_t pw_len = 0, new_pw_len = 0;
|
||||
uint8_t *pw = NULL, *new_pw = NULL, hsh[33] = { 0 };
|
||||
uint8_t hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
asn1_ctx_t ctxi, pw = { 0 }, new_pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NEW_PASSWORD, &new_pw_len, &new_pw) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_NEW_PASSWORD, &new_pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(new_pw, new_pw_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
|
||||
double_hash_pin(new_pw.data, new_pw.len, hsh + 1);
|
||||
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_otp_pin() {
|
||||
size_t pw_len = 0;
|
||||
uint8_t *pw = NULL, hsh[33] = { 0 }, data_hsh[33];
|
||||
uint8_t hsh[33] = { 0 }, data_hsh[33];
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
|
||||
asn1_ctx_t ctxi, pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw, pw_len, hsh + 1);
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
memcpy(data_hsh, file_get_data(ef_otp_pin), sizeof(data_hsh));
|
||||
if (data_hsh[0] == 0 || memcmp(data_hsh + 1, hsh + 1, 32) != 0) {
|
||||
if (data_hsh[0] > 0) {
|
||||
data_hsh[0] -= 1;
|
||||
}
|
||||
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
data_hsh[0] = MAX_OTP_COUNTER;
|
||||
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_hotp() {
|
||||
size_t key_len = 0, chal_len = 0, name_len = 0, code_len = 0;
|
||||
uint8_t *key = NULL, *chal = NULL, *name = NULL, *code = NULL;
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, code = { 0 };
|
||||
asn1_ctx_init(apdu.data, apdu.nc, &ctxi);
|
||||
uint32_t code_int = 0;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
|
||||
if ((key.data[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
|
||||
&chal) == false) {
|
||||
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &code_len, &code) == true) {
|
||||
code_int = (code[0] << 24) | (code[1] << 16) | (code[2] << 8) | code[3];
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
|
||||
code_int = (code.data[0] << 24) | (code.data[1] << 16) | (code.data[2] << 8) | code.data[3];
|
||||
}
|
||||
|
||||
int ret = calculate_oath(0x01, key, key_len, chal, chal_len);
|
||||
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
|
||||
if (ret != CCID_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "usb.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
@ -169,7 +170,7 @@ void init_otp() {
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
flash_write_data_to_file(ef, new_data, sizeof(new_data));
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,7 +259,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
|
||||
memcpy(new_otp_config, otp_config, otp_config_size);
|
||||
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
|
||||
flash_write_data_to_file(ef, new_otp_config, sizeof(new_otp_config));
|
||||
file_put_data(ef, new_otp_config, sizeof(new_otp_config));
|
||||
low_flash_available();
|
||||
}
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
@ -322,7 +323,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
flash_write_data_to_file(ef, new_data, sizeof(new_data));
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
@ -387,7 +388,7 @@ int cmd_otp() {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
|
||||
flash_write_data_to_file(ef, apdu.data, otp_config_size + 8);
|
||||
file_put_data(ef, apdu.data, otp_config_size + 8);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status();
|
||||
@ -420,7 +421,7 @@ int cmd_otp() {
|
||||
(odata->tkt_flags & TKTFLAG_UPDATE_MASK);
|
||||
odata->cfg_flags = (otpc->cfg_flags & ~CFGFLAG_UPDATE_MASK) |
|
||||
(odata->cfg_flags & CFGFLAG_UPDATE_MASK);
|
||||
flash_write_data_to_file(ef, apdu.data, otp_config_size);
|
||||
file_put_data(ef, apdu.data, otp_config_size);
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
@ -434,13 +435,13 @@ int cmd_otp() {
|
||||
ef1_data = true;
|
||||
}
|
||||
if (file_has_data(ef2)) {
|
||||
flash_write_data_to_file(ef1, file_get_data(ef2), file_get_size(ef2));
|
||||
file_put_data(ef1, file_get_data(ef2), file_get_size(ef2));
|
||||
}
|
||||
else {
|
||||
delete_file(ef1);
|
||||
}
|
||||
if (ef1_data) {
|
||||
flash_write_data_to_file(ef2, tmp, sizeof(tmp));
|
||||
file_put_data(ef2, tmp, sizeof(tmp));
|
||||
}
|
||||
else {
|
||||
delete_file(ef2);
|
||||
|
@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0508
|
||||
#define PICO_FIDO_VERSION 0x050A
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
@ -147,11 +147,13 @@ def test_bad_type_pubKeyCredParams_alg(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": "7", "type": "public-key"}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
|
||||
|
||||
def test_unsupported_algorithm(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
|
||||
assert e.value.code == CtapError.ERR.UNSUPPORTED_ALGORITHM
|
||||
|
||||
def test_exclude_list(resetdevice):
|
||||
resetdevice.doMC(exclude_list=[{"id": b"1234", "type": "rot13"}])
|
||||
|
Loading…
Reference in New Issue
Block a user