Compare commits
49 Commits
v7.0-eddsa
...
nightly-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb20a75ef4 | ||
|
|
8d709cf745 | ||
|
|
bbbbcadf4c | ||
|
|
fbbf1feb49 | ||
|
|
22de41bfe0 | ||
|
|
31a6315721 | ||
|
|
9fb8d475b3 | ||
|
|
c0298ece7d | ||
|
|
bea7706d63 | ||
|
|
cfd22c2d2c | ||
|
|
8ea118fe91 | ||
|
|
8e6f571b48 | ||
|
|
3c20800839 | ||
|
|
f2eef5b839 | ||
|
|
bc6ebdd069 | ||
|
|
3f890757ac | ||
|
|
18d68d7e05 | ||
|
|
c8d62de621 | ||
|
|
60165c21ca | ||
|
|
55a60f8875 | ||
|
|
7ed90007ef | ||
|
|
c23cc9ffe1 | ||
|
|
dc4565a8fb | ||
|
|
804ee68e86 | ||
|
|
fe49149d86 | ||
|
|
81d97f1a18 | ||
|
|
d16016cf1e | ||
|
|
2e0333677b | ||
|
|
becdc94339 | ||
|
|
bd499ae1d4 | ||
|
|
5fc84d7097 | ||
|
|
ac7e34522a | ||
|
|
70dec5596a | ||
|
|
2331dcb3ec | ||
|
|
42b8b0af5f | ||
|
|
ceb54d9a08 | ||
|
|
527943fbba | ||
|
|
7dddfd971e | ||
|
|
29f942dab9 | ||
|
|
aa9df892d3 | ||
|
|
7ac2ce30f0 | ||
|
|
e86862033c | ||
|
|
ae36143498 | ||
|
|
d90dbb6c5f | ||
|
|
1d21d93b74 | ||
|
|
8b9be258de | ||
|
|
46720fb387 | ||
|
|
1867f0330f | ||
|
|
bb542e3b83 |
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Delete private key
|
||||
run: rm private.pem
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
uses: pyTooling/Actions/releaser@v6.7.0
|
||||
with:
|
||||
tag: nightly-${{ matrix.refs }}
|
||||
rm: true
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(USB_VID 0x2E8A)
|
||||
set(USB_PID 0x10FE)
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
set(DENABLE_POWER_ON_RESET 0)
|
||||
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src)
|
||||
set(EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/fido)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
else()
|
||||
|
||||
@@ -112,7 +115,7 @@ set(SOURCES ${SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 2)
|
||||
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 3)
|
||||
if(ESP_PLATFORM)
|
||||
project(pico_fido)
|
||||
endif()
|
||||
@@ -161,7 +164,7 @@ if(ENABLE_EMULATION)
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
endif (APPLE)
|
||||
target_link_libraries(pico_fido PRIVATE pthread m)
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk mbedtls pthread m)
|
||||
else()
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board)
|
||||
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
|
||||
|
||||
11
README.md
11
README.md
@@ -1,7 +1,7 @@
|
||||
# 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.
|
||||
|
||||
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
|
||||
Pico FIDO includes the following features:
|
||||
@@ -36,12 +36,13 @@ Pico FIDO includes the following features:
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is directly typed
|
||||
- Yubico Authenticator app compatible
|
||||
- Yubico YKMAN compatible
|
||||
- Nitrokey nitropy and nitroapp compatible
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
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").
|
||||
UF2 files are shiped with a VID/PID granted by RaspberryPi (2E8A:10FE). 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 for internal purposes, 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
|
||||
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
||||
|
||||
@@ -1,48 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="7"
|
||||
VERSION_MINOR="0"
|
||||
NO_EDDSA=0
|
||||
VERSION_MINOR="4"
|
||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||
#fi
|
||||
|
||||
if [[ $1 == "--no-eddsa" ]]; then
|
||||
NO_EDDSA=1
|
||||
echo "Skipping EDDSA build"
|
||||
fi
|
||||
|
||||
mkdir -p build_release
|
||||
mkdir -p release
|
||||
mkdir -p release_eddsa
|
||||
rm -rf -- release/*
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
rm -rf -- release_eddsa/*
|
||||
fi
|
||||
cd build_release
|
||||
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
|
||||
SECURE_BOOT_PKEY="${SECURE_BOOT_PKEY:-../../ec_private_key.pem}"
|
||||
board_dir=${PICO_SDK_PATH}/src/boards/include/boards
|
||||
for board in "$board_dir"/*
|
||||
boards=("pico" "pico2")
|
||||
|
||||
for board_name in "${boards[@]}"
|
||||
do
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY}
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX.uf2
|
||||
done
|
||||
|
||||
# Build with EDDSA
|
||||
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY} -DENABLE_EDDSA=1
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release_eddsa/pico_fido_$board_name-$SUFFIX-eddsa1.uf2
|
||||
done
|
||||
fi
|
||||
|
||||
Submodule pico-keys-sdk updated: d0dea3d0c5...7abedc5b0e
@@ -1,6 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src
|
||||
REQUIRES esp_tinyusb mbedtls efuse
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES esp_tinyusb mbedtls efuse pico-keys-sdk
|
||||
)
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)
|
||||
|
||||
@@ -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_vendor(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();
|
||||
|
||||
@@ -59,6 +60,9 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] != CTAP_GET_NEXT_ASSERTION) {
|
||||
reset_gna_state();
|
||||
}
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
}
|
||||
|
||||
@@ -419,6 +419,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
hsh[1] = pin_len;
|
||||
hsh[2] = 1; // New format indicator
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
mbedtls_platform_zeroize(paddedNewPin, sizeof(paddedNewPin));
|
||||
pin_derive_verifier(dhash, 16, hsh + 3);
|
||||
file_put_data(ef_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
@@ -430,6 +431,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
needs_power_cycle = false;
|
||||
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x4) { //changePIN
|
||||
@@ -458,6 +461,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (needs_power_cycle) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
@@ -587,6 +593,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
low_flash_available();
|
||||
resetPinUvAuthToken();
|
||||
resetPersistentPinUvAuthToken();
|
||||
needs_power_cycle = false;
|
||||
goto err; // No return
|
||||
}
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
|
||||
@@ -623,6 +630,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (needs_power_cycle) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
@@ -720,6 +730,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
|
||||
needs_power_cycle = false;
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
|
||||
@@ -144,12 +144,6 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
if (subcommand == 0xFF) {
|
||||
#ifndef ENABLE_EMULATION
|
||||
const bool is_phy = (vendorCommandId == CTAP_CONFIG_PHY_VIDPID ||
|
||||
vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO ||
|
||||
vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS ||
|
||||
vendorCommandId == CTAP_CONFIG_PHY_OPTS);
|
||||
#endif
|
||||
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE){
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
@@ -192,25 +186,6 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_VIDPID) {
|
||||
phy_data.vid = (vendorParamInt >> 16) & 0xFFFF;
|
||||
phy_data.pid = vendorParamInt & 0xFFFF;
|
||||
phy_data.vidpid_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO) {
|
||||
phy_data.led_gpio = (uint8_t)vendorParamInt;
|
||||
phy_data.led_gpio_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS) {
|
||||
phy_data.led_brightness = (uint8_t)vendorParamInt;
|
||||
phy_data.led_brightness_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_OPTS) {
|
||||
phy_data.opts = (uint16_t)vendorParamInt;
|
||||
}
|
||||
#endif
|
||||
else if (vendorCommandId == CTAP_CONFIG_EA_UPLOAD) {
|
||||
if (vendorParamByteString.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
@@ -239,11 +214,6 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_SUBCOMMAND);
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
if (is_phy && phy_save() != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
#endif
|
||||
goto err;
|
||||
}
|
||||
else if (subcommand == 0x03) {
|
||||
|
||||
@@ -42,6 +42,22 @@ uint32_t timerx = 0;
|
||||
uint8_t *datax = NULL;
|
||||
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) {
|
||||
(void) data;
|
||||
(void) len;
|
||||
@@ -57,19 +73,7 @@ int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||
credentialCounter++;
|
||||
err:
|
||||
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
||||
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;
|
||||
reset_gna_state();
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
@@ -424,7 +428,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (!silent) {
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
if (creds[j].board_creation > creds[i].board_creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
|
||||
@@ -613,24 +613,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
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};
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
|
||||
|
||||
@@ -206,7 +206,12 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
uint8_t buffer[1024];
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
uint8_t keydev[32] = {0};
|
||||
if (load_keydev(keydev) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, keydev, 32);
|
||||
mbedtls_platform_zeroize(keydev, sizeof(keydev));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
@@ -238,41 +243,6 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
|
||||
}
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
else if (cmd == CTAP_VENDOR_PHY_OPTS) {
|
||||
if (vendorCmd == 0x01) {
|
||||
uint16_t opts = 0;
|
||||
if (file_has_data(ef_phy)) {
|
||||
uint8_t *pdata = file_get_data(ef_phy);
|
||||
opts = get_uint16_t_be(pdata + PHY_OPTS);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, opts));
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (cmd == CTAP_VENDOR_MEMORY) {
|
||||
if (vendorCmd == 0x01) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_free_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_used_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_total_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_num_files()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_size()));
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "files.h"
|
||||
#include "otp.h"
|
||||
|
||||
extern bool has_set_rtc();
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
|
||||
|
||||
static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, uint8_t *outk) {
|
||||
@@ -148,6 +149,10 @@ int credential_create(CborCharString *rpId,
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
if (has_set_rtc()) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) get_rtc_time()));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + (uint16_t)rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
|
||||
@@ -220,7 +225,7 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
CBOR_FIELD_GET_TEXT(cred->userDisplayName, 1);
|
||||
}
|
||||
else if (val_u == 0x06) {
|
||||
CBOR_FIELD_GET_UINT(cred->creation, 1);
|
||||
CBOR_FIELD_GET_UINT(cred->board_creation, 1);
|
||||
}
|
||||
else if (val_u == 0x07) {
|
||||
cred->extensions.present = true;
|
||||
@@ -255,6 +260,9 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x0C) {
|
||||
CBOR_FIELD_GET_UINT(cred->rtc_creation, 1);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(1);
|
||||
}
|
||||
@@ -319,7 +327,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
credential_free(&rcred);
|
||||
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;
|
||||
credential_free(&rcred);
|
||||
new_record = false;
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct Credential {
|
||||
CborByteString userId;
|
||||
CborCharString userName;
|
||||
CborCharString userDisplayName;
|
||||
uint64_t creation;
|
||||
uint64_t board_creation;
|
||||
CredExtensions extensions;
|
||||
const bool *use_sign_count;
|
||||
int64_t alg;
|
||||
@@ -51,6 +51,7 @@ typedef struct Credential {
|
||||
CborByteString id;
|
||||
CredOptions opts;
|
||||
bool present;
|
||||
uint64_t rtc_creation;
|
||||
} Credential;
|
||||
|
||||
#define CRED_PROT_UV_OPTIONAL 0x01
|
||||
|
||||
@@ -53,6 +53,11 @@ const uint8_t fido_aid[] = {
|
||||
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
};
|
||||
|
||||
const uint8_t fido_aid_backup[] = {
|
||||
8,
|
||||
0xB0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
};
|
||||
|
||||
const uint8_t atr_fido[] = {
|
||||
23,
|
||||
0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59,
|
||||
@@ -86,6 +91,7 @@ INITIALIZER ( fido_ctor ) {
|
||||
get_version_major = fido_get_version_major;
|
||||
get_version_minor = fido_get_version_minor;
|
||||
register_app(fido_select, fido_aid);
|
||||
register_app(fido_select, fido_aid_backup);
|
||||
}
|
||||
|
||||
int fido_unload() {
|
||||
@@ -477,11 +483,13 @@ void scan_all() {
|
||||
}
|
||||
|
||||
extern void init_otp();
|
||||
extern bool needs_power_cycle;
|
||||
void init_fido() {
|
||||
scan_all();
|
||||
#ifdef ENABLE_OTP_APP
|
||||
init_otp();
|
||||
#endif
|
||||
needs_power_cycle = false;
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
@@ -530,18 +538,32 @@ extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
extern int cbor_parse(int, uint8_t *, size_t);
|
||||
extern int cbor_vendor(const uint8_t *data, size_t len);
|
||||
extern void driver_init_hid();
|
||||
|
||||
#define CTAP_CBOR 0x10
|
||||
|
||||
int cmd_vendor() {
|
||||
uint8_t *old_buf = res_APDU;
|
||||
driver_init_hid();
|
||||
int ret = cbor_vendor(apdu.data, apdu.nc);
|
||||
res_APDU = old_buf;
|
||||
if (ret != 0) {
|
||||
return set_res_sw(0x64, ret);
|
||||
}
|
||||
res_APDU_size += 1;
|
||||
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_cbor() {
|
||||
uint8_t *old_buf = res_APDU;
|
||||
driver_init_hid();
|
||||
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
res_APDU = old_buf;
|
||||
if (ret != 0) {
|
||||
return set_res_sw(0x64, ret);
|
||||
}
|
||||
res_APDU_size += 1;
|
||||
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
|
||||
return SW_OK();
|
||||
@@ -552,6 +574,7 @@ static const cmd_t cmds[] = {
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ CTAP_CBOR, cmd_cbor },
|
||||
{ 0x41, cmd_vendor },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#define EF_OATH_CODE 0xBAFF
|
||||
#define EF_OTP_SLOT1 0xBB00
|
||||
#define EF_OTP_SLOT2 0xBB01
|
||||
#define EF_OTP_SLOT3 0xBB02
|
||||
#define EF_OTP_SLOT4 0xBB03
|
||||
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
|
||||
@@ -36,19 +36,6 @@ extern uint8_t session_pin[32];
|
||||
uint8_t mkek_mask[MKEK_KEY_SIZE];
|
||||
bool has_mkek_mask = false;
|
||||
|
||||
#define POLY 0xedb88320
|
||||
|
||||
uint32_t crc32c(const uint8_t *buf, size_t len) {
|
||||
uint32_t crc = 0xffffffff;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
void mkek_masked(uint8_t *mkek, const uint8_t *mask) {
|
||||
if (mask) {
|
||||
for (int i = 0; i < MKEK_KEY_SIZE; i++) {
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "asn1.h"
|
||||
#include "management.h"
|
||||
|
||||
bool is_gpg = true;
|
||||
|
||||
int man_process_apdu();
|
||||
int man_unload();
|
||||
|
||||
@@ -44,6 +46,7 @@ int man_select(app_t *a, uint8_t force) {
|
||||
init_otp();
|
||||
#endif
|
||||
}
|
||||
is_gpg = false;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,22 +94,22 @@ int oath_select(app_t *a, uint8_t force) {
|
||||
res_APDU[res_APDU_size++] = sizeof(challenge);
|
||||
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
|
||||
res_APDU_size += sizeof(challenge);
|
||||
}
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
const uint8_t *pin_data = file_get_data(ef_otp_pin);
|
||||
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = *pin_data;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
if (is_nk) {
|
||||
res_APDU[res_APDU_size++] = TAG_SERIAL_NUMBER;
|
||||
res_APDU[res_APDU_size++] = 8;
|
||||
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8);
|
||||
res_APDU_size += 8;
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
const uint8_t *pin_data = file_get_data(ef_otp_pin);
|
||||
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = *pin_data;
|
||||
}
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return PICOKEY_OK;
|
||||
|
||||
@@ -49,6 +49,11 @@ void append_keyboard_buffer(const uint8_t *buf, size_t len) {}
|
||||
#define CONFIG_LED_INV 0x10
|
||||
#define CONFIG_STATUS_MASK 0x1f
|
||||
|
||||
#define CONFIG3_VALID 0x01
|
||||
#define CONFIG4_VALID 0x02
|
||||
#define CONFIG3_TOUCH 0x04
|
||||
#define CONFIG4_TOUCH 0x08
|
||||
|
||||
/* EXT Flags */
|
||||
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
|
||||
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
|
||||
@@ -161,7 +166,7 @@ extern void scan_all();
|
||||
void init_otp() {
|
||||
if (scanned == false) {
|
||||
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);
|
||||
uint8_t *data = file_get_data(ef);
|
||||
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)) {
|
||||
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);
|
||||
otp_config_t *otp_config = (otp_config_t *) data;
|
||||
if (file_has_data(ef) == false) {
|
||||
@@ -333,6 +339,39 @@ int otp_unload() {
|
||||
}
|
||||
|
||||
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) {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
@@ -384,12 +423,13 @@ bool check_crc(const otp_config_t *data) {
|
||||
bool _is_otp = false;
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
||||
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)) {
|
||||
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) {
|
||||
@@ -415,10 +455,14 @@ int cmd_otp() {
|
||||
}
|
||||
else if (p1 == 0x04 || p1 == 0x05) { // Update slot
|
||||
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) {
|
||||
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)) {
|
||||
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) {
|
||||
@@ -446,8 +490,16 @@ int cmd_otp() {
|
||||
else if (p1 == 0x06) { // Swap slots
|
||||
uint8_t tmp[otp_config_size + 8];
|
||||
bool ef1_data = false;
|
||||
file_t *ef1 = file_new(EF_OTP_SLOT1);
|
||||
file_t *ef2 = file_new(EF_OTP_SLOT2);
|
||||
uint16_t slot1 = EF_OTP_SLOT1, slot2 = 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)) {
|
||||
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
|
||||
ef1_data = true;
|
||||
@@ -458,7 +510,7 @@ int cmd_otp() {
|
||||
else {
|
||||
delete_file(ef1);
|
||||
// When a dynamic file is deleted, existing referenes are invalidated
|
||||
ef2 = file_new(EF_OTP_SLOT2);
|
||||
ef2 = file_new(slot2);
|
||||
}
|
||||
if (ef1_data) {
|
||||
file_put_data(ef2, tmp, sizeof(tmp));
|
||||
@@ -478,8 +530,15 @@ int cmd_otp() {
|
||||
else if (p1 == 0x13) { // Get config
|
||||
man_get_config();
|
||||
}
|
||||
else if (p1 == 0x14) {
|
||||
otp_status_ext();
|
||||
}
|
||||
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)) {
|
||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||
if (!(otp_config->tkt_flags & CHAL_RESP)) {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0700
|
||||
#define PICO_FIDO_VERSION 0x0704
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
@@ -51,7 +51,7 @@ def test_lockout(device, resetdevice, client_pin):
|
||||
res = client_pin.get_pin_retries()
|
||||
assert res[0] == attempts
|
||||
|
||||
if err == CtapError.ERR.PIN_AUTH_BLOCKED:
|
||||
if e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED:
|
||||
device.reboot()
|
||||
client_pin = ClientPin(resetdevice.client()._backend.ctap2)
|
||||
|
||||
|
||||
@@ -202,10 +202,29 @@ def test_rk_with_allowlist_of_different_rp(resetdevice):
|
||||
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):
|
||||
"""
|
||||
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"}
|
||||
user = generate_random_user()
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import sys
|
||||
import keyring
|
||||
|
||||
DOMAIN = "PicoKeys.com"
|
||||
USERNAME = "Pico-Fido"
|
||||
|
||||
try:
|
||||
import keyring
|
||||
from keyrings.osx_keychain_keys.backend import OSXKeychainKeysBackend, OSXKeychainKeyType, OSXKeyChainKeyClassType
|
||||
except:
|
||||
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyrings.osx-keychain-keys`')
|
||||
sys.exit(-1)
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
|
||||
except:
|
||||
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
def get_backend(use_secure_enclave=False):
|
||||
backend = OSXKeychainKeysBackend(
|
||||
key_type=OSXKeychainKeyType.EC, # Key type, e.g. RSA, RC, DSA, ...
|
||||
key_class_type=OSXKeyChainKeyClassType.Private, # Private key, Public key, Symmetric-key
|
||||
key_size_in_bits=256,
|
||||
is_permanent=True, # If set, saves the key in keychain; else, returns a transient key
|
||||
use_secure_enclave=use_secure_enclave, # Saves the key in the T2 (TPM) chip, requires a code-signed interpreter
|
||||
access_group=None, # Limits key management and retrieval to set group, requires a code-signed interpreter
|
||||
is_extractable=True # If set, private key is extractable; else, it can't be retrieved, but only operated against
|
||||
)
|
||||
return backend
|
||||
|
||||
def generate_secure_key(use_secure_enclave=False):
|
||||
backend = get_backend(use_secure_enclave)
|
||||
backend.set_password(DOMAIN, USERNAME, password=None)
|
||||
return backend.get_password(DOMAIN, USERNAME)
|
||||
|
||||
def get_d(key):
|
||||
return key.private_numbers().private_value.to_bytes(32, 'big')
|
||||
|
||||
def set_secure_key(pk):
|
||||
backend = get_backend(False)
|
||||
try:
|
||||
backend.delete_password(DOMAIN, USERNAME)
|
||||
except:
|
||||
pass
|
||||
backend.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()))
|
||||
|
||||
def get_secure_key():
|
||||
key = None
|
||||
try:
|
||||
backend = get_backend(False)
|
||||
key = backend.get_password(DOMAIN, USERNAME)[0]
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
try:
|
||||
key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault
|
||||
except keyring.errors.PasswordSetError:
|
||||
key = generate_secure_key(False)[0]
|
||||
return get_d(key)
|
||||
@@ -1,44 +0,0 @@
|
||||
import sys
|
||||
|
||||
DOMAIN = "PicoKeys.com"
|
||||
USERNAME = "Pico-Fido"
|
||||
|
||||
try:
|
||||
import keyring
|
||||
except:
|
||||
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyring`')
|
||||
sys.exit(-1)
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
except:
|
||||
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
|
||||
def generate_secure_key():
|
||||
pkey = ec.generate_private_key(ec.SECP256R1())
|
||||
set_secure_key(pkey)
|
||||
return keyring.get_password(DOMAIN, USERNAME)
|
||||
|
||||
def get_d(key):
|
||||
return load_pem_private_key(key, password=None).private_numbers().private_value.to_bytes(32, 'big')
|
||||
|
||||
def set_secure_key(pk):
|
||||
try:
|
||||
keyring.delete_password(DOMAIN, USERNAME)
|
||||
except:
|
||||
pass
|
||||
keyring.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()).decode())
|
||||
|
||||
def get_secure_key():
|
||||
key = None
|
||||
try:
|
||||
key = keyring.get_password(DOMAIN, USERNAME)
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
key = generate_secure_key()
|
||||
return get_d(key.encode())
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user