Compare commits

...

54 Commits

Author SHA1 Message Date
303c42901a limit length of wLength 2019-07-15 11:32:02 -04:00
df2f950e69 Merge pull request #217 from merlokk/extapdu
Extended length apdu, iso14443 chaining and select
2019-07-08 22:03:02 -04:00
10bf4242e1 fail with more import related info 2019-07-08 21:54:48 -04:00
9e95b0075c default no serial printing 2019-07-08 21:54:36 -04:00
ddbe31776c Merge pull request #220 from merlokk/obt_src
added text how to obtain source code
2019-07-08 21:22:48 -04:00
645ca6a5a0 add 3-space list 2019-07-08 18:12:28 +03:00
15fc39faed added text how to obtain source code 2019-07-08 17:58:57 +03:00
a1eedc0048 small fix 2019-07-06 13:09:19 +03:00
89e00482e4 some improvements 2019-07-06 12:52:23 +03:00
533ce39237 fix nfc_cc length 2019-07-06 00:15:21 +03:00
63ee003535 Merge pull request #202 from winksaville/patch-1
Update building.md
2019-07-05 10:29:40 -04:00
fa9408d5d6 fix u2f tests 2019-07-05 12:39:32 +03:00
ed9689435d APDU_FIDO_U2F_VERSION 2019-07-05 12:33:23 +03:00
24a006068d fix extended apdu decode 2019-07-05 12:25:46 +03:00
315b6564ab u2f works with extended apdu and now user presence not needs if request come from nfc and power from usb 2019-07-04 23:12:31 +03:00
4d9285085f fix tests 2019-07-04 20:42:24 +03:00
2272e69e15 fix tests 2019-07-04 20:14:24 +03:00
151e1d0e9b fix some errors in tests 2019-07-04 20:09:47 +03:00
d1df8b8b77 u2f authenticate fix 2019-07-04 19:54:00 +03:00
cb76c34ed2 fix addressing 2019-07-04 19:45:09 +03:00
f2ebaf6abe invalid cla and r-block works 2019-07-04 19:14:26 +03:00
4845d2c172 fix 14443 apdu decode and select 2019-07-04 17:52:00 +03:00
75b1d9cd01 offset calc refactoring 2019-07-04 17:38:34 +03:00
26bc8a2889 apdu decoding works 2019-07-04 17:27:03 +03:00
88a8eba424 gitignore 2019-07-04 16:32:11 +03:00
d2c85881e6 applet selection and apdu check 2019-07-04 16:29:30 +03:00
236498ee03 add make 2019-07-04 16:27:57 +03:00
a51c9192b1 add apdu_decode 2019-07-04 16:27:33 +03:00
4dc6bcf771 apdu decode sketch 2019-07-03 23:01:37 +03:00
cce81b23d9 Merge branch 'master' of https://github.com/merlokk/solo into extapdu 2019-07-03 22:59:41 +03:00
8c2e2386a9 fix NFC applet selection does not work correctly #213 2019-07-03 20:35:50 +03:00
c783a1442a Merge pull request #215 from merlokk/nfc-testing
Nfc testing
2019-07-03 11:37:19 -04:00
b61e5db736 style 2019-07-03 17:57:27 +03:00
b41cd5d5b8 add nfc test force flag 2019-07-03 17:54:53 +03:00
b42e990f67 format fix 2019-07-03 01:39:38 +03:00
ff53bb1e32 fix style 2019-07-03 01:16:55 +03:00
2d72e02051 remove unused lib 2019-07-03 01:03:34 +03:00
91c77da179 cbor.loads changed to cbor.decode_from 2019-07-03 00:43:51 +03:00
795cf5c4a1 selecting NFC key works 2019-07-02 19:55:04 +03:00
d1722b85af add library not found error 2019-07-02 19:45:46 +03:00
2c500fe25a check pyscard module first 2019-06-28 12:32:52 +03:00
751b2fd69c add nfc device search 2019-06-28 12:16:59 +03:00
c2216929a9 Create SECURITY.md 2019-06-14 00:19:14 +02:00
3f225f362f Update building.md
Adding `solo` as a prerequesite, it's required by `make build-hacker` to merge the hex files.
2019-05-29 15:11:18 -07:00
dd4ff920ad Merge pull request #200 from solokeys/persistedkey
use persisted key info
2019-05-28 18:36:50 -04:00
bddd60c080 use persisted key info 2019-05-27 13:54:29 -04:00
5f878ff022 Merge pull request #196 from solokeys/fido2-conformance
Fido2 conformance
2019-05-19 12:24:36 -04:00
14f91a6e15 add screenshots of tests passing 2019-05-19 12:24:15 -04:00
cd29a0e0fe python black 2019-05-18 21:11:22 -04:00
46b7f9a778 add UP as UV method for when PIN is not set 2019-05-18 14:35:30 -04:00
31328fe7e7 dont fail when public key type is too large 2019-05-18 14:34:54 -04:00
035b1a8632 Merge pull request #195 from solokeys/bump_2.3.0
Update STABLE_VERSION
2019-05-13 23:11:33 +02:00
b1563dbe94 Update STABLE_VERSION 2019-05-13 16:43:14 -04:00
2a9e3ac576 Merge pull request #194 from solokeys/sanitize
fix potential memory leaks
2019-05-13 16:10:42 -04:00
24 changed files with 433 additions and 126 deletions

2
.gitignore vendored
View File

@ -83,3 +83,5 @@ targets/*/docs/
main main
builds/* builds/*
tools/testing/.idea/*
tools/testing/tests/__pycache__/*

32
SECURITY.md Normal file
View File

@ -0,0 +1,32 @@
# Security Policy
## Supported Versions
We fix security issues as soon as they are found, and release firmware updates.
Each such release is accompanied by release notes, see <https://github.com/solokeys/solo/releases>.
The latest version can be determined using the file <https://github.com/solokeys/solo/blob/master/STABLE_VERSION>.
To update your key:
- either visit <https://update.solokeys.com>, or
- use our commandline tool <https://github.com/solokeys/solo-python>:
```
solo key update [--secure|--hacker]
```
## Reporting a Vulnerability
To report vulnerabilities you have found:
- preferably contact [@conor1](https://keybase.io/conor1), [@0x0ece](https://keybase.io/0x0ece) or [@nickray](https://keybase.io/nickray) via Keybase, or
- send us e-mail using OpenPGP to [security@solokeys.com](mailto:security@solokeys.com).
<https://keys.openpgp.org/vks/v1/by-fingerprint/85AFA2769F4381E5712C36A04DDFC46FEF1F7F3F>
We do not currently run a paid bug bounty program, but are happy to provide you with a bunch of Solo keys in recognition of your findings.
## Mailing List
Join our release notification mailing list to be informed about each release:
https://sendy.solokeys.com/subscription?f=9MLIqMDmox1Ucz89C892Kq09IqYMM7OB8UrBrkvtTkDI763QF3L5PMYlRhlVNo2AI892mO

View File

@ -1 +1 @@
2.2.2 2.3.0

View File

@ -1,22 +1,40 @@
# Building solo
To build, develop and debug the firmware for the STM32L432. This will work To build, develop and debug the firmware for the STM32L432. This will work
for Solo Hacker, the Nucleo development board, or your own homemade Solo. for Solo Hacker, the Nucleo development board, or your own homemade Solo.
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)). There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
# Prerequisites ## Prerequisites
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM. Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`, You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`. but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
To program your build, you'll need one of the following programs. To program your build, you'll need one of the following programs.
- [openocd](http://openocd.org) - [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink) - [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html) - [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
# Compilation ## Obtain source code and solo tool
Source code can be downloaded from:
- [github releases list](https://github.com/solokeys/solo/releases)
- [github repository](https://github.com/solokeys/solo)
**solo** tool can be downloaded from:
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
- from installing prerequisites `pip3 install -r tools/requirements.txt`
- github repository: [repository](https://github.com/solokeys/solo-python)
- installation python enviroment witn command `make venv` from root directory of source code
## Compilation
Enter the `stm32l4xx` target directory. Enter the `stm32l4xx` target directory.
@ -80,8 +98,7 @@ make build-release-locked
Programming `all.hex` will cause the device to permanently lock itself. Programming `all.hex` will cause the device to permanently lock itself.
## Programming
# Programming
It's recommended to test a debug/hacker build first to make sure Solo is working as expected. It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!). Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
@ -95,7 +112,7 @@ pip3 install -r tools/requirements.txt
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/). If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
## Pre-programmed Solo Hacker ### Pre-programmed Solo Hacker
If your Solo device is already programmed (it flashes green when powered), we recommend If your Solo device is already programmed (it flashes green when powered), we recommend
programming it using the Solo bootloader. programming it using the Solo bootloader.
@ -118,7 +135,7 @@ If something bad happens, you can always boot the Solo bootloader by doing the f
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update). If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
Don't use the ST DFU unless you know what you're doing. Don't use the ST DFU unless you know what you're doing.
## ST USB DFU ### ST USB DFU
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
off and it enumerates as "STM BOOTLOADER". off and it enumerates as "STM BOOTLOADER".
@ -136,7 +153,7 @@ Make sure to program `all.hex`, as this contains both the bootloader and the Sol
If all goes well, you should see a slow-flashing green light. If all goes well, you should see a slow-flashing green light.
## Solo Hacker vs Solo ### Solo Hacker vs Solo
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch. A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
@ -144,7 +161,7 @@ Solo (locked) needs the button to be held down when plugged in to boot to the bo
A locked Solo will only accept signed updates. A locked Solo will only accept signed updates.
## Signed updates ### Signed updates
If this is not a device with a hacker build, you can only program signed updates. If this is not a device with a hacker build, you can only program signed updates.
@ -162,7 +179,7 @@ solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.js
If your Solo isn't locked, you can always reprogram it using a debugger connected directly If your Solo isn't locked, you can always reprogram it using a debugger connected directly
to the token. to the token.
# Permanently locking the device ## Permanently locking the device
If you plan to be using your Solo for real, you should lock it permanently. This prevents If you plan to be using your Solo for real, you should lock it permanently. This prevents
someone from connecting a debugger to your token and stealing credentials. someone from connecting a debugger to your token and stealing credentials.

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

122
fido2/apdu.c Normal file
View File

@ -0,0 +1,122 @@
// Copyright 2019 SoloKeys Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
// iso7816:2013. 5.3.2 Decoding conventions for command bodies
#include "apdu.h"
int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
{
EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
apdu->cla = hapdu->cla;
apdu->ins = hapdu->ins;
apdu->p1 = hapdu->p1;
apdu->p2 = hapdu->p2;
apdu->lc = 0;
apdu->data = NULL;
apdu->le = 0;
apdu->extended_apdu = false;
apdu->case_type = 0x00;
uint8_t b0 = hapdu->lc[0];
// case 1
if (len == 4)
{
apdu->case_type = 0x01;
}
// case 2S (Le)
if (len == 5)
{
apdu->case_type = 0x02;
apdu->le = b0;
if (!apdu->le)
apdu->le = 0x100;
}
// case 3S (Lc + data)
if (len == 5U + b0 && b0 != 0)
{
apdu->case_type = 0x03;
apdu->lc = b0;
}
// case 4S (Lc + data + Le)
if (len == 5U + b0 + 1U && b0 != 0)
{
apdu->case_type = 0x04;
apdu->lc = b0;
apdu->le = data[len - 1];
if (!apdu->le)
apdu->le = 0x100;
}
// extended length apdu
if (len >= 7 && b0 == 0)
{
uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
// case 2E (Le) - extended
if (len == 7)
{
apdu->case_type = 0x12;
apdu->extended_apdu = true;
apdu->le = extlen;
if (!apdu->le)
apdu->le = 0x10000;
}
// case 3E (Lc + data) - extended
if (len == 7U + extlen)
{
apdu->case_type = 0x13;
apdu->extended_apdu = true;
apdu->lc = extlen;
}
// case 4E (Lc + data + Le) - extended 2-byte Le
if (len == 7U + extlen + 2U)
{
apdu->case_type = 0x14;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
// case 4E (Lc + data + Le) - extended 3-byte Le
if (len == 7U + extlen + 3U && data[len - 3] == 0)
{
apdu->case_type = 0x24;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
}
if (!apdu->case_type)
return 1;
if (apdu->lc)
{
if (apdu->extended_apdu)
{
apdu->data = data + 7;
} else {
apdu->data = data + 5;
}
}
return 0;
}

View File

@ -2,6 +2,8 @@
#define _APDU_H_ #define _APDU_H_
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef struct typedef struct
{ {
@ -12,6 +14,30 @@ typedef struct
uint8_t lc; uint8_t lc;
} __attribute__((packed)) APDU_HEADER; } __attribute__((packed)) APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint8_t lc[3];
} __attribute__((packed)) EXT_APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint16_t lc;
uint8_t *data;
uint32_t le;
bool extended_apdu;
uint8_t case_type;
} __attribute__((packed)) APDU_STRUCT;
extern int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
#define APDU_FIDO_U2F_REGISTER 0x01 #define APDU_FIDO_U2F_REGISTER 0x01
#define APDU_FIDO_U2F_AUTHENTICATE 0x02 #define APDU_FIDO_U2F_AUTHENTICATE 0x02
#define APDU_FIDO_U2F_VERSION 0x03 #define APDU_FIDO_U2F_VERSION 0x03
@ -25,6 +51,7 @@ typedef struct
#define SW_COND_USE_NOT_SATISFIED 0x6985 #define SW_COND_USE_NOT_SATISFIED 0x6985
#define SW_FILE_NOT_FOUND 0x6a82 #define SW_FILE_NOT_FOUND 0x6a82
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid #define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
#define SW_CLA_INVALID 0x6e00
#define SW_INTERNAL_EXCEPTION 0x6f00 #define SW_INTERNAL_EXCEPTION 0x6f00
#endif //_APDU_H_ #endif //_APDU_H_

View File

@ -1987,5 +1987,5 @@ void ctap_reset()
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH)); memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
ctap_reset_key_agreement(); ctap_reset_key_agreement();
crypto_reset_master_secret(); crypto_load_master_secret(STATE.key_space);
} }

View File

@ -929,7 +929,15 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
buflen = sizeof(type); buflen = sizeof(type);
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL); ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
if (ret == CborErrorOutOfMemory)
{
cred->type = PUB_KEY_CRED_UNKNOWN;
}
else
{
check_ret(ret); check_ret(ret);
}
if (strncmp(type, "public-key",11) == 0) if (strncmp(type, "public-key",11) == 0)
{ {

View File

@ -105,6 +105,8 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param);
#define NFC_IS_AVAILABLE 2 #define NFC_IS_AVAILABLE 2
int device_is_nfc(); int device_is_nfc();
void request_from_nfc(bool request_active);
void device_init_button(); void device_init_button();
#endif #endif

View File

@ -113,14 +113,14 @@ end:
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length); printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
} }
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp) void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp)
{ {
if (len < 5 || !req) if (!header)
return; return;
uint32_t alen = req[4]; request_from_nfc(true); // disable presence test
u2f_request_ex((APDU_HEADER *)header, data, datalen, resp);
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp); request_from_nfc(false); // enable presence test
} }
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp) void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)

View File

@ -101,7 +101,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
// u2f_request send a U2F message to NFC protocol // u2f_request send a U2F message to NFC protocol
// @req data with iso7816 apdu message // @req data with iso7816 apdu message
// @len data length // @len data length
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp); void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp);
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid); int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);

View File

@ -20,6 +20,9 @@
], ],
"userVerificationDetails": [ "userVerificationDetails": [
[ [
{
"userVerification": 1
},
{ {
"userVerification": 4 "userVerification": 4
} }

View File

@ -7,7 +7,7 @@ SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB) SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib # FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c

View File

@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL; uint8_t *pbuf = NULL;
uint16_t status_info = 0U; uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK; USBD_StatusTypeDef ret = USBD_OK;
req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK) switch (req->bmRequest & USB_REQ_TYPE_MASK)
{ {
@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break; break;
case USB_REQ_GET_DESCRIPTOR: case USB_REQ_GET_DESCRIPTOR:
req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC) if(req->wValue >> 8 == HID_REPORT_DESC)
{ {
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength); len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);

View File

@ -31,7 +31,7 @@
// #define DISABLE_CTAPHID_WINK // #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR // #define DISABLE_CTAPHID_CBOR
#define ENABLE_SERIAL_PRINTING // #define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER) #if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION #define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION

View File

@ -43,6 +43,7 @@ uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd; extern PCD_HandleTypeDef hpcd;
static int _NFC_status = 0; static int _NFC_status = 0;
static bool isLowFreq = 0; static bool isLowFreq = 0;
static bool _RequestComeFromNFC = false;
// #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN)) // #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
static int is_physical_button_pressed() static int is_physical_button_pressed()
@ -57,6 +58,10 @@ static int is_touch_button_pressed()
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed; int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
void request_from_nfc(bool request_active) {
_RequestComeFromNFC = request_active;
}
// Timer6 overflow handler. happens every ~90ms. // Timer6 overflow handler. happens every ~90ms.
void TIM6_DAC_IRQHandler() void TIM6_DAC_IRQHandler()
{ {
@ -491,7 +496,7 @@ static int handle_packets()
int ctap_user_presence_test(uint32_t up_delay) int ctap_user_presence_test(uint32_t up_delay)
{ {
int ret; int ret;
if (device_is_nfc() == NFC_IS_ACTIVE) if (device_is_nfc() == NFC_IS_ACTIVE || _RequestComeFromNFC)
{ {
return 1; return 1;
} }

View File

@ -14,6 +14,16 @@
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN)) #define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
uint8_t p14443_block_offset(uint8_t pcb) {
uint8_t offset = 1;
// NAD following
if (pcb & 0x04) offset++;
// CID following
if (pcb & 0x08) offset++;
return offset;
}
// Capability container // Capability container
const CAPABILITY_CONTAINER NFC_CC = { const CAPABILITY_CONTAINER NFC_CC = {
.cclen_hi = 0x00, .cclen_lo = 0x0f, .cclen_hi = 0x00, .cclen_lo = 0x0f,
@ -112,6 +122,7 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
while (tstart + timeout_ms > millis()) while (tstart + timeout_ms > millis())
{ {
uint8_t int0 = ams_read_reg(AMS_REG_INT0); uint8_t int0 = ams_read_reg(AMS_REG_INT0);
if (int0) process_int0(int0);
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2); uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE)) if (buffer_status2 && (int0 & AMS_INT_RXE))
@ -161,14 +172,18 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
if (len > 32 - 3) if (len > 32 - 3)
return false; return false;
res[0] = NFC_CMD_IBLOCK | (req0 & 3); res[0] = NFC_CMD_IBLOCK | (req0 & 0x0f);
res[1] = 0;
res[2] = 0;
uint8_t block_offset = p14443_block_offset(req0);
if (len && data) if (len && data)
memcpy(&res[1], data, len); memcpy(&res[block_offset], data, len);
res[len + 1] = resp >> 8; res[len + block_offset + 0] = resp >> 8;
res[len + 2] = resp & 0xff; res[len + block_offset + 1] = resp & 0xff;
nfc_write_frame(res, 3 + len); nfc_write_frame(res, block_offset + len + 2);
return true; return true;
} }
@ -182,21 +197,24 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
{ {
uint8_t res[32 + 2]; uint8_t res[32 + 2];
int sendlen = 0; int sendlen = 0;
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3); uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
uint8_t block_offset = p14443_block_offset(req0);
if (len <= 31) if (len <= 31)
{ {
uint8_t res[32] = {0}; uint8_t res[32] = {0};
res[0] = iBlock; res[0] = iBlock;
if (len && data) if (len && data)
memcpy(&res[1], data, len); memcpy(&res[block_offset], data, len);
nfc_write_frame(res, len + 1); nfc_write_frame(res, len + block_offset);
} else { } else {
do { do {
// transmit I block // transmit I block
int vlen = MIN(31, len - sendlen); int vlen = MIN(32 - block_offset, len - sendlen);
res[0] = iBlock; res[0] = iBlock;
memcpy(&res[1], &data[sendlen], vlen); res[1] = 0;
res[2] = 0;
memcpy(&res[block_offset], &data[sendlen], vlen);
// if not a last block // if not a last block
if (vlen + sendlen < len) if (vlen + sendlen < len)
@ -205,7 +223,7 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
} }
// send data // send data
nfc_write_frame(res, vlen + 1); nfc_write_frame(res, vlen + block_offset);
sendlen += vlen; sendlen += vlen;
// wait for transmit (32 bytes aprox 2,5ms) // wait for transmit (32 bytes aprox 2,5ms)
@ -226,9 +244,10 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
break; break;
} }
if (reclen != 1) uint8_t rblock_offset = p14443_block_offset(recbuf[0]);
if (reclen != rblock_offset)
{ {
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len); printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen, sendlen, len);
dump_hex1(TAG_NFC, recbuf, reclen); dump_hex1(TAG_NFC, recbuf, reclen);
break; break;
} }
@ -371,39 +390,72 @@ int answer_rats(uint8_t parameter)
nfc_write_frame(res, sizeof(res)); nfc_write_frame(res, sizeof(res));
ams_wait_for_tx(10); if (!ams_wait_for_tx(10))
{
printf1(TAG_NFC, "RATS TX timeout.\r\n");
ams_write_command(AMS_CMD_DEFAULT);
return 1;
}
return 0; return 0;
} }
void rblock_acknowledge() void rblock_acknowledge(uint8_t req0, bool ack)
{ {
uint8_t buf[32]; uint8_t buf[32] = {0};
uint8_t block_offset = p14443_block_offset(req0);
NFC_STATE.block_num = !NFC_STATE.block_num; NFC_STATE.block_num = !NFC_STATE.block_num;
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
nfc_write_frame(buf,1); buf[0] = NFC_CMD_RBLOCK | (req0 & 0x0f);
if (ack)
buf[0] |= NFC_CMD_RBLOCK_ACK;
nfc_write_frame(buf, block_offset);
}
// international AID = RID:PIX
// RID length == 5 bytes
// usually aid length must be between 5 and 16 bytes
int applet_cmp(uint8_t * aid, int len, uint8_t * const_aid, int const_len)
{
if (len > const_len)
return 10;
// if international AID
if ((const_aid[0] & 0xf0) == 0xa0)
{
if (len < 5)
return 11;
return memcmp(aid, const_aid, MIN(len, const_len));
} else {
if (len != const_len)
return 11;
return memcmp(aid, const_aid, const_len);
}
} }
// Selects application. Returns 1 if success, 0 otherwise // Selects application. Returns 1 if success, 0 otherwise
int select_applet(uint8_t * aid, int len) int select_applet(uint8_t * aid, int len)
{ {
if (memcmp(aid,AID_FIDO,sizeof(AID_FIDO)) == 0) if (applet_cmp(aid, len, (uint8_t *)AID_FIDO, sizeof(AID_FIDO) - 1) == 0)
{ {
NFC_STATE.selected_applet = APP_FIDO; NFC_STATE.selected_applet = APP_FIDO;
return APP_FIDO; return APP_FIDO;
} }
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0) else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TYPE_4, sizeof(AID_NDEF_TYPE_4) - 1) == 0)
{ {
NFC_STATE.selected_applet = APP_NDEF_TYPE_4; NFC_STATE.selected_applet = APP_NDEF_TYPE_4;
return APP_NDEF_TYPE_4; return APP_NDEF_TYPE_4;
} }
else if (memcmp(aid,AID_CAPABILITY_CONTAINER,sizeof(AID_CAPABILITY_CONTAINER)) == 0) else if (applet_cmp(aid, len, (uint8_t *)AID_CAPABILITY_CONTAINER, sizeof(AID_CAPABILITY_CONTAINER) - 1) == 0)
{ {
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER; NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
return APP_CAPABILITY_CONTAINER; return APP_CAPABILITY_CONTAINER;
} }
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0) else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TAG, sizeof(AID_NDEF_TAG) - 1) == 0)
{ {
NFC_STATE.selected_applet = APP_NDEF_TAG; NFC_STATE.selected_applet = APP_NDEF_TAG;
return APP_NDEF_TAG; return APP_NDEF_TAG;
@ -413,25 +465,36 @@ int select_applet(uint8_t * aid, int len)
void nfc_process_iblock(uint8_t * buf, int len) void nfc_process_iblock(uint8_t * buf, int len)
{ {
APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
uint8_t * payload = buf + 1 + 5;
uint8_t plen = apdu->lc;
int selected; int selected;
CTAP_RESPONSE ctap_resp; CTAP_RESPONSE ctap_resp;
int status; int status;
uint16_t reslen;
printf1(TAG_NFC,"Iblock: "); printf1(TAG_NFC,"Iblock: ");
dump_hex1(TAG_NFC, buf, len); dump_hex1(TAG_NFC, buf, len);
uint8_t block_offset = p14443_block_offset(buf[0]);
APDU_STRUCT apdu;
if (apdu_decode(buf + block_offset, len - block_offset, &apdu)) {
printf1(TAG_NFC,"apdu decode error\r\n");
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
return;
}
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
// check CLA
if (apdu.cla != 0x00 && apdu.cla != 0x80) {
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu.cla);
nfc_write_response(buf[0], SW_CLA_INVALID);
return;
}
// TODO this needs to be organized better // TODO this needs to be organized better
switch(apdu->ins) switch(apdu.ins)
{ {
case APDU_INS_SELECT: case APDU_INS_SELECT:
if (plen > len - 6)
{
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
plen = len-6;
}
// if (apdu->p1 == 0 && apdu->p2 == 0x0c) // if (apdu->p1 == 0 && apdu->p2 == 0x0c)
// { // {
// printf1(TAG_NFC,"Select NDEF\r\n"); // printf1(TAG_NFC,"Select NDEF\r\n");
@ -446,14 +509,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
// } // }
// else // else
{ {
selected = select_applet(payload, plen); selected = select_applet(apdu.data, apdu.lc);
if (selected == APP_FIDO) if (selected == APP_FIDO)
{ {
// block = buf[0] & 1;
// block = NFC_STATE.block_num;
// block = !block;
// NFC_STATE.block_num = block;
// NFC_STATE.block_num = block;
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS); nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
printf1(TAG_NFC, "FIDO applet selected.\r\n"); printf1(TAG_NFC, "FIDO applet selected.\r\n");
} }
@ -465,7 +523,7 @@ void nfc_process_iblock(uint8_t * buf, int len)
else else
{ {
nfc_write_response(buf[0], SW_FILE_NOT_FOUND); nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen); printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu.data, apdu.lc);
} }
} }
break; break;
@ -478,7 +536,8 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F GetVersion command.\r\n"); printf1(TAG_NFC, "U2F GetVersion command.\r\n");
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS); u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
break; break;
case APDU_FIDO_U2F_REGISTER: case APDU_FIDO_U2F_REGISTER:
@ -489,9 +548,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F Register command.\r\n"); printf1(TAG_NFC, "U2F Register command.\r\n");
if (plen != 64) if (apdu.lc != 64)
{ {
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen); printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu.lc);
nfc_write_response(buf[0], SW_WRONG_LENGTH); nfc_write_response(buf[0], SW_WRONG_LENGTH);
return; return;
} }
@ -502,20 +561,16 @@ void nfc_process_iblock(uint8_t * buf, int len)
// WTX_on(WTX_TIME_DEFAULT); // WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32(); // SystemClock_Config_LF32();
// delay(300); // delay(300);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);; if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
u2f_request_nfc(&buf[1], len, &ctap_resp); u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);; if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
// if (!WTX_off()) // if (!WTX_off())
// return; // return;
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp()); printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length); nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
// printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp()); printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
break; break;
@ -527,17 +582,17 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F Authenticate command.\r\n"); printf1(TAG_NFC, "U2F Authenticate command.\r\n");
if (plen != 64 + 1 + buf[6 + 64]) if (apdu.lc != 64 + 1 + apdu.data[64])
{ {
delay(5); delay(5);
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]); printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu.lc, apdu.data[64]);
nfc_write_response(buf[0], SW_WRONG_LENGTH); nfc_write_response(buf[0], SW_WRONG_LENGTH);
return; return;
} }
timestamp(); timestamp();
// WTX_on(WTX_TIME_DEFAULT); // WTX_on(WTX_TIME_DEFAULT);
u2f_request_nfc(&buf[1], len, &ctap_resp); u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
// if (!WTX_off()) // if (!WTX_off())
// return; // return;
@ -550,14 +605,16 @@ void nfc_process_iblock(uint8_t * buf, int len)
case APDU_FIDO_NFCCTAP_MSG: case APDU_FIDO_NFCCTAP_MSG:
if (NFC_STATE.selected_applet != APP_FIDO) { if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID); nfc_write_response(buf[0], SW_INS_INVALID);
break; return;
} }
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp()); printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
WTX_on(WTX_TIME_DEFAULT); WTX_on(WTX_TIME_DEFAULT);
request_from_nfc(true);
ctap_response_init(&ctap_resp); ctap_response_init(&ctap_resp);
status = ctap_request(payload, plen, &ctap_resp); status = ctap_request(apdu.data, apdu.lc, &ctap_resp);
request_from_nfc(false);
if (!WTX_off()) if (!WTX_off())
return; return;
@ -580,44 +637,37 @@ void nfc_process_iblock(uint8_t * buf, int len)
break; break;
case APDU_INS_READ_BINARY: case APDU_INS_READ_BINARY:
// response length
reslen = apdu.le & 0xffff;
switch(NFC_STATE.selected_applet) switch(NFC_STATE.selected_applet)
{ {
case APP_CAPABILITY_CONTAINER: case APP_CAPABILITY_CONTAINER:
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n"); printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
if (plen > 15) if (reslen == 0 || reslen > sizeof(NFC_CC))
{ reslen = sizeof(NFC_CC);
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc); nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, reslen, SW_SUCCESS);
plen = 15;
}
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, plen, SW_SUCCESS);
ams_wait_for_tx(10); ams_wait_for_tx(10);
break; break;
case APP_NDEF_TAG: case APP_NDEF_TAG:
printf1(TAG_NFC,"APP_NDEF_TAG\r\n"); printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
if (plen > (sizeof(NDEF_SAMPLE) - 1)) if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
{ reslen = sizeof(NDEF_SAMPLE) - 1;
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc); nfc_write_response_ex(buf[0], NDEF_SAMPLE, reslen, SW_SUCCESS);
plen = sizeof(NDEF_SAMPLE) - 1;
}
nfc_write_response_ex(buf[0], NDEF_SAMPLE, plen, SW_SUCCESS);
ams_wait_for_tx(10); ams_wait_for_tx(10);
break; break;
default: default:
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
printf1(TAG_ERR, "No binary applet selected!\r\n"); printf1(TAG_ERR, "No binary applet selected!\r\n");
return; return;
break; break;
} }
break; break;
default: default:
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins); printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu.ins);
nfc_write_response(buf[0], SW_INS_INVALID); nfc_write_response(buf[0], SW_INS_INVALID);
break; break;
} }
} }
static uint8_t ibuf[1024]; static uint8_t ibuf[1024];
@ -631,7 +681,7 @@ void clear_ibuf()
void nfc_process_block(uint8_t * buf, unsigned int len) void nfc_process_block(uint8_t * buf, unsigned int len)
{ {
printf1(TAG_NFC, "-----\r\n");
if (!len) if (!len)
return; return;
@ -641,6 +691,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
} }
else if (IS_IBLOCK(buf[0])) else if (IS_IBLOCK(buf[0]))
{ {
uint8_t block_offset = p14443_block_offset(buf[0]);
if (buf[0] & 0x10) if (buf[0] & 0x10)
{ {
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len); printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
@ -654,27 +705,27 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
printf1(TAG_NFC_APDU,"i> "); printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len); dump_hex1(TAG_NFC_APDU, buf, len);
if (len) if (len > block_offset)
{ {
memcpy(&ibuf[ibuflen], &buf[1], len - 1); memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - 1; ibuflen += len - block_offset;
} }
// send R block // send R block
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3); rblock_acknowledge(buf[0], true);
nfc_write_frame(&rb, 1);
} else { } else {
if (ibuflen) if (ibuflen)
{ {
if (len) if (len > block_offset)
{ {
memcpy(&ibuf[ibuflen], &buf[1], len - 1); memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - 1; ibuflen += len - block_offset;
} }
memmove(&ibuf[1], ibuf, ibuflen); // add last chaining to top of the block
ibuf[0] = buf[0]; memmove(&ibuf[block_offset], ibuf, ibuflen);
ibuflen++; memmove(ibuf, buf, block_offset);
ibuflen += block_offset;
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len); printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
@ -683,7 +734,6 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
nfc_process_iblock(ibuf, ibuflen); nfc_process_iblock(ibuf, ibuflen);
} else { } else {
// printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
nfc_process_iblock(buf, len); nfc_process_iblock(buf, len);
} }
clear_ibuf(); clear_ibuf();
@ -691,7 +741,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
} }
else if (IS_RBLOCK(buf[0])) else if (IS_RBLOCK(buf[0]))
{ {
rblock_acknowledge(); rblock_acknowledge(buf[0], false);
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n"); printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
} }
else if (IS_SBLOCK(buf[0])) else if (IS_SBLOCK(buf[0]))
@ -710,6 +760,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
else else
{ {
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len); printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
} }
dump_hex1(TAG_NFC, buf, len); dump_hex1(TAG_NFC, buf, len);
} }

View File

@ -40,6 +40,8 @@ typedef struct
#define NFC_CMD_SBLOCK 0xc0 #define NFC_CMD_SBLOCK 0xc0
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) ) #define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
extern uint8_t p14443_block_offset(uint8_t pcb);
#define NFC_SBLOCK_DESELECT 0x30 #define NFC_SBLOCK_DESELECT 0x30
#define NFC_SBLOCK_WTX 0x30 #define NFC_SBLOCK_WTX 0x30

View File

@ -19,7 +19,9 @@ from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>") print("Usage: %s [sim] [nfc] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
print(" sim - test via UDP simulation backend only")
print(" nfc - test via NFC interface only")
sys.exit(0) sys.exit(0)
t = Tester() t = Tester()
@ -31,7 +33,11 @@ if __name__ == "__main__":
t.set_sim(True) t.set_sim(True)
t.set_user_count(10) t.set_user_count(10)
t.find_device() nfcOnly = False
if "nfc" in sys.argv:
nfcOnly = True
t.find_device(nfcOnly)
if "solo" in sys.argv: if "solo" in sys.argv:
SoloTests(t).run() SoloTests(t).run()

View File

@ -78,7 +78,7 @@ def TestCborKeysSorted(cbor_obj):
# https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form # https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form
if isinstance(cbor_obj, bytes): if isinstance(cbor_obj, bytes):
cbor_obj = cbor.loads(cbor_obj)[0] cbor_obj = cbor.decode_from(cbor_obj)[0]
if isinstance(cbor_obj, dict): if isinstance(cbor_obj, dict):
l = [x for x in cbor_obj] l = [x for x in cbor_obj]
@ -222,9 +222,7 @@ class FIDO2Tests(Tester):
) )
with Test("Get shared secret"): with Test("Get shared secret"):
key_agreement, shared_secret = ( key_agreement, shared_secret = self.client.pin_protocol.get_shared_secret()
self.client.pin_protocol._init_shared_secret()
)
cipher = Cipher( cipher = Cipher(
algorithms.AES(shared_secret), algorithms.AES(shared_secret),
modes.CBC(b"\x00" * 16), modes.CBC(b"\x00" * 16),
@ -341,8 +339,8 @@ class FIDO2Tests(Tester):
def test_get_info(self,): def test_get_info(self,):
with Test("Get info"): with Test("Get info"):
info = self.ctap.get_info() info = self.ctap.get_info()
print(bytes(info)) print("data:", bytes(info))
print(cbor.loads(bytes(info))) print("decoded:", cbor.decode_from(bytes(info)))
with Test("Check FIDO2 string is in VERSIONS field"): with Test("Check FIDO2 string is in VERSIONS field"):
assert "FIDO_2_0" in info.versions assert "FIDO_2_0" in info.versions
@ -621,6 +619,20 @@ class FIDO2Tests(Tester):
other={"exclude_list": [{"id": b"1234", "type": "rot13"}]}, other={"exclude_list": [{"id": b"1234", "type": "rot13"}]},
) )
self.testMC(
"Send MC request with excludeList item with bogus type, expect SUCCESS",
cdh,
rp,
user,
key_params,
expectedError=CtapError.ERR.SUCCESS,
other={
"exclude_list": [
{"id": b"1234", "type": "mangoPapayaCoconutNotAPublicKey"}
]
},
)
self.testMC( self.testMC(
"Send MC request with excludeList with bad item, expect error", "Send MC request with excludeList with bad item, expect error",
cdh, cdh,

View File

@ -51,6 +51,7 @@ class Tester:
self.host = "examplo.org" self.host = "examplo.org"
self.user_count = 10 self.user_count = 10
self.is_sim = False self.is_sim = False
self.nfc_interface_only = False
if tester: if tester:
self.initFromTester(tester) self.initFromTester(tester)
@ -61,10 +62,23 @@ class Tester:
self.ctap = tester.ctap self.ctap = tester.ctap
self.ctap1 = tester.ctap1 self.ctap1 = tester.ctap1
self.client = tester.client self.client = tester.client
self.nfc_interface_only = tester.nfc_interface_only
def find_device(self,): def find_device(self, nfcInterfaceOnly=False):
dev = None
self.nfc_interface_only = nfcInterfaceOnly
if not nfcInterfaceOnly:
print("--- HID ---")
print(list(CtapHidDevice.list_devices())) print(list(CtapHidDevice.list_devices()))
dev = next(CtapHidDevice.list_devices(), None) dev = next(CtapHidDevice.list_devices(), None)
if not dev:
from fido2.pcsc import CtapPcscDevice
print("--- NFC ---")
print(list(CtapPcscDevice.list_devices()))
dev = next(CtapPcscDevice.list_devices(), None)
if not dev: if not dev:
raise RuntimeError("No FIDO device found") raise RuntimeError("No FIDO device found")
self.dev = dev self.dev = dev
@ -89,7 +103,7 @@ class Tester:
else: else:
print("Please reboot authentictor and hit enter") print("Please reboot authentictor and hit enter")
input() input()
self.find_device() self.find_device(self.nfc_interface_only)
def send_data(self, cmd, data): def send_data(self, cmd, data):
if not isinstance(data, bytes): if not isinstance(data, bytes):
@ -183,7 +197,7 @@ class Tester:
print("You must power cycle authentictor. Hit enter when done.") print("You must power cycle authentictor. Hit enter when done.")
input() input()
time.sleep(0.2) time.sleep(0.2)
self.find_device() self.find_device(self.nfc_interface_only)
self.ctap.reset() self.ctap.reset()
def testMC(self, test, *args, **kwargs): def testMC(self, test, *args, **kwargs):

View File

@ -42,12 +42,14 @@ class U2FTests(Tester):
with Test("Check bad INS"): with Test("Check bad INS"):
try: try:
self.ctap1.send_apdu(0, 0, 0, 0, b"") self.ctap1.send_apdu(0, 0, 0, 0, b"")
assert False
except ApduError as e: except ApduError as e:
assert e.code == 0x6D00 assert e.code == 0x6D00
with Test("Check bad CLA"): with Test("Check bad CLA"):
try: try:
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc") self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
assert False
except ApduError as e: except ApduError as e:
assert e.code == 0x6E00 assert e.code == 0x6E00