From 9e724be03606888c795370b1a0b3fd2b69696248 Mon Sep 17 00:00:00 2001 From: z4yx Date: Tue, 21 May 2024 19:51:34 +0800 Subject: [PATCH 1/5] test PIV cipher extension with Yubico piv tool --- applets/piv/piv.c | 8 ++++---- test-real/test-piv.sh | 26 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/applets/piv/piv.c b/applets/piv/piv.c index 92908071..da1f2c1f 100644 --- a/applets/piv/piv.c +++ b/applets/piv/piv.c @@ -41,10 +41,10 @@ #define ALG_RSA_2048 0x07 #define ALG_ECC_256 0x11 #define ALG_ECC_384 0x14 -#define ALG_ED25519_DEFAULT 0x22 // defined in https://github.com/go-piv/piv-go/pull/69 +#define ALG_ED25519_DEFAULT 0xE0 #define ALG_RSA_3072_DEFAULT 0x05 // defined in NIST SP 800-78-5 (Initial Public Draft) -#define ALG_RSA_4096_DEFAULT 0x51 -#define ALG_X25519_DEFAULT 0x52 +#define ALG_RSA_4096_DEFAULT 0x16 +#define ALG_X25519_DEFAULT 0xE1 #define ALG_SECP256K1_DEFAULT 0x53 #define ALG_SM2_DEFAULT 0x54 @@ -1099,7 +1099,7 @@ static int piv_get_version(const CAPDU *capdu, RAPDU *rapdu) { if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2); if (LC != 0) EXCEPT(SW_WRONG_LENGTH); RDATA[0] = 0x05; - RDATA[1] = 0x04; + RDATA[1] = 0x07; RDATA[2] = 0x00; LL = 3; return 0; diff --git a/test-real/test-piv.sh b/test-real/test-piv.sh index cac821f7..71db6619 100755 --- a/test-real/test-piv.sh +++ b/test-real/test-piv.sh @@ -86,8 +86,8 @@ test_ChangePin() { assertEquals 'set-mgm-key' 0 $? } -test_RSA2048() { - for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" RSA2048; done +rsa_tests() { + for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" $1; done YPT -a status PIVSignDec 9e # PIN not required for key 9e for s in 9a 9c 9d; do PIVSignDec $s 1; done @@ -101,6 +101,18 @@ test_RSA2048() { assertEquals 'openssl dgst verify' 0 $? } +test_RSA2048() { + rsa_tests RSA2048 +} + +test_RSA3072() { + rsa_tests RSA3072 +} + +test_RSA4096() { + rsa_tests RSA4096 +} + test_ECC256() { for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" ECCP256; done YPT -a status @@ -119,6 +131,16 @@ test_ECC384() { assertContains 'CERT' "$out" 'CN = CertAtSlot9c' } +test_25519() { + for s in 9a 9c 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" ED25519; done + for s in 9d; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" X25519; done + YPT -a status + for s in 9a 9c 9e; do PIVSignDec $s 1 s; done # 9a/9c/9e only do the EDDSA + # PIVSignDec 9d 1 d # 9d only do the EDDH + # out=$(pkcs15-tool --reader "$RDID" --read-certificate 01 | openssl x509 -text) + # assertContains 'CERT' "$out" 'CN = CertAtSlot9a' +} + test_PinBlock() { out=$(YPT -a verify-pin -P 222222 2>&1) assertContains 'verify-pin' "$out" '2 tries left before pin is blocked.' From 4fb369f4c6e69453996ce43279e3efa94d6900cd Mon Sep 17 00:00:00 2001 From: z4yx Date: Sun, 9 Jun 2024 18:03:32 +0800 Subject: [PATCH 2/5] bug fix: touch event being consumed by "pass" before "u2f" --- include/device.h | 1 + interfaces/USB/class/kbdhid/kbdhid.c | 6 +++--- src/device.c | 28 +++++++++++++++++++--------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/device.h b/include/device.h index d277c36e..d8d8d1ae 100644 --- a/include/device.h +++ b/include/device.h @@ -112,6 +112,7 @@ static inline void start_quick_blinking(uint8_t sec) { } void stop_blinking(void); uint8_t device_is_blinking(void); +bool device_allow_kbd_touch(void); void fm11_init(void); uint8_t fm_read_reg(uint16_t reg); void fm_read_regs(uint16_t reg, uint8_t *buf, uint8_t len); diff --git a/interfaces/USB/class/kbdhid/kbdhid.c b/interfaces/USB/class/kbdhid/kbdhid.c index 967a752f..1ae04ac2 100644 --- a/interfaces/USB/class/kbdhid/kbdhid.c +++ b/interfaces/USB/class/kbdhid/kbdhid.c @@ -139,7 +139,7 @@ uint8_t KBDHID_Init() { } uint8_t KBDHID_Loop(void) { - if (state == KBDHID_Idle) { + if (state == KBDHID_Idle && device_allow_kbd_touch()) { const uint8_t touch = get_touch_result(); if (touch != TOUCH_NO) { const int len = pass_handle_touch(touch, key_sequence); @@ -150,9 +150,9 @@ uint8_t KBDHID_Loop(void) { key_sequence[len] = 0; key_seq_position = 0; state = KBDHID_Typing; - DBG_MSG("Start typing %s", key_sequence); + DBG_MSG("Start typing %s\n", key_sequence); + set_touch_result(TOUCH_NO); } - set_touch_result(TOUCH_NO); } else { KBDHID_TypeKeySeq(); } diff --git a/src/device.c b/src/device.c index b3d1fcf9..1e5ccd6e 100644 --- a/src/device.c +++ b/src/device.c @@ -9,22 +9,31 @@ volatile static uint8_t touch_result; static uint8_t has_rf; -static uint32_t last_blink = UINT32_MAX, blink_timeout, blink_interval; +static uint32_t last_blink, blink_timeout, blink_interval; static enum { ON, OFF } led_status; typedef enum { WAIT_NONE = 1, WAIT_CCID, WAIT_CTAPHID, WAIT_DEEP, WAIT_DEEP_TOUCHED, WAIT_DEEP_CANCEL } wait_status_t; volatile static wait_status_t wait_status = WAIT_NONE; // WAIT_NONE is not 0, hence inited -uint8_t device_is_blinking(void) { return last_blink != UINT32_MAX; } +uint8_t device_is_blinking(void) { return blink_timeout != 0; } void device_loop(uint8_t has_touch) { CCID_Loop(); CTAPHID_Loop(0); WebUSB_Loop(); - if (has_touch && // hardware features the touch pad - !device_is_blinking() && // applets are not waiting for touch - device_get_tick() > 2000 // ignore touch for the first 2 seconds - ) - KBDHID_Loop(); + KBDHID_Loop(); +} + +bool device_allow_kbd_touch(void) { + uint32_t now = device_get_tick(); + if (!device_is_blinking() && // applets are not waiting for touch + now > 2000 && // ignore touch for the first 2 seconds + now - 1000 > last_blink && + get_touch_result() != TOUCH_NO + ) { + DBG_MSG("now=%lu last_blink=%lu\n", now, last_blink); + return true; + } + return false; } uint8_t get_touch_result(void) { @@ -140,8 +149,9 @@ static void toggle_led(void) { void device_update_led(void) { uint32_t now = device_get_tick(); + if (!device_is_blinking()) return; if (now > blink_timeout) stop_blinking(); - if (now >= last_blink && now - last_blink >= blink_interval) { + else if (now >= last_blink && now - last_blink >= blink_interval) { last_blink = now; toggle_led(); } @@ -160,7 +170,7 @@ void start_blinking_interval(uint8_t sec, uint32_t interval) { } void stop_blinking(void) { - last_blink = UINT32_MAX; + blink_timeout = 0; if (cfg_is_led_normally_on()) { led_on(); led_status = ON; From 0339e3ec7328defc85656bbad2701eace865d721 Mon Sep 17 00:00:00 2001 From: z4yx Date: Tue, 25 Jun 2024 21:57:53 +0800 Subject: [PATCH 3/5] test algorithm extension --- test-real/test-piv.sh | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/test-real/test-piv.sh b/test-real/test-piv.sh index 71db6619..3ecad0c6 100755 --- a/test-real/test-piv.sh +++ b/test-real/test-piv.sh @@ -15,6 +15,7 @@ PIVGenKeyCert() { algo="$3" YPT -a generate -A $algo -s $key >$TEST_TMP_DIR/pubkey-$key.pem # generate key at $key assertEquals 'yubico-piv-tool generate' 0 $? + if [[ $algo == "X25519" ]]; then return; fi YPT -P 654321 -a verify-pin -a selfsign-certificate -s $key -S "$subject" < $TEST_TMP_DIR/pubkey-$key.pem >$TEST_TMP_DIR/cert-$key.pem assertEquals 'yubico-piv-tool selfsign-certificate' 0 $? YPT -a import-certificate -s $key < $TEST_TMP_DIR/cert-$key.pem @@ -36,13 +37,15 @@ PIVSignDec() { key=$1 pinArgs= op=$3 + inp_file=$TEST_TMP_DIR/cert-$key.pem + if [[ $key == X25519 ]]; then inp_file=$TEST_TMP_DIR/pubkey-$key.pem; fi if [[ -n "$2" ]]; then pinArgs="-P 654321 -a verify-pin"; fi if [[ -z "$op" || s = "$op" ]]; then - YPT $pinArgs -a test-signature -s $key < $TEST_TMP_DIR/cert-$key.pem; + YPT $pinArgs -a test-signature -s $key < $inp_file; assertEquals 'yubico-piv-tool test-signature' 0 $? fi if [[ -z "$op" || d = "$op" ]]; then - YPT $pinArgs -a test-decipher -s $key < $TEST_TMP_DIR/cert-$key.pem; + YPT $pinArgs -a test-decipher -s $key < $inp_file; assertEquals 'yubico-piv-tool test-decipher' 0 $? fi } @@ -113,32 +116,32 @@ test_RSA4096() { rsa_tests RSA4096 } -test_ECC256() { - for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" ECCP256; done +ec_tests() { + for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" $1; done YPT -a status - for s in 9a 9c 9e; do PIVSignDec $s 1 s; done # 9a/9c/9e only do the ECDSA - PIVSignDec 9d 1 d # 9d only do the ECDH - out=$(pkcs15-tool --reader "$RDID" --read-certificate 01 | openssl x509 -text) - assertContains 'CERT' "$out" 'CN = CertAtSlot9a' + for s in 9a 9c 9d 9e; do + if [[ $1 != "X25519" ]]; then PIVSignDec $s 1 s; fi + if [[ $1 != "ED25519" ]]; then PIVSignDec $s 1 d; fi + done + if [[ $1 != *25519 ]]; then + out=$(pkcs15-tool --reader "$RDID" --read-certificate 01 | openssl x509 -text) + assertContains 'CERT' "$out" 'CN = CertAtSlot9a' + out=$(pkcs15-tool --reader "$RDID" --read-certificate 02 | openssl x509 -text) + assertContains 'CERT' "$out" 'CN = CertAtSlot9c' + fi +} + +test_ECC256() { + ec_tests ECCP256 } test_ECC384() { - for s in 9a 9c 9d 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" ECCP384; done - YPT -a status - for s in 9a 9c 9e; do PIVSignDec $s 1 s; done # 9a/9c/9e only do the ECDSA - PIVSignDec 9d 1 d # 9d only do the ECDH - out=$(pkcs15-tool --reader "$RDID" --read-certificate 02 | openssl x509 -text) - assertContains 'CERT' "$out" 'CN = CertAtSlot9c' + ec_tests ECCP384 } test_25519() { - for s in 9a 9c 9e; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" ED25519; done - for s in 9d; do PIVGenKeyCert $s "/CN=CertAtSlot$s/" X25519; done - YPT -a status - for s in 9a 9c 9e; do PIVSignDec $s 1 s; done # 9a/9c/9e only do the EDDSA - # PIVSignDec 9d 1 d # 9d only do the EDDH - # out=$(pkcs15-tool --reader "$RDID" --read-certificate 01 | openssl x509 -text) - # assertContains 'CERT' "$out" 'CN = CertAtSlot9a' + ec_tests ED25519 + ec_tests X25519 } test_PinBlock() { From 69495fc60762ec6c03defabe1c9639ea7d440dfa Mon Sep 17 00:00:00 2001 From: z4yx Date: Fri, 28 Jun 2024 13:08:17 +0800 Subject: [PATCH 4/5] fix tests with piv-go --- .github/workflows/tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a95d08f3..13063c04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -384,12 +384,16 @@ jobs: set -o xtrace go test -v test-via-pcsc/piv_test.go RDID="Canokey [OpenPGP PIV OATH] 00 00" + export PIV_EXT_AUTH_KEY=$PWD/test-via-pcsc/PIV_EXT_AUTH_KEY.txt yubico-piv-tool -r "$RDID" -a status -a set-ccc -a set-chuid -a status opensc-tool -r "$RDID" -s '00 F8 00 00' | grep 'SW1=0x90, SW2=0x00' # PIV_INS_GET_SERIAL, Yubico opensc-tool -r "$RDID" -s '00 FD 00 00' | grep 'SW1=0x90, SW2=0x00' # PIV_INS_GET_VERSION, Yubico pkcs15-tool --reader "$RDID" -D + # change the algorithm identifier of ED25519 + piv-tool --admin M:9B:03 -s '00 EE 02 00 07 01 22 05 51 52 53 54' | grep 'SW1=0x90, SW2=0x00' # PIV_INS_ALGORITHM_EXTENSION, Yubico cd piv-go; go test -v ./piv --wipe-yubikey; cd - + piv-tool --admin M:9B:03 -s '00 EE 02 00 07 01 E0 05 16 E1 53 54' | grep 'SW1=0x90, SW2=0x00' # PIV_INS_ALGORITHM_EXTENSION, Yubico yubico-piv-tool -r "$RDID" -a verify-pin -P 123456 yubico-piv-tool -r "$RDID" -a change-pin -P 123456 -N 654321 @@ -399,7 +403,6 @@ jobs: yubico-piv-tool -r "$RDID" -a verify-pin -P 654321 yubico-piv-tool -r "$RDID" -a set-mgm-key -n F1F2F3F4F5F6F7F8F1F2F3F4F5F6F7F8F1F2F3F4F5F6F7F8 yubico-piv-tool -r "$RDID" -a set-mgm-key --key=F1F2F3F4F5F6F7F8F1F2F3F4F5F6F7F8F1F2F3F4F5F6F7F8 -n 010203040506070801020304050607080102030405060708 - export PIV_EXT_AUTH_KEY=$PWD/test-via-pcsc/PIV_EXT_AUTH_KEY.txt # opensc 0.22.0~0.23.0 has a bug on External Auth. See opensc commit: a0aef25c7f2ce0ec2c7e1014f959f0fe86ff0479 piv-tool --reader "$RDID" --admin A:9B:03 # External Auth piv-tool --reader "$RDID" --admin M:9B:03 # Mutual Auth From 82d16abc5b321f656639180845d11aeca2859e71 Mon Sep 17 00:00:00 2001 From: Yuxiang Zhang Date: Sat, 29 Jun 2024 13:27:11 +0800 Subject: [PATCH 5/5] skip reseting algorithm extensions ID --- applets/piv/piv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applets/piv/piv.c b/applets/piv/piv.c index da1f2c1f..469f950e 100644 --- a/applets/piv/piv.c +++ b/applets/piv/piv.c @@ -253,7 +253,8 @@ int piv_install(const uint8_t reset) { if (write_attr(pin.path, TAG_PIN_KEY_DEFAULT, &tmp, sizeof(tmp)) < 0) return -1; if (pin_create(&puk, DEFAULT_PUK, 8, 3) < 0) return -1; if (write_attr(puk.path, TAG_PIN_KEY_DEFAULT, &tmp, sizeof(tmp)) < 0) return -1; - + + if (get_file_size(ALGORITHM_EXT_CONFIG_PATH) == sizeof(alg_ext_cfg)) return 0; // Algorithm extensions alg_ext_cfg.enabled = 1; alg_ext_cfg.ed25519 = ALG_ED25519_DEFAULT;