diff --git a/src/openpgp/cmd_get_data.c b/src/openpgp/cmd_get_data.c index ebce369..162de34 100644 --- a/src/openpgp/cmd_get_data.c +++ b/src/openpgp/cmd_get_data.c @@ -29,10 +29,20 @@ int cmd_get_data() { if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { return SW_REFERENCE_NOT_FOUND(); } - if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { + if (fid == EF_PRIV_DO_3) { + if (!has_pw2 && !has_pw3) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + } + else if (fid == EF_PRIV_DO_4) { + if (!has_pw3) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + } + else if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { return SW_SECURITY_STATUS_NOT_SATISFIED(); } - if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected + if (currentEF && currentEF->fid == fid) { // previously selected same EF ef = currentEF; } else { diff --git a/src/openpgp/cmd_put_data.c b/src/openpgp/cmd_put_data.c index 0636f30..0168468 100644 --- a/src/openpgp/cmd_put_data.c +++ b/src/openpgp/cmd_put_data.c @@ -32,11 +32,17 @@ int cmd_put_data() { if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { return SW_SECURITY_STATUS_NOT_SATISFIED(); } + if ((fid == EF_PRIV_DO_1 || fid == EF_PRIV_DO_3) && (!has_pw2 && !has_pw3)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if ((fid == EF_PRIV_DO_2 || fid == EF_PRIV_DO_4) && !has_pw3) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } if (fid == EF_PW_STATUS) { fid = EF_PW_PRIV; apdu.nc = 4; //we silently ommit the reset parameters } - if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected + if (currentEF && currentEF->fid == fid) { // previously selected same EF ef = currentEF; } if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) { diff --git a/src/openpgp/files.c b/src/openpgp/files.c index 51ed59c..4b296d5 100644 --- a/src/openpgp/files.c +++ b/src/openpgp/files.c @@ -55,12 +55,12 @@ uint8_t historical_bytes[] = { uint8_t extended_capabilities[] = { 10, 0, - 0x77, /* + 0x7f, /* * No Secure Messaging supported * GET CHALLENGE supported * Key import supported * PW status byte can be put - * No private_use_DO + * private_use_DO * Algorithm attrs are changable * ENC/DEC with AES * KDF-DO available @@ -68,7 +68,7 @@ uint8_t extended_capabilities[] = { 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ 0x00, 128, /* Max size of GET CHALLENGE */ 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ - 0x00, 0xff, + 0x08, 0x00, /* max. length of private DO (2KiB) */ 0x00, 0x1 }; @@ -476,10 +476,28 @@ file_t file_entries[] = { /* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 131 */ { .fid = EF_PRIV_DO_1, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 132 */ { .fid = EF_PRIV_DO_2, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 133 */ { .fid = EF_PRIV_DO_3, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 134 */ { .fid = EF_PRIV_DO_4, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 135 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 136 */ { .fid = EF_PW_STATUS, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, - /* 132 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, + /* 137 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, - /* 133 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, + /* 138 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, .ef_structure = 0, .acl = ACL_NONE } //end }; diff --git a/src/openpgp/files.h b/src/openpgp/files.h index 2246481..629a161 100644 --- a/src/openpgp/files.h +++ b/src/openpgp/files.h @@ -163,4 +163,9 @@ #define EF_DEV_CONF 0x1122 +#define EF_PRIV_DO_1 0x0101 +#define EF_PRIV_DO_2 0x0102 +#define EF_PRIV_DO_3 0x0103 +#define EF_PRIV_DO_4 0x0104 + #endif diff --git a/tests/card_test_empty_card.py b/tests/card_test_empty_card.py index 3ab24cb..c61fe07 100644 --- a/tests/card_test_empty_card.py +++ b/tests/card_test_empty_card.py @@ -201,7 +201,7 @@ def test_extended_capabilities(card): pytest.skip("Yubikey returns 6B00 when no key") else: a = get_data_object(card, 0xc0) - assert a == None or match(b'[\x70\x74\x75\x77]\x00\x00.[\x00\x08]\x00\x00\xff[\x00\x01][\x00\x01]', a) + assert a == None or match(b'[\x70\x7F\x75\x77]\x00\x00.[\x00\x08]\x00\x08\x00[\x00\x01][\x00\x01]', a) def test_key_attributes_1(card): if card.is_yubikey: diff --git a/tests/card_test_personalize_card_2.py b/tests/card_test_personalize_card_2.py index fbbfd66..8b72655 100644 --- a/tests/card_test_personalize_card_2.py +++ b/tests/card_test_personalize_card_2.py @@ -28,6 +28,26 @@ from card_const import * from constants_for_test import * import pytest +PRIVATE_DO_0101 = (0x01, 0x01) +PRIVATE_DO_0102 = (0x01, 0x02) +PRIVATE_DO_0103 = (0x01, 0x03) +PRIVATE_DO_0104 = (0x01, 0x04) + + +def _assert_sw(e, sw_list): + sw = e.args[0] + assert sw in sw_list + + +def _expect_security_error(callable_): + try: + callable_() + except ValueError as e: + _assert_sw(e, ["6982", "6985"]) + return + assert False + + class Test_Card_Personalize_Card_2(object): def test_verify_pw3_0(self, card): v = card.verify(3, PW3_TEST0) @@ -202,3 +222,112 @@ class Test_Card_Personalize_Card_2(object): def test_verify_pw3_2(self, card): v = card.verify(3, PW3_TEST0) assert v + + def test_private_do_0101_write_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw3_ok") + assert r + + def test_private_do_0101_write_fail_with_pw1_81(self, card): + card.cmd_select_openpgp() + v = card.verify(1, PW1_TEST4) + assert v + _expect_security_error( + lambda: card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw1_81") + ) + + def test_private_do_0101_write_ok_with_pw1_82(self, card): + card.cmd_select_openpgp() + v = card.verify(2, PW1_TEST4) + assert v + r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_ok") + assert r + + def test_private_do_0101_read_always(self, card): + card.cmd_select_openpgp() + data = get_data_object(card, 0x0101) + assert data == b"priv0101_ok" or data == b"priv0101_pw3_ok" + + def test_private_do_0102_write_fail_with_pw1(self, card): + card.cmd_select_openpgp() + v = card.verify(2, PW1_TEST4) + assert v + _expect_security_error( + lambda: card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_pw1") + ) + + def test_private_do_0102_write_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + r = card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_ok") + assert r + + def test_private_do_0102_read_always(self, card): + card.cmd_select_openpgp() + data = get_data_object(card, 0x0102) + assert data == b"priv0102_ok" + + def test_private_do_0103_read_fail_without_auth(self, card): + card.cmd_select_openpgp() + _expect_security_error(lambda: get_data_object(card, 0x0103)) + + def test_private_do_0103_read_fail_with_pw1_81(self, card): + card.cmd_select_openpgp() + v = card.verify(1, PW1_TEST4) + assert v + _expect_security_error(lambda: get_data_object(card, 0x0103)) + + def test_private_do_0103_write_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_pw3_ok") + assert r + + def test_private_do_0103_read_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + data = get_data_object(card, 0x0103) + assert data == b"priv0103_pw3_ok" + + def test_private_do_0103_write_ok_with_pw1_82(self, card): + card.cmd_select_openpgp() + v = card.verify(2, PW1_TEST4) + assert v + r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_ok") + assert r + + def test_private_do_0103_read_ok_with_pw1_82(self, card): + card.cmd_select_openpgp() + v = card.verify(2, PW1_TEST4) + assert v + data = get_data_object(card, 0x0103) + assert data == b"priv0103_ok" + + def test_private_do_0104_read_fail_without_auth(self, card): + card.cmd_select_openpgp() + _expect_security_error(lambda: get_data_object(card, 0x0104)) + + def test_private_do_0104_read_fail_with_pw1(self, card): + card.cmd_select_openpgp() + v = card.verify(2, PW1_TEST4) + assert v + _expect_security_error(lambda: get_data_object(card, 0x0104)) + + def test_private_do_0104_write_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + r = card.cmd_put_data(PRIVATE_DO_0104[0], PRIVATE_DO_0104[1], b"priv0104_ok") + assert r + + def test_private_do_0104_read_ok_with_pw3(self, card): + card.cmd_select_openpgp() + v = card.verify(3, PW3_TEST0) + assert v + data = get_data_object(card, 0x0104) + assert data == b"priv0104_ok"