diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e57754..9f0b4ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ name: "Emulation and test" on: workflow_dispatch: push: - branches: [ "main" ] + branches: [ "main", "piv" ] pull_request: # The branches below must be a subset of the branches above - branches: [ "main" ] + branches: [ "main", "piv" ] schedule: - cron: '23 5 * * 4' diff --git a/pico-keys-sdk b/pico-keys-sdk index 4d77ca7..3d0a27c 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 4d77ca7b75eff04bd401208054a83857844ecca4 +Subproject commit 3d0a27c834dfa05787c24ec6e8b32639a5c7ad66 diff --git a/src/openpgp/files.c b/src/openpgp/files.c index 6fd335b..1d185b6 100644 --- a/src/openpgp/files.c +++ b/src/openpgp/files.c @@ -39,6 +39,7 @@ extern int parse_algoinfo(const file_t *f, int mode); extern int parse_app_data(const file_t *f, int mode); extern int parse_discrete_do(const file_t *f, int mode); extern int parse_pw_status(const file_t *f, int mode); +extern int piv_parse_discovery(const file_t *f); uint8_t historical_bytes[] = { 10, 0, @@ -249,10 +250,236 @@ file_t file_entries[] = { /* 56 */ { .fid = EF_CH_3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE }, + // ** PIV ** // + /* 57 */ { .fid = EF_PIV_ADMIN_DATA, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 58 */ { .fid = EF_PIV_ATTESTATION, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 59 */ { .fid = EF_PIV_MSCMAP, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 60 */ { .fid = EF_PIV_MSROOTS1, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 61 */ { .fid = EF_PIV_MSROOTS2, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 62 */ { .fid = EF_PIV_MSROOTS3, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 63 */ { .fid = EF_PIV_MSROOTS4, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 64 */ { .fid = EF_PIV_MSROOTS5, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 65 */ { .fid = EF_PIV_KEY_AUTHENTICATION, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 66 */ { .fid = EF_PIV_KEY_CARDMGM, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 67 */ { .fid = EF_PIV_KEY_SIGNATURE, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 68 */ { .fid = EF_PIV_KEY_KEYMGM, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 69 */ { .fid = EF_PIV_KEY_CARDAUTH, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 70 */ { .fid = EF_PIV_KEY_RETIRED1, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 71 */ { .fid = EF_PIV_KEY_RETIRED2, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 72 */ { .fid = EF_PIV_KEY_RETIRED3, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 73 */ { .fid = EF_PIV_KEY_RETIRED4, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 74 */ { .fid = EF_PIV_KEY_RETIRED5, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 75 */ { .fid = EF_PIV_KEY_RETIRED6, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 76 */ { .fid = EF_PIV_KEY_RETIRED7, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 77 */ { .fid = EF_PIV_KEY_RETIRED8, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 78 */ { .fid = EF_PIV_KEY_RETIRED9, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 79 */ { .fid = EF_PIV_KEY_RETIRED10, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 80 */ { .fid = EF_PIV_KEY_RETIRED11, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 81 */ { .fid = EF_PIV_KEY_RETIRED12, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 82 */ { .fid = EF_PIV_KEY_RETIRED12, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 83 */ { .fid = EF_PIV_KEY_RETIRED13, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 84 */ { .fid = EF_PIV_KEY_RETIRED14, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 85 */ { .fid = EF_PIV_KEY_RETIRED15, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 86 */ { .fid = EF_PIV_KEY_RETIRED16, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 87 */ { .fid = EF_PIV_KEY_RETIRED17, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 88 */ { .fid = EF_PIV_KEY_RETIRED18, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 89 */ { .fid = EF_PIV_KEY_RETIRED19, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 90 */ { .fid = EF_PIV_KEY_RETIRED20, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 91 */ { .fid = EF_PIV_KEY_ATTESTATION, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 92 */ { .fid = EF_PIV_CAPABILITY, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 93 */ { .fid = EF_PIV_CHUID, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 94 */ { .fid = EF_PIV_AUTHENTICATION, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 95 */ { .fid = EF_PIV_FINGERPRINTS, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 96 */ { .fid = EF_PIV_SECURITY, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 97 */ { .fid = EF_PIV_FACIAL, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 98 */ { .fid = EF_PIV_PRINTED, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 99 */ { .fid = EF_PIV_SIGNATURE, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 100 */ { .fid = EF_PIV_KEY_MANAGEMENT, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 101 */ { .fid = EF_PIV_CARD_AUTH, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 102 */ { .fid = EF_PIV_DISCOVERY, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) piv_parse_discovery, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 103 */ { .fid = EF_PIV_KEY_HISTORY, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 104 */ { .fid = EF_PIV_IRIS, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 105 */ { .fid = EF_PIV_BITGT, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 106 */ { .fid = EF_PIV_SM_SIGNER, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 107 */ { .fid = EF_PIV_PC_REF_DATA, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 108 */ { .fid = EF_PIV_RETIRED1, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 109 */ { .fid = EF_PIV_RETIRED2, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 110 */ { .fid = EF_PIV_RETIRED3, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 111 */ { .fid = EF_PIV_RETIRED4, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 112 */ { .fid = EF_PIV_RETIRED5, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 113 */ { .fid = EF_PIV_RETIRED6, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 114 */ { .fid = EF_PIV_RETIRED7, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 115 */ { .fid = EF_PIV_RETIRED8, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 116 */ { .fid = EF_PIV_RETIRED9, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 117 */ { .fid = EF_PIV_RETIRED10, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 118 */ { .fid = EF_PIV_RETIRED11, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 119 */ { .fid = EF_PIV_RETIRED12, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 120 */ { .fid = EF_PIV_RETIRED13, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 121 */ { .fid = EF_PIV_RETIRED14, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 122 */ { .fid = EF_PIV_RETIRED15, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 123 */ { .fid = EF_PIV_RETIRED16, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 124 */ { .fid = EF_PIV_RETIRED17, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 125 */ { .fid = EF_PIV_RETIRED18, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 126 */ { .fid = EF_PIV_RETIRED19, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 127 */ { .fid = EF_PIV_RETIRED20, .parent = 0, .name = NULL, + .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, + /* 128 */ { .fid = EF_PIV_PIN, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 129 */ { .fid = EF_PIV_PUK, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP }, + /* 130 */ { .fid = EF_META, .parent = 0, .name = NULL, + .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, + .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE }, + /* 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 }, - /* 57 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, + /* 132 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, - /* 58 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, + /* 133 */ { .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 1918927..e75e1f2 100644 --- a/src/openpgp/files.h +++ b/src/openpgp/files.h @@ -28,6 +28,7 @@ #define EF_ALGO_PRIV2 0x10c2 #define EF_ALGO_PRIV3 0x10c3 #define EF_PW_PRIV 0x10c4 +#define EF_PW_RETRIES 0x10c5 #define EF_PK_SIG 0x10d1 #define EF_PK_DEC 0x10d2 #define EF_PK_AUT 0x10d3 @@ -81,4 +82,83 @@ #define EF_EXLEN_INFO 0x7f66 //C #define EF_GFM 0x7f74 //C +// PIV + +#define EF_PIV_PIN 0x1184 +#define EF_PIV_PUK 0x1185 + +#define EF_PIV_ADMIN_DATA 0xff00 +#define EF_PIV_ATTESTATION 0xff01 +#define EF_PIV_MSCMAP 0xff10 +#define EF_PIV_MSROOTS1 0xff11 +#define EF_PIV_MSROOTS2 0xff12 +#define EF_PIV_MSROOTS3 0xff13 +#define EF_PIV_MSROOTS4 0xff14 +#define EF_PIV_MSROOTS5 0xff15 + +#define EF_PIV_KEY_AUTHENTICATION 0x009a +#define EF_PIV_KEY_CARDMGM 0x009b +#define EF_PIV_KEY_SIGNATURE 0x009c +#define EF_PIV_KEY_KEYMGM 0x009d +#define EF_PIV_KEY_CARDAUTH 0x009e +#define EF_PIV_KEY_RETIRED1 0x0082 +#define EF_PIV_KEY_RETIRED2 0x0083 +#define EF_PIV_KEY_RETIRED3 0x0084 +#define EF_PIV_KEY_RETIRED4 0x0085 +#define EF_PIV_KEY_RETIRED5 0x0086 +#define EF_PIV_KEY_RETIRED6 0x0087 +#define EF_PIV_KEY_RETIRED7 0x0088 +#define EF_PIV_KEY_RETIRED8 0x0089 +#define EF_PIV_KEY_RETIRED9 0x008a +#define EF_PIV_KEY_RETIRED10 0x008b +#define EF_PIV_KEY_RETIRED11 0x008c +#define EF_PIV_KEY_RETIRED12 0x008d +#define EF_PIV_KEY_RETIRED13 0x008e +#define EF_PIV_KEY_RETIRED14 0x008f +#define EF_PIV_KEY_RETIRED15 0x0090 +#define EF_PIV_KEY_RETIRED16 0x0091 +#define EF_PIV_KEY_RETIRED17 0x0092 +#define EF_PIV_KEY_RETIRED18 0x0096 // It's 0x93 but assigned to EF_SIG_COUNT +#define EF_PIV_KEY_RETIRED19 0x0094 +#define EF_PIV_KEY_RETIRED20 0x0095 +#define EF_PIV_KEY_ATTESTATION 0x00fb // It's 0xf9 but assigned to EF_KDF + +#define EF_PIV_CAPABILITY 0xc107 +#define EF_PIV_CHUID 0xc102 +#define EF_PIV_AUTHENTICATION 0xc105 /* cert for 9a key */ +#define EF_PIV_FINGERPRINTS 0xc103 +#define EF_PIV_SECURITY 0xc106 +#define EF_PIV_FACIAL 0xc108 +#define EF_PIV_PRINTED 0xc109 +#define EF_PIV_SIGNATURE 0xc10a /* cert for 9c key */ +#define EF_PIV_KEY_MANAGEMENT 0xc10b /* cert for 9d key */ +#define EF_PIV_CARD_AUTH 0xc101 /* cert for 9e key */ +#define EF_PIV_DISCOVERY 0x007e +#define EF_PIV_KEY_HISTORY 0xc10c +#define EF_PIV_IRIS 0xc121 +#define EF_PIV_BITGT 0x7f61 +#define EF_PIV_SM_SIGNER 0xc122 +#define EF_PIV_PC_REF_DATA 0xc123 + +#define EF_PIV_RETIRED1 0xc10d +#define EF_PIV_RETIRED2 0xc10e +#define EF_PIV_RETIRED3 0xc10f +#define EF_PIV_RETIRED4 0xc110 +#define EF_PIV_RETIRED5 0xc111 +#define EF_PIV_RETIRED6 0xc112 +#define EF_PIV_RETIRED7 0xc113 +#define EF_PIV_RETIRED8 0xc114 +#define EF_PIV_RETIRED9 0xc115 +#define EF_PIV_RETIRED10 0xc116 +#define EF_PIV_RETIRED11 0xc117 +#define EF_PIV_RETIRED12 0xc118 +#define EF_PIV_RETIRED13 0xc119 +#define EF_PIV_RETIRED14 0xc11a +#define EF_PIV_RETIRED15 0xc11b +#define EF_PIV_RETIRED16 0xc11c +#define EF_PIV_RETIRED17 0xc11d +#define EF_PIV_RETIRED18 0xc11e +#define EF_PIV_RETIRED19 0xc11f +#define EF_PIV_RETIRED20 0xc120 + #endif diff --git a/src/openpgp/openpgp.c b/src/openpgp/openpgp.c index 5954611..884ddd9 100644 --- a/src/openpgp/openpgp.c +++ b/src/openpgp/openpgp.c @@ -22,8 +22,6 @@ #include "random.h" #include "eac.h" #include "crypto_utils.h" -#include "mbedtls/rsa.h" -#include "mbedtls/ecdsa.h" #include "mbedtls/ecdh.h" #include "mbedtls/asn1.h" #include "asn1.h" @@ -174,8 +172,32 @@ void scan_files() { memset((char *) ef->data + 12, 0, 4); #endif } - if ((ef = search_by_fid(EF_PW1, NULL, SPECIFY_ANY))) { + bool reset_dek = false; + if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) { if (!ef->data) { + printf("DEK is empty\r\n"); + const uint8_t def1[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; + const uint8_t def3[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; + + uint8_t def[IV_SIZE + 32 + 32 + 32]; + const uint8_t *dek = random_bytes_get(IV_SIZE + 32); + memcpy(def, dek, IV_SIZE + 32); + memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); + memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32); + hash_multi(def1, sizeof(def1), session_pw1); + aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); + memset(session_pw1, 0, sizeof(session_pw1)); + + hash_multi(def3, sizeof(def3), session_pw3); + aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32, 32); + aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32); + memset(session_pw3, 0, sizeof(session_pw3)); + flash_write_data_to_file(ef, def, sizeof(def)); + reset_dek = true; + } + } + if ((ef = search_by_fid(EF_PW1, NULL, SPECIFY_ANY))) { + if (!ef->data || reset_dek) { printf("PW1 is empty. Initializing with default password\r\n"); const uint8_t def[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; uint8_t dhash[33]; @@ -185,7 +207,7 @@ void scan_files() { } } if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) { - if (!ef->data) { + if (!ef->data || reset_dek) { printf("RC is empty. Initializing with default password\r\n"); const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; @@ -196,7 +218,7 @@ void scan_files() { } } if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) { - if (!ef->data) { + if (!ef->data || reset_dek) { printf("PW3 is empty. Initializing with default password\r\n"); const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; @@ -220,28 +242,6 @@ void scan_files() { flash_write_data_to_file(ef, def, sizeof(def)); } } - if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) { - if (!ef->data) { - printf("DEK is empty\r\n"); - const uint8_t def1[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; - const uint8_t def3[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; - - uint8_t def[IV_SIZE + 32 + 32 + 32]; - const uint8_t *dek = random_bytes_get(IV_SIZE + 32); - memcpy(def, dek, IV_SIZE + 32); - memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); - memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32); - hash_multi(def1, sizeof(def1), session_pw1); - aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32); - memset(session_pw1, 0, sizeof(session_pw1)); - - hash_multi(def3, sizeof(def3), session_pw3); - aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32, 32); - aes_encrypt_cfb_256(session_pw3, def, def + IV_SIZE + 32 + 32, 32); - memset(session_pw3, 0, sizeof(session_pw3)); - flash_write_data_to_file(ef, def, sizeof(def)); - } - } if ((ef = search_by_fid(EF_UIF_SIG, NULL, SPECIFY_ANY))) { if (!ef->data) { printf("UIF SIG is empty. Initializing to default\r\n"); @@ -270,7 +270,6 @@ void scan_files() { flash_write_data_to_file(ef, def, sizeof(def)); } } - if ((ef = search_by_fid(EF_SEX, NULL, SPECIFY_ANY))) { if (!ef->data) { printf("Sex is empty. Initializing to default\r\n"); @@ -278,11 +277,20 @@ void scan_files() { flash_write_data_to_file(ef, def, sizeof(def)); } } + if ((ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY))) { + if (!ef->data) { + printf("PW retries is empty. Initializing to default\r\n"); + const uint8_t def[] = { 0x1, 3, 3, 3 }; + flash_write_data_to_file(ef, def, sizeof(def)); + } + } low_flash_available(); } +extern bool has_pwpiv; +extern uint8_t session_pwpiv[32]; int load_dek() { - if (!has_pw1 && !has_pw2 && !has_pw3) { + if (!has_pw1 && !has_pw2 && !has_pw3 && !has_pwpiv) { return CCID_NO_LOGIN; } file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); @@ -299,6 +307,11 @@ int load_dek() { memcpy(dek + IV_SIZE, file_get_data(tf) + IV_SIZE + 32 + 32, 32); r = aes_decrypt_cfb_256(session_pw3, dek, dek + IV_SIZE, 32); } + else if (has_pwpiv) { + memcpy(dek, file_get_data(tf), IV_SIZE); + memcpy(dek + IV_SIZE, file_get_data(tf) + IV_SIZE + 32 + 32 + 32, 32); + r = aes_decrypt_cfb_256(session_pwpiv, dek, dek + IV_SIZE, 32); + } if (r != 0) { return CCID_EXEC_ERROR; } @@ -578,14 +591,6 @@ int parse_pw_status(const file_t *f, int mode) { return res_APDU_size - init_len; } -#define ALGO_RSA 0x01 -#define ALGO_ECDH 0x12 -#define ALGO_ECDSA 0x13 -#define ALGO_AES 0x70 -#define ALGO_AES_128 0x71 -#define ALGO_AES_192 0x72 -#define ALGO_AES_256 0x74 - #define ALGO_RSA_1K 0 #define ALGO_RSA_2k 1 #define ALGO_RSA_3K 2 @@ -820,8 +825,10 @@ static int cmd_get_data() { uint16_t data_len = parse_do(fids, 1); uint8_t *p = NULL; uint16_t tg = 0; - size_t tg_len = 0; - if (walk_tlv(res_APDU, data_len, &p, &tg, &tg_len, NULL)) { + uint16_t tg_len = 0; + asn1_ctx_t ctxi; + asn1_ctx_init(res_APDU, data_len, &ctxi); + if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) { uint8_t dec = 2; if ((tg & 0x1f) == 0x1f) { dec++; @@ -846,16 +853,21 @@ int pin_reset_retries(const file_t *pin, bool force) { return CCID_ERR_NULL_PARAM; } file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF); - if (!pw_status) { + file_t *pw_retries = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_EF); + if (!pw_status || !pw_retries) { return CCID_ERR_FILE_NOT_FOUND; } - uint8_t p[7]; - memcpy(p, file_get_data(pw_status), 7); - uint8_t retries = p[3 + (pin->fid & 0x3)]; + if (3 + (pin->fid & 0xf) >= file_get_size(pw_status) || (pin->fid & 0xf) >= file_get_size(pw_retries)) { + return CCID_ERR_MEMORY_FATAL; + } + uint8_t p[64]; + memcpy(p, file_get_data(pw_status), file_get_size(pw_status)); + uint8_t retries = p[3 + (pin->fid & 0xf)]; if (retries == 0 && force == false) { //blocked return CCID_ERR_BLOCKED; } - p[3 + (pin->fid & 0x3)] = 3; + uint8_t max_retries = file_get_data(pw_retries)[(pin->fid & 0xf)]; + p[3 + (pin->fid & 0xf)] = max_retries; int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status)); low_flash_available(); return r; @@ -869,19 +881,19 @@ int pin_wrong_retry(const file_t *pin) { if (!pw_status) { return CCID_ERR_FILE_NOT_FOUND; } - uint8_t p[7]; - memcpy(p, file_get_data(pw_status), 7); - if (p[3 + (pin->fid & 0x3)] > 0) { - p[3 + (pin->fid & 0x3)] -= 1; + uint8_t p[64]; + memcpy(p, file_get_data(pw_status), file_get_size(pw_status)); + if (p[3 + (pin->fid & 0xf)] > 0) { + p[3 + (pin->fid & 0xf)] -= 1; int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status)); if (r != CCID_OK) { return r; } low_flash_available(); - if (p[3 + (pin->fid & 0x3)] == 0) { + if (p[3 + (pin->fid & 0xf)] == 0) { return CCID_ERR_BLOCKED; } - return p[3 + (pin->fid & 0x3)]; + return p[3 + (pin->fid & 0xf)]; } return CCID_ERR_BLOCKED; } @@ -972,7 +984,7 @@ static int cmd_verify() { if (apdu.nc > 0) { return check_pin(pw, apdu.data, apdu.nc); } - uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0x3)); + uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0xf)); if (retries == 0) { return SW_PIN_BLOCKED(); } @@ -1020,7 +1032,7 @@ static int cmd_put_data() { if (!tf) { return SW_REFERENCE_NOT_FOUND(); } - uint8_t def[IV_SIZE + 32 + 32 + 32]; + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; memcpy(def, file_get_data(tf), file_get_size(tf)); hash_multi(apdu.data, apdu.nc, session_rc); memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); @@ -1065,7 +1077,7 @@ static int cmd_change_pin() { if (!tf) { return SW_REFERENCE_NOT_FOUND(); } - uint8_t def[IV_SIZE + 32 + 32 + 32]; + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; memcpy(def, file_get_data(tf), file_get_size(tf)); if (P2(apdu) == 0x81) { hash_multi(apdu.data + pin_len, apdu.nc - pin_len, session_pw1); @@ -1124,7 +1136,7 @@ static int cmd_reset_retry() { if (!tf) { return SW_REFERENCE_NOT_FOUND(); } - uint8_t def[IV_SIZE + 32 + 32 + 32]; + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; memcpy(def, file_get_data(tf), file_get_size(tf)); hash_multi(apdu.data + (apdu.nc - newpin_len), newpin_len, session_pw1); memcpy(def + IV_SIZE, dek + IV_SIZE, 32); @@ -1144,7 +1156,7 @@ static int cmd_reset_retry() { return SW_INCORRECT_P1P2(); } -int store_keys(void *key_ctx, int type, uint16_t key_id) { +int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek) { int r, key_size = 0; uint8_t kdata[4096 / 8]; //worst @@ -1182,9 +1194,11 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) { } memcpy(kdata, key_ctx, key_size); } - r = dek_encrypt(kdata, key_size); - if (r != CCID_OK) { - return r; + if (use_kek) { + r = dek_encrypt(kdata, key_size); + if (r != CCID_OK) { + return r; + } } //r = aes_encrypt_cfb_256(file_read(pw3->data+2), session_pw3, kdata, key_size); //if (r != CCID_OK) @@ -1197,11 +1211,11 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) { return CCID_OK; } -int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) { +int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek) { int key_size = file_get_size(fkey); uint8_t kdata[4096 / 8]; memcpy(kdata, file_get_data(fkey), key_size); - if (dek_decrypt(kdata, key_size) != 0) { + if (use_dek && dek_decrypt(kdata, key_size) != 0) { return CCID_EXEC_ERROR; } if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size / 2) != 0) { @@ -1231,11 +1245,11 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) { return CCID_OK; } -int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) { +int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey, bool use_dek) { int key_size = file_get_size(fkey); uint8_t kdata[67]; //Worst case, 521 bit + 1byte memcpy(kdata, file_get_data(fkey), key_size); - if (dek_decrypt(kdata, key_size) != 0) { + if (use_dek && dek_decrypt(kdata, key_size) != 0) { return CCID_EXEC_ERROR; } mbedtls_ecp_group_id gid = kdata[0]; @@ -1244,6 +1258,12 @@ int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) { mbedtls_ecdsa_free(ctx); return CCID_EXEC_ERROR; } + mbedtls_platform_zeroize(kdata, sizeof(kdata)); + r = mbedtls_ecp_mul(&ctx->grp, &ctx->Q, &ctx->d, &ctx->grp.G, random_gen, NULL); + if (r != 0) { + mbedtls_ecdsa_free(ctx); + return CCID_EXEC_ERROR; + } return CCID_OK; } @@ -1376,7 +1396,7 @@ static int cmd_keypair_gen() { mbedtls_rsa_free(&rsa); return SW_EXEC_ERROR(); } - r = store_keys(&rsa, ALGO_RSA, fid); + r = store_keys(&rsa, ALGO_RSA, fid, true); make_rsa_response(&rsa); mbedtls_rsa_free(&rsa); if (r != CCID_OK) { @@ -1397,7 +1417,7 @@ static int cmd_keypair_gen() { mbedtls_ecdsa_free(&ecdsa); return SW_EXEC_ERROR(); } - r = store_keys(&ecdsa, algo[0], fid); + r = store_keys(&ecdsa, algo[0], fid, true); make_ecdsa_response(&ecdsa); mbedtls_ecdsa_free(&ecdsa); if (r != CCID_OK) { @@ -1424,7 +1444,7 @@ static int cmd_keypair_gen() { uint8_t aes_key[32]; //maximum AES key size uint8_t key_size = 32; memcpy(aes_key, random_bytes_get(key_size), key_size); - r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY); + r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY, true); /* if storing the key fails, we silently continue */ //if (r != CCID_OK) // return SW_EXEC_ERROR(); @@ -1452,7 +1472,7 @@ int rsa_sign(mbedtls_rsa_context *ctx, uint8_t *d = (uint8_t *) data, *end = d + data_len, *hsh = NULL; size_t seq_len = 0, hash_len = 0; int key_size = ctx->len, r = 0; - mbedtls_md_type_t md = MBEDTLS_MD_SHA256; + mbedtls_md_type_t md = MBEDTLS_MD_NONE; if (mbedtls_asn1_get_tag(&d, end, &seq_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0) { mbedtls_asn1_buf mdb; @@ -1608,7 +1628,7 @@ static int cmd_pso() { if (algo[0] == ALGO_RSA) { mbedtls_rsa_context ctx; mbedtls_rsa_init(&ctx); - r = load_private_key_rsa(&ctx, ef); + r = load_private_key_rsa(&ctx, ef, true); if (r != CCID_OK) { mbedtls_rsa_free(&ctx); return SW_EXEC_ERROR(); @@ -1647,7 +1667,7 @@ static int cmd_pso() { if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) { mbedtls_ecdsa_context ctx; mbedtls_ecdsa_init(&ctx); - r = load_private_key_ecdsa(&ctx, ef); + r = load_private_key_ecdsa(&ctx, ef, true); if (r != CCID_OK) { mbedtls_ecdsa_free(&ctx); return SW_EXEC_ERROR(); @@ -1777,7 +1797,7 @@ static int cmd_internal_aut() { if (algo[0] == ALGO_RSA) { mbedtls_rsa_context ctx; mbedtls_rsa_init(&ctx); - r = load_private_key_rsa(&ctx, ef); + r = load_private_key_rsa(&ctx, ef, true); if (r != CCID_OK) { mbedtls_rsa_free(&ctx); return SW_EXEC_ERROR(); @@ -1793,7 +1813,7 @@ static int cmd_internal_aut() { else if (algo[0] == ALGO_ECDH || algo[0] == ALGO_ECDSA) { mbedtls_ecdsa_context ctx; mbedtls_ecdsa_init(&ctx); - r = load_private_key_ecdsa(&ctx, ef); + r = load_private_key_ecdsa(&ctx, ef, true); if (r != CCID_OK) { mbedtls_ecdsa_free(&ctx); return SW_EXEC_ERROR(); @@ -1964,7 +1984,7 @@ static int cmd_import_data() { mbedtls_rsa_free(&rsa); return SW_EXEC_ERROR(); } - r = store_keys(&rsa, ALGO_RSA, fid); + r = store_keys(&rsa, ALGO_RSA, fid, true); make_rsa_response(&rsa); mbedtls_rsa_free(&rsa); if (r != CCID_OK) { @@ -1997,7 +2017,7 @@ static int cmd_import_data() { mbedtls_ecdsa_free(&ecdsa); return SW_EXEC_ERROR(); } - r = store_keys(&ecdsa, ALGO_ECDSA, fid); + r = store_keys(&ecdsa, ALGO_ECDSA, fid, true); make_ecdsa_response(&ecdsa); mbedtls_ecdsa_free(&ecdsa); if (r != CCID_OK) { diff --git a/src/openpgp/openpgp.h b/src/openpgp/openpgp.h index 07a4f94..be5126c 100644 --- a/src/openpgp/openpgp.h +++ b/src/openpgp/openpgp.h @@ -25,8 +25,35 @@ #include "pico_keys.h" #include "apdu.h" +#include "mbedtls/rsa.h" +#include "mbedtls/ecdsa.h" extern bool has_pw1; extern bool has_pw3; +extern int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek); +extern void make_rsa_response(mbedtls_rsa_context *rsa); +extern void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa); +extern int ecdsa_sign(mbedtls_ecdsa_context *ctx, + const uint8_t *data, + size_t data_len, + uint8_t *out, + size_t *out_len); +extern int rsa_sign(mbedtls_rsa_context *ctx, + const uint8_t *data, + size_t data_len, + uint8_t *out, + size_t *out_len); +extern int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek); +extern int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey, bool use_dek); +extern int pin_reset_retries(const file_t *pin, bool force); + +#define ALGO_RSA 0x01 +#define ALGO_ECDH 0x12 +#define ALGO_ECDSA 0x13 +#define ALGO_AES 0x70 +#define ALGO_AES_128 0x71 +#define ALGO_AES_192 0x72 +#define ALGO_AES_256 0x74 + #endif diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c index 2b48376..e605a83 100644 --- a/src/openpgp/piv.c +++ b/src/openpgp/piv.c @@ -19,11 +19,48 @@ #include "files.h" #include "apdu.h" #include "pico_keys.h" +#include "random.h" #include "eac.h" +#include "crypto_utils.h" #include "version.h" +#ifndef ENABLE_EMULATION #include "pico/unique_id.h" +#endif +#include "asn1.h" +#include "mbedtls/aes.h" +#include "mbedtls/des.h" +#include "mbedtls/x509_crt.h" +#include "openpgp.h" -extern bool has_pw1; +#define PIV_ALGO_3DES 0x03 +#define PIV_ALGO_AES128 0x08 +#define PIV_ALGO_AES192 0x0a +#define PIV_ALGO_AES256 0x0c +#define PIV_ALGO_RSA1024 0x06 +#define PIV_ALGO_RSA2048 0x07 +#define PIV_ALGO_RSA3072 0x05 +#define PIV_ALGO_RSA4096 0x16 +#define PIV_ALGO_ECCP256 0x11 +#define PIV_ALGO_ECCP384 0x14 +#define PIV_ALGO_X25519 0xE1 + +#define PINPOLICY_DEFAULT 0 +#define PINPOLICY_NEVER 1 +#define PINPOLICY_ONCE 2 +#define PINPOLICY_ALWAYS 3 + +#define TOUCHPOLICY_DEFAULT 0 +#define TOUCHPOLICY_NEVER 1 +#define TOUCHPOLICY_ALWAYS 2 +#define TOUCHPOLICY_CACHED 3 +#define TOUCHPOLICY_AUTO 0xFF + +#define ORIGIN_GENERATED 0x01 +#define ORIGIN_IMPORTED 0x02 + +#define IS_RETIRED(x) ((x) >= EF_PIV_KEY_RETIRED1 && (x) <= EF_PIV_KEY_RETIRED20) +#define IS_ACTIVE(x) ((x) >= EF_PIV_KEY_AUTHENTICATION && (x) <= EF_PIV_KEY_CARDAUTH) +#define IS_KEY(x) ((IS_ACTIVE((x))) || (IS_RETIRED((x)))) uint8_t piv_aid[] = { 5, @@ -38,15 +75,205 @@ uint8_t mgmt_aid[] = { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; +bool has_pwpiv = false; +uint8_t session_pwpiv[32]; + int piv_process_apdu(); +static int get_serial() { +#ifndef ENABLE_EMULATION + pico_unique_board_id_t unique_id; + pico_get_unique_board_id(&unique_id); + uint32_t serial = (unique_id.id[0] & 7F) << 24 | unique_id.id[1] << 16 | unique_id.id[2] << 8 | unique_id.id[3]; + return serial; +#else + return 0; +#endif +} + +static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attestation, uint8_t *buffer, size_t buffer_size) { + mbedtls_x509write_cert ctx; + mbedtls_x509write_crt_init(&ctx); + mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3); + mbedtls_x509write_crt_set_validity(&ctx, "20240325000000", "20741231235959"); + uint8_t serial[20]; + random_gen(NULL, serial, sizeof(serial)); + mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial)); + mbedtls_pk_context skey, ikey; + mbedtls_ecdsa_context actx; // attestation key + mbedtls_pk_init(&skey); + mbedtls_pk_init(&ikey); + if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048) { + mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); + } + else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) { + mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + } + skey.pk_ctx = pk_ctx; + mbedtls_x509write_crt_set_subject_key(&ctx, &skey); + char buf_sname[256]; + if (attestation) { + sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Attestation %X", slot); + mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname); + mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot F9"); + file_t *ef_key = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_EF); + mbedtls_ecdsa_init(&actx); + load_private_key_ecdsa(&actx, ef_key, false); + mbedtls_pk_setup(&ikey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + ikey.pk_ctx = &actx; + mbedtls_x509write_crt_set_issuer_key(&ctx, &ikey); + uint8_t ver[] = {PIV_VERSION_MAJOR, PIV_VERSION_MINOR, 0}; + mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x03", 10, 0, ver, sizeof(ver)); + uint32_t serial = get_serial(); + mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x07", 10, 0, (const uint8_t *)&serial, sizeof(serial)); + int meta_len = 0; + uint8_t *meta; + if ((meta_len = meta_find(slot, &meta)) >= 0) { + mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x08", 10, 0, &meta[1], 2); + } + uint8_t v = 1; + mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x09", 10, 0, &v, sizeof(serial)); + } + else { + uint8_t wslot = slot; + if (slot == EF_PIV_KEY_ATTESTATION) { + wslot = 0xF9; + } + else if (slot == EF_PIV_KEY_RETIRED18) { + wslot = 0x93; + } + sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot %X", wslot); + mbedtls_x509write_crt_set_issuer_name(&ctx, buf_sname); + mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname); + mbedtls_x509write_crt_set_issuer_key(&ctx, &skey); + } + if (algo == PIV_ALGO_ECCP384) { + mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA384); + } + else { + mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA256); + } + if (slot == EF_PIV_KEY_ATTESTATION) { + mbedtls_x509write_crt_set_basic_constraints(&ctx, 1, 1); + } + else { + mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0); + } + mbedtls_x509write_crt_set_subject_key_identifier(&ctx); + mbedtls_x509write_crt_set_authority_key_identifier(&ctx); + mbedtls_x509write_crt_set_key_usage(&ctx, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | + MBEDTLS_X509_KU_KEY_CERT_SIGN); + int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL); + /* skey cannot be freed, as it is freed later */ + if (attestation) { + mbedtls_ecdsa_free(&actx); + } + return ret; +} + static void scan_files() { scan_flash(); + file_t *ef = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_EF); + if ((ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY))) { + if (file_get_size(ef) == 0) { + printf("PW status is empty. Initializing to default\r\n"); + const uint8_t def[] = { 0x1, 127, 127, 127, 3, 3, 3, 3, 3 }; + flash_write_data_to_file(ef, def, sizeof(def)); + } + else if (file_get_size(ef) == 7) { + printf("PW status is older. Initializing to default\r\n"); + uint8_t def[9] = { 0 }; + memcpy(def, file_get_data(ef), 7); + def[7] = def[8] = 3; // PIV retries + flash_write_data_to_file(ef, def, sizeof(def)); + } + } + if ((ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY))) { + if (file_get_size(ef) == 0) { + printf("PW retries is empty. Initializing to default\r\n"); + const uint8_t def[] = { 0x1, 3, 3, 3, 3, 3 }; + flash_write_data_to_file(ef, def, sizeof(def)); + } + else if (file_get_size(ef) == 4) { + printf("PW retries is older. Initializing to default\r\n"); + uint8_t def[6] = { 0 }; + memcpy(def, file_get_data(ef), 4); + def[4] = def[5] = 3; // PIV retries + flash_write_data_to_file(ef, def, sizeof(def)); + } + } + bool reset_dek = false; + if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) { + if (file_get_size(ef) == 0 || file_get_size(ef) == IV_SIZE+32*3) { + printf("DEK is empty or older\r\n"); + const uint8_t defpin[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; + const uint8_t *dek = random_bytes_get(IV_SIZE + 32); + uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; + if (file_get_size(ef) > 0) { + memcpy(def, file_get_data(ef), file_get_size(ef)); + } + else { + memcpy(def, dek, IV_SIZE); + } + memcpy(def + IV_SIZE + 32*3, dek + IV_SIZE, 32); + hash_multi(defpin, sizeof(defpin), session_pwpiv); + aes_encrypt_cfb_256(session_pwpiv, def, def + IV_SIZE + 32*3, 32); + flash_write_data_to_file(ef, def, sizeof(def)); + + has_pwpiv = true; + uint8_t *key = (uint8_t *)"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"; + file_t *ef = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, key, 24); + uint8_t meta[] = { PIV_ALGO_AES192, PINPOLICY_ALWAYS, TOUCHPOLICY_ALWAYS, ORIGIN_GENERATED }; + meta_add(EF_PIV_KEY_CARDMGM, meta, sizeof(meta)); + has_pwpiv = false; + memset(session_pwpiv, 0, sizeof(session_pwpiv)); + + reset_dek = true; + } + } + if ((ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY))) { + if (!ef->data || reset_dek) { + printf("PIV PIN is empty. Initializing with default password\r\n"); + const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF }; + uint8_t dhash[33]; + dhash[0] = sizeof(def); + double_hash_pin(def, sizeof(def), dhash + 1); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + } + } + if ((ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY))) { + if (!ef->data) { + printf("PIV PUK is empty. Initializing with default password\r\n"); + const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; + uint8_t dhash[33]; + dhash[0] = sizeof(def); + double_hash_pin(def, sizeof(def), dhash + 1); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + } + } + if ((ef = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_ANY))) { + if (!ef->data) { + printf("ATTESTATION key is empty. Initializing with random one\r\n"); + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int r = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP384R1, random_gen, NULL); + r = store_keys(&ecdsa, ALGO_ECDSA, EF_PIV_KEY_ATTESTATION, false); + uint8_t cert[2048]; + r = x509_create_cert(&ecdsa, PIV_ALGO_ECCP384, EF_PIV_KEY_ATTESTATION, false, cert, sizeof(cert)); + ef = search_by_fid(EF_PIV_ATTESTATION, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + mbedtls_ecdsa_free(&ecdsa); + } + } + low_flash_available(); } void init_piv() { scan_files(); - //cmd_select(); + has_pwpiv = false; + // cmd_select(); } int piv_unload() { @@ -111,11 +338,18 @@ static int cmd_select() { return SW_OK(); } +int piv_parse_discovery(const file_t *ef) { + memcpy(res_APDU, "\x7E\x12\x4F\x0B\xA0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00\x5F\x2F\x02\x40\x10", 20); + res_APDU_size = 20; + return res_APDU_size; +} + static int cmd_get_serial() { - pico_unique_board_id_t unique_id; - pico_get_unique_board_id(&unique_id); - memcpy(res_APDU, unique_id.id, 4); - res_APDU_size = 4; + uint32_t serial = get_serial(); + res_APDU[res_APDU_size++] = serial >> 24; + res_APDU[res_APDU_size++] = serial >> 16; + res_APDU[res_APDU_size++] = serial >> 8; + res_APDU[res_APDU_size++] = serial & 0xFF; return SW_OK(); } @@ -129,10 +363,7 @@ static int cmd_verify() { return SW_REFERENCE_NOT_FOUND(); } file_t *pw, *pw_status; - uint16_t fid = 0x0; - if (key_ref == 0x80) { - fid = EF_PW1; - } + uint16_t fid = EF_PIV_PIN; if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF))) { return SW_REFERENCE_NOT_FOUND(); } @@ -143,29 +374,929 @@ static int cmd_verify() { return SW_REFERENCE_NOT_FOUND(); } if (apdu.nc > 0) { - return check_pin(pw, apdu.data, apdu.nc); + uint16_t ret = check_pin(pw, apdu.data, apdu.nc); + if (ret == 0x9000) { + has_pwpiv = true; + hash_multi(apdu.data, apdu.nc, session_pwpiv); + } + return ret; //SW already set } - uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0x3)); + uint8_t retries = *(file_get_data(pw_status) + 3 + (fid & 0xf)); if (retries == 0) { return SW_PIN_BLOCKED(); } - if ((key_ref == 0x80 && has_pw1)) { + if ((key_ref == 0x80 && has_pwpiv)) { return SW_OK(); } return set_res_sw(0x63, 0xc0 | retries); } +static int cmd_get_data() { + if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) { + return SW_INCORRECT_P1P2(); + } + if (apdu.data[0] != 0x5C || (apdu.data[1] & 0x80) || apdu.data[1] >= 4 || apdu.data[1] == 0) { + return SW_WRONG_DATA(); + } + uint32_t fid = apdu.data[2]; + for (uint8_t lt = 1; lt < apdu.data[1]; lt++) { + fid <<= 8; + fid |= apdu.data[2 + lt]; + } + if ((fid & 0xFFFF00) != 0x5FC100 && (fid & 0xFFFF) != EF_PIV_BITGT && (fid & 0xFFFF) != EF_PIV_DISCOVERY && (fid & 0xFFFF) != EF_PIV_ATTESTATION) { + return SW_REFERENCE_NOT_FOUND(); + } + file_t *ef = NULL; + if ((ef = search_by_fid((uint16_t)(fid & 0xFFFF), NULL, SPECIFY_EF))) { + uint16_t data_len = 0; + res_APDU_size = 2; // Minimum: TAG+LEN + if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) { + data_len = ((int (*)(const file_t *))(ef->data))((const file_t *) ef); + } + else { + if (ef->data) { + data_len = file_get_size(ef); + memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len); + } + } + if (data_len == 0) { + return SW_FILE_NOT_FOUND(); + } + if (data_len > 255) { + memmove(res_APDU + res_APDU_size + 2, res_APDU + res_APDU_size, data_len); + } + else if (data_len > 127) { + memmove(res_APDU + res_APDU_size + 1, res_APDU + res_APDU_size, data_len); + } + res_APDU[0] = 0x53; + res_APDU_size = 1 + format_tlv_len(data_len, res_APDU + 1) + data_len; + } + else { + return SW_FILE_NOT_FOUND(); + } + return SW_OK(); +} + +static int cmd_get_metadata() { + if (P1(apdu) != 0x00) { + return SW_INCORRECT_P1P2(); + } + uint8_t *meta = NULL; + uint16_t key_ref = P2(apdu); + if (key_ref == 0x80) { + key_ref = EF_PIV_PIN; + } + else if (key_ref == 0x81) { + key_ref = EF_PIV_PUK; + } + file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_key)) { + return SW_MEMORY_FAILURE(); + } + if (key_ref != EF_PIV_PIN && key_ref != EF_PIV_PUK) { + int meta_len = 0; + if ((meta_len = meta_find(key_ref, &meta)) <= 0) { + return SW_REFERENCE_NOT_FOUND(); + } + res_APDU[res_APDU_size++] = 0x1; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = meta[0]; + res_APDU[res_APDU_size++] = 0x2; + res_APDU[res_APDU_size++] = 2; + res_APDU[res_APDU_size++] = meta[1]; + res_APDU[res_APDU_size++] = meta[2]; + res_APDU[res_APDU_size++] = 0x3; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = meta[3]; + if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048 || meta[0] == PIV_ALGO_RSA3072 || meta[0] == PIV_ALGO_RSA4096 || meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) { + res_APDU[res_APDU_size++] = 0x4; + if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048 || meta[0] == PIV_ALGO_RSA3072 || meta[0] == PIV_ALGO_RSA4096) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + int r = load_private_key_rsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + res_APDU[res_APDU_size++] = 0x81; + res_APDU[res_APDU_size++] = 0x82; + put_uint16_t(mbedtls_mpi_size(&ctx.N), res_APDU + res_APDU_size); res_APDU_size += 2; + mbedtls_mpi_write_binary(&ctx.N, res_APDU + res_APDU_size, mbedtls_mpi_size(&ctx.N)); + res_APDU_size += mbedtls_mpi_size(&ctx.N); + res_APDU[res_APDU_size++] = 0x82; + res_APDU[res_APDU_size++] = mbedtls_mpi_size(&ctx.E) & 0xff; + mbedtls_mpi_write_binary(&ctx.E, res_APDU + res_APDU_size, mbedtls_mpi_size(&ctx.E)); + res_APDU_size += mbedtls_mpi_size(&ctx.E); + mbedtls_rsa_free(&ctx); + } + else { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + int r = load_private_key_ecdsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + uint8_t pt[MBEDTLS_ECP_MAX_PT_LEN]; + size_t plen = 0; + mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &plen, pt, sizeof(pt)); + mbedtls_ecdsa_free(&ctx); + res_APDU[res_APDU_size++] = 0x86; + if (plen >= 128) { + res_APDU[res_APDU_size++] = 0x81; + } + res_APDU[res_APDU_size++] = plen; + memcpy(res_APDU + res_APDU_size, pt, plen); + res_APDU_size += plen; + } + } + } + if (key_ref == EF_PIV_PIN || key_ref == EF_PIV_PUK || key_ref == EF_PIV_KEY_MANAGEMENT) { + uint8_t dhash[32]; + int32_t eq = false; + if (key_ref == EF_PIV_PIN) { + double_hash_pin((const uint8_t *)"\x31\x32\x33\x34\x35\x36\xFF\xFF", 8, dhash); + eq = memcmp(dhash, file_get_data(ef_key) + 1, file_get_size(ef_key) - 1); + } + else if (key_ref == EF_PIV_PUK) { + double_hash_pin((const uint8_t *)"\x31\x32\x33\x34\x35\x36\x37\x38", 8, dhash); + eq = memcmp(dhash, file_get_data(ef_key) + 1, file_get_size(ef_key) - 1); + } + else if (key_ref == EF_PIV_KEY_MANAGEMENT) { + eq = memcmp("\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08", file_get_data(ef_key), file_get_size(ef_key)); + } + res_APDU[res_APDU_size++] = 0x5; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = eq; + if (key_ref == EF_PIV_PIN || key_ref == EF_PIV_PUK) { + file_t *pw_status; + if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t retries = *(file_get_data(pw_status) + 3 + (key_ref & 0xf)); + res_APDU[res_APDU_size++] = 0x6; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = retries; + } + } + return SW_OK(); +} +uint8_t challenge[16]; +bool has_challenge = false; +bool has_mgm = false; +static int cmd_authenticate() { + uint8_t algo = P1(apdu), key_ref = P2(apdu); + if (apdu.nc == 0) { + return SW_WRONG_LENGTH(); + } + if (apdu.data[0] != 0x7C) { + return SW_WRONG_DATA(); + } + if (key_ref == EF_PIV_KEY_CARDMGM) { + if (algo != PIV_ALGO_AES128 && algo != PIV_ALGO_AES192 && algo != PIV_ALGO_AES256 && algo != PIV_ALGO_3DES) { + return SW_INCORRECT_P1P2(); + } + file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_mgm)) { + return SW_MEMORY_FAILURE(); + } + uint16_t mgm_len = file_get_size(ef_mgm); + if ((algo == PIV_ALGO_AES128 && mgm_len != 16) || (algo == PIV_ALGO_AES192 && mgm_len != 24) || (algo == PIV_ALGO_AES256 && mgm_len != 32) || (algo == PIV_ALGO_3DES && mgm_len != 24)) { + return SW_INCORRECT_P1P2(); + } + } + uint8_t *meta = NULL; + int meta_len = 0; + if ((meta_len = meta_find(key_ref, &meta)) <= 0) { + return SW_REFERENCE_NOT_FOUND(); + } + if (meta[1] == PINPOLICY_DEFAULT) { + if (key_ref == EF_PIV_KEY_SIGNATURE) { + meta[1] = PINPOLICY_ALWAYS; + } + else { + meta[1] = PINPOLICY_ONCE; + } + } + if ((meta[1] == PINPOLICY_ALWAYS || meta[1] == PINPOLICY_ONCE) && (!has_pwpiv && (key_ref == EF_PIV_KEY_AUTHENTICATION || key_ref == EF_PIV_KEY_SIGNATURE || key_ref == EF_PIV_KEY_KEYMGM || key_ref == EF_PIV_KEY_CARDAUTH || IS_RETIRED(key_ref)))) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + uint8_t chal_len = (algo == PIV_ALGO_3DES ? sizeof(challenge) / 2 : sizeof(challenge)); + asn1_ctx_t ctxi, a7c = { 0 }; + asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); + if (!asn1_find_tag(&ctxi, 0x7C, &a7c) || asn1_len(&ctxi) == 0) { + return SW_WRONG_DATA(); + } + asn1_ctx_t a80 = { 0 }, a81 = { 0 }, a82 = { 0 }; + asn1_find_tag(&a7c, 0x80, &a80); + asn1_find_tag(&a7c, 0x81, &a81); + asn1_find_tag(&a7c, 0x82, &a82); + if (a80.data) { + if (a80.len == 0) { + memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge)); + if (algo == PIV_ALGO_AES128 || algo == PIV_ALGO_AES192 || algo == PIV_ALGO_AES256 || algo == PIV_ALGO_3DES) { + if (key_ref != EF_PIV_KEY_CARDMGM) { + return SW_INCORRECT_P1P2(); + } + file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_mgm)) { + return SW_MEMORY_FAILURE(); + } + uint16_t mgm_len = file_get_size(ef_mgm); + res_APDU[res_APDU_size++] = 0x7C; + res_APDU[res_APDU_size++] = 18; + res_APDU[res_APDU_size++] = 0x80; + res_APDU[res_APDU_size++] = 16; + int r = 0; + if (algo == PIV_ALGO_3DES) { + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + r = mbedtls_des3_set3key_enc(&ctx, file_get_data(ef_mgm)); + if (r != 0) { + mbedtls_des3_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_des3_crypt_ecb(&ctx, challenge, res_APDU + res_APDU_size); + res_APDU_size += 8; + mbedtls_des3_free(&ctx); + } + else { + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_mgm), mgm_len * 8); + if (r != 0) { + mbedtls_aes_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU + res_APDU_size); + res_APDU_size += 16; + mbedtls_aes_free(&ctx); + } + if (r != 0) { + return SW_EXEC_ERROR(); + } + has_challenge = true; + } + } + else { + if (!has_challenge) { + return SW_INCORRECT_PARAMS(); + } + if (!asn1_len(&a81)) { + return SW_INCORRECT_PARAMS(); + } + if (key_ref != EF_PIV_KEY_CARDMGM) { + return SW_INCORRECT_P1P2(); + } + if (memcmp(a80.data, challenge, a80.len) == 0) { + has_mgm = true; + } + } + } + if (a81.data) { + if (!a81.len) { + memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge)); + res_APDU[res_APDU_size++] = 0x7C; + res_APDU[res_APDU_size++] = chal_len + 2; + res_APDU[res_APDU_size++] = 0x81; + res_APDU[res_APDU_size++] = chal_len; + memcpy(res_APDU + res_APDU_size, challenge, chal_len); + res_APDU_size += chal_len; + has_challenge = true; + } + else { + file_t *ef_key = search_by_fid(key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_key)) { + return SW_MEMORY_FAILURE(); + } + if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048 || algo == PIV_ALGO_RSA3072 || algo == PIV_ALGO_RSA4096) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + int r = load_private_key_rsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = file_get_size(ef_key); + if (algo == PIV_ALGO_RSA1024) { + memcpy(res_APDU, "\x7C\x81\x00\x82\x81\x00", 6); + res_APDU_size = 6; + } + else { + memcpy(res_APDU, "\x7C\x82\x00\x00\x82\x82\x00\x00", 8); + res_APDU_size = 8; + } + r = mbedtls_rsa_private(&ctx, random_gen, NULL, a81.data, res_APDU + res_APDU_size); + mbedtls_rsa_free(&ctx); + if (algo == PIV_ALGO_RSA1024) { + res_APDU[res_APDU_size - 1] = olen; + res_APDU[res_APDU_size - 4] = olen + 3; + } + else { + res_APDU[res_APDU_size - 2] = olen >> 8; + res_APDU[res_APDU_size - 1] = olen & 0xFF; + res_APDU[res_APDU_size - 6] = (olen + 4) >> 8; + res_APDU[res_APDU_size - 5] = (olen + 4) & 0xFF; + } + res_APDU_size += olen; + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + int r = load_private_key_ecdsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + memcpy(res_APDU, "\x7C\x00\x82\x00", 4); + res_APDU_size = 4; + r = mbedtls_ecdsa_write_signature(&ctx, algo == PIV_ALGO_ECCP256 ? MBEDTLS_MD_SHA256 : MBEDTLS_MD_SHA384, a81.data, a81.len, res_APDU + res_APDU_size, MBEDTLS_ECDSA_MAX_LEN, &olen, random_gen, NULL); + mbedtls_ecdsa_free(&ctx); + res_APDU[res_APDU_size - 1] = olen; + res_APDU[res_APDU_size - 3] = olen + 2; + res_APDU_size += olen; + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + else if (algo == PIV_ALGO_AES128 || algo == PIV_ALGO_AES192 || algo == PIV_ALGO_AES256 || algo == PIV_ALGO_3DES) { + uint16_t key_len = file_get_size(ef_key); + memcpy(res_APDU, "\x7C\x12\x82\x10", 4); + res_APDU_size = 4; + int r = 0; + if (algo == PIV_ALGO_3DES) { + if (a81.len % 8 != 0) { + return SW_DATA_INVALID(); + } + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + r = mbedtls_des3_set3key_enc(&ctx, file_get_data(ef_key)); + if (r != 0) { + mbedtls_des3_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_des3_crypt_ecb(&ctx, a81.data, res_APDU + res_APDU_size); + mbedtls_des3_free(&ctx); + res_APDU_size += 8; + } + else { + if (a81.len % 16 != 0) { + return SW_DATA_INVALID(); + } + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_key), key_len * 8); + if (r != 0) { + mbedtls_aes_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, a81.data, res_APDU + res_APDU_size); + mbedtls_aes_free(&ctx); + res_APDU_size += 16; + } + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + } + } + if (a82.data) { + if (!a82.len) { + // Should be handled by a81 or a80 + } + else { + if (key_ref != EF_PIV_KEY_CARDMGM) { + return SW_INCORRECT_P1P2(); + } + if (!has_challenge) { + return SW_INCORRECT_PARAMS(); + } + if (chal_len != a82.len) { + return SW_DATA_INVALID(); + } + file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_key)) { + return SW_MEMORY_FAILURE(); + } + uint16_t key_len = file_get_size(ef_key); + int r = 0; + if (algo == PIV_ALGO_3DES) + { + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + r = mbedtls_des3_set3key_dec(&ctx, file_get_data(ef_key)); + if (r != 0) { + mbedtls_des3_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_des3_crypt_ecb(&ctx, a82.data, res_APDU); + mbedtls_des3_free(&ctx); + } + else { + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + r = mbedtls_aes_setkey_dec(&ctx, file_get_data(ef_key), key_len * 8); + if (r != 0) { + mbedtls_aes_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, a82.data, res_APDU); + mbedtls_aes_free(&ctx); + } + if (r != 0) { + return SW_EXEC_ERROR(); + } + if (memcmp(res_APDU, challenge, chal_len) != 0) { + return SW_DATA_INVALID(); + } + } + } + if (meta[1] == PINPOLICY_ALWAYS) { + has_pwpiv = false; + } + return SW_OK(); +} + +static int cmd_asym_keygen() { + uint8_t key_ref = P2(apdu); + if (apdu.nc == 0) { + return SW_WRONG_LENGTH(); + } + if (apdu.data[0] != 0xAC) { + return SW_WRONG_DATA(); + } + if (P1(apdu) != 0x0) { + return SW_INCORRECT_P1P2(); + } + if (!has_mgm) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if (key_ref != EF_PIV_KEY_AUTHENTICATION && key_ref != EF_PIV_KEY_SIGNATURE && key_ref != EF_PIV_KEY_KEYMGM && key_ref != EF_PIV_KEY_CARDAUTH && !(key_ref >= EF_PIV_KEY_RETIRED1 && key_ref <= EF_PIV_KEY_RETIRED20)) { + return SW_INCORRECT_P1P2(); + } + asn1_ctx_t ctxi, aac = {0}; + asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); + if (!asn1_find_tag(&ctxi, 0xAC, &aac) || asn1_len(&aac) == 0) { + return SW_WRONG_DATA(); + } + asn1_ctx_t a80 = {0}, aaa = {0}, aab = {0}; + asn1_find_tag(&aac, 0x80, &a80); + asn1_find_tag(&aac, 0xAA, &aaa); + asn1_find_tag(&aac, 0xAB, &aab); + if (asn1_len(&a80) == 0) { + return SW_WRONG_DATA(); + } + uint16_t key_cert = 0; + if (key_ref == EF_PIV_KEY_AUTHENTICATION) { + key_cert = EF_PIV_AUTHENTICATION; + } + else if (key_ref == EF_PIV_KEY_SIGNATURE) { + key_cert = EF_PIV_SIGNATURE; + } + else if (key_ref == EF_PIV_KEY_KEYMGM) { + key_cert = EF_PIV_KEY_MANAGEMENT; + } + else if (key_ref == EF_PIV_KEY_CARDAUTH) { + key_cert = EF_PIV_CARD_AUTH; + } + else { + key_cert = key_ref + 0xC08B; + } + if (a80.data[0] == PIV_ALGO_RSA1024 || a80.data[0] == PIV_ALGO_RSA2048) { + printf("KEYPAIR RSA\r\n"); + asn1_ctx_t a81 = {0}; + asn1_find_tag(&aac, 0x81, &a81); + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa); + int exponent = 65537, nlen = (a80.data[0] == PIV_ALGO_RSA1024 ? 1024 : 2048); + if (asn1_len(&a81)) { + exponent = (int)asn1_get_uint(&a81); + } + int r = mbedtls_rsa_gen_key(&rsa, random_gen, NULL, nlen, exponent); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + make_rsa_response(&rsa); + uint8_t cert[2048]; + r = x509_create_cert(&rsa, a80.data[0], key_ref, false, cert, sizeof(cert)); + file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + r = store_keys(&rsa, ALGO_RSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false); + mbedtls_rsa_free(&rsa); + if (r != CCID_OK) { + return SW_EXEC_ERROR(); + } + } + else if (a80.data[0] == PIV_ALGO_ECCP256 || a80.data[0] == PIV_ALGO_ECCP384) { + printf("KEYPAIR ECDSA\r\n"); + mbedtls_ecp_group_id gid = a80.data[0] == PIV_ALGO_ECCP256 ? MBEDTLS_ECP_DP_SECP256R1 : MBEDTLS_ECP_DP_SECP384R1; + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, NULL); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + make_ecdsa_response(&ecdsa); + uint8_t cert[2048]; + r = x509_create_cert(&ecdsa, a80.data[0], key_ref, false, cert, sizeof(cert)); + file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + r = store_keys(&ecdsa, ALGO_ECDSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false); + mbedtls_ecdsa_free(&ecdsa); + if (r != CCID_OK) { + return SW_EXEC_ERROR(); + } + } + else if (a80.data[0] == PIV_ALGO_X25519) { + } + else { + return SW_DATA_INVALID(); + } + uint8_t def_pinpol = PINPOLICY_ONCE; + if (key_ref == EF_PIV_KEY_SIGNATURE) { + def_pinpol = PINPOLICY_ALWAYS; + } + uint8_t meta[] = {a80.data[0], asn1_len(&aaa) ? aaa.data[0] : def_pinpol, asn1_len(&aab) ? aab.data[0] : TOUCHPOLICY_ALWAYS, ORIGIN_GENERATED}; + meta_add(key_ref, meta, sizeof(meta)); + low_flash_available(); + return SW_OK(); +} + +int cmd_put_data() { + if (P1(apdu) != 0x3F || P2(apdu) != 0xFF) { + return SW_INCORRECT_P1P2(); + } + asn1_ctx_t ctxi, a5c = {0}, a53 = {0}; + asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); + if (apdu.data[0] != 0x7E && apdu.data[0] != 0x7F && (!asn1_find_tag(&ctxi, 0x5C, &a5c) || !asn1_find_tag(&ctxi, 0x53, &a53))) { + return SW_WRONG_DATA(); + } + if (a5c.data && a53.data) { + if (a5c.len != 3 || a5c.data[0] != 0x5F || a5c.data[1] != 0xC1) { + return SW_WRONG_DATA(); + } + uint16_t fid = (a5c.data[1] << 8 | a5c.data[2]); + file_t *ef = search_by_fid(fid, NULL, SPECIFY_EF); + if (!ef) { + return SW_MEMORY_FAILURE(); + } + if (a53.len > 0) { + flash_write_data_to_file(ef, a53.data, a53.len); + } + else { + flash_clear_file(ef); + } + low_flash_available(); + } + return SW_OK(); +} + +static int cmd_set_mgmkey() { + if (P1(apdu) != 0xFF) { + return SW_INCORRECT_P1P2(); + } + if (apdu.nc < 5) { + return SW_WRONG_LENGTH(); + } + uint8_t touch = P2(apdu); + if (touch != 0xFF && touch != 0xFE) { + if (touch == 0xFF) { + touch = TOUCHPOLICY_NEVER; + } + else if (touch == 0xFE) { + touch = TOUCHPOLICY_ALWAYS; + } + } + uint8_t algo = apdu.data[0], key_ref = apdu.data[1], pinlen = apdu.data[2]; + if ((key_ref != EF_PIV_KEY_CARDMGM) || (!(algo == PIV_ALGO_AES128 && pinlen == 16) && !(algo == PIV_ALGO_AES192 && pinlen == 24) && !(algo == PIV_ALGO_AES256 && pinlen == 32) && !(algo == PIV_ALGO_3DES && pinlen == 24))) { + return SW_WRONG_DATA(); + } + file_t *ef = search_by_fid(key_ref, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, apdu.data + 3, pinlen); + uint8_t *meta = NULL, new_meta[4]; + int meta_len = 0; + if ((meta_len = meta_find(key_ref, &meta)) <= 0) { + return SW_REFERENCE_NOT_FOUND(); + } + memcpy(new_meta, meta, 4); + new_meta[0] = algo; + new_meta[2] = touch; + meta_add(key_ref, new_meta, sizeof(new_meta)); + low_flash_available(); + return SW_OK(); +} + +static int cmd_move_key() { + if (apdu.nc != 0) { + return SW_WRONG_LENGTH(); + } + uint8_t to = P1(apdu), from = P2(apdu); + if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) { + return SW_INCORRECT_P1P2(); + } + if (from == 0x93) { + from = EF_PIV_KEY_RETIRED18; + } + if (to == 0x93) { + to = EF_PIV_KEY_RETIRED18; + } + file_t *efs, *efd; + if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) { + return SW_FILE_NOT_FOUND(); + } + if (to != 0xFF) { + flash_write_data_to_file(efd, file_get_data(efs), file_get_size(efs)); + } + flash_clear_file(efs); + low_flash_available(); + return SW_OK(); +} + +static int cmd_change_pin() { + uint8_t pin_ref = P2(apdu); + if (P1(apdu) != 0x0 || (pin_ref != 0x80 && pin_ref != 0x81)) { + return SW_INCORRECT_P1P2(); + } + file_t *ef = search_by_fid(pin_ref == 0x80 ? EF_PIV_PIN : EF_PIV_PUK, NULL, SPECIFY_ANY); + if (!ef) { + return SW_MEMORY_FAILURE(); + } + uint8_t *pin_data = file_get_data(ef), pin_len = apdu.nc - pin_data[0]; + uint16_t ret = check_pin(ef, apdu.data, pin_data[0]); + if (ret != 0x9000) { + return ret; + } + uint8_t dhash[33]; + dhash[0] = pin_len; + double_hash_pin(apdu.data + pin_data[0], pin_len, dhash + 1); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + low_flash_available(); + return SW_OK(); +} + +static int cmd_reset_retry() { + if (P1(apdu) != 0x0 || P2(apdu) != 0x80) { + return SW_INCORRECT_P1P2(); + } + file_t *ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY); + if (!ef) { + return SW_MEMORY_FAILURE(); + } + uint8_t *puk_data = file_get_data(ef), pin_len = apdu.nc - puk_data[0]; + uint16_t ret = check_pin(ef, apdu.data, puk_data[0]); + if (ret != 0x9000) { + return ret; + } + uint8_t dhash[33]; + dhash[0] = pin_len; + double_hash_pin(apdu.data + puk_data[0], pin_len, dhash + 1); + ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + pin_reset_retries(ef, true); + low_flash_available(); + return SW_OK(); +} + +static int cmd_set_retries() { + file_t *ef = search_by_fid(EF_PW_RETRIES, NULL, SPECIFY_ANY); + if (!ef) { + return SW_MEMORY_FAILURE(); + } + uint8_t *tmp = (uint8_t *)calloc(1, file_get_size(ef)); + memcpy(tmp, file_get_data(ef), file_get_size(ef)); + tmp[4] = P1(apdu); + tmp[5] = P2(apdu); + flash_write_data_to_file(ef, tmp, file_get_size(ef)); + free(tmp); + + ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY); + const uint8_t def_pin[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF }; + uint8_t dhash[33]; + dhash[0] = sizeof(def_pin); + double_hash_pin(def_pin, sizeof(def_pin), dhash + 1); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + pin_reset_retries(ef, true); + + ef = search_by_fid(EF_PIV_PUK, NULL, SPECIFY_ANY); + const uint8_t def_puk[8] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}; + dhash[0] = sizeof(def_puk); + double_hash_pin(def_puk, sizeof(def_puk), dhash + 1); + flash_write_data_to_file(ef, dhash, sizeof(dhash)); + pin_reset_retries(ef, true); + + low_flash_available(); + return SW_OK(); +} + +static int cmd_reset() { + if (P1(apdu) != 0x0 || P2(apdu) != 0x0) { + return SW_INCORRECT_P1P2(); + } + file_t *pw_status; + if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF))) + { + return SW_REFERENCE_NOT_FOUND(); + } + uint8_t retPIN = *(file_get_data(pw_status) + 3 + (EF_PIV_PIN & 0xf)), retPUK = *(file_get_data(pw_status) + 3 + (EF_PIV_PUK & 0xf)); + if (retPIN != 0 || retPUK != 0) { + return SW_INCORRECT_PARAMS(); + } + initialize_flash(true); + low_flash_available(); + init_piv(); + return SW_OK(); +} + +static int cmd_attestation() { + uint8_t key_ref = P1(apdu); + if (P2(apdu) != 0x00) { + return SW_INCORRECT_P1P2(); + } + if (!IS_KEY(key_ref)) { + return SW_REFERENCE_NOT_FOUND(); + } + file_t *ef_key = NULL; + int meta_len = 0; + uint8_t *meta = NULL; + if (!(ef_key = search_by_fid(key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, NULL, SPECIFY_EF)) || !file_has_data(ef_key)) { + return SW_REFERENCE_NOT_FOUND(); + } + if ((meta_len = meta_find(key_ref, &meta)) <= 0) { + return SW_REFERENCE_NOT_FOUND(); + } + if (meta[3] != ORIGIN_GENERATED) { + return SW_INCORRECT_PARAMS(); + } + int r = 0; + if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + r = load_private_key_rsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); + mbedtls_rsa_free(&ctx); + } + else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + r = load_private_key_ecdsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); + mbedtls_ecdsa_free(&ctx); + } + else { + return SW_WRONG_DATA(); + } + if (r <= 0) { + return SW_EXEC_ERROR(); + } + memmove(res_APDU, res_APDU + 2048 - r, r); + res_APDU_size = r; + return SW_OK(); +} + +static int cmd_import_asym() { + uint8_t algo = P1(apdu), key_ref = P2(apdu); + if (key_ref != EF_PIV_KEY_AUTHENTICATION && key_ref != EF_PIV_KEY_SIGNATURE && key_ref != EF_PIV_KEY_KEYMGM && key_ref != EF_PIV_KEY_CARDAUTH && !(key_ref >= EF_PIV_KEY_RETIRED1 && key_ref <= EF_PIV_KEY_RETIRED20)) { + return SW_INCORRECT_P1P2(); + } + asn1_ctx_t ctxi, aaa = {0}, aab = {0}; + asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); + asn1_find_tag(&ctxi, 0xAA, &aaa); + asn1_find_tag(&ctxi, 0xAB, &aab); + if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048 || algo == PIV_ALGO_RSA3072 || algo == PIV_ALGO_RSA4096) { + asn1_ctx_t a1 = { 0 }, a2 = { 0 }; + asn1_find_tag(&ctxi, 0x01, &a1); + asn1_find_tag(&ctxi, 0x02, &a2); + if (asn1_len(&a1) <= 0 || asn1_len(&a2) <= 0) { + return SW_WRONG_DATA(); + } + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa); + int r = mbedtls_mpi_read_binary(&rsa.P, a1.data, a1.len); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_WRONG_DATA(); + } + r = mbedtls_mpi_read_binary(&rsa.Q, a2.data, a2.len); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_WRONG_DATA(); + } + int exponent = 65537; + mbedtls_mpi_lset(&rsa.E, exponent); + r = mbedtls_rsa_import(&rsa, NULL, &rsa.P, &rsa.Q, NULL, &rsa.E); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_rsa_complete(&rsa); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_rsa_check_privkey(&rsa); + if (r != 0) { + mbedtls_rsa_free(&rsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&rsa, ALGO_RSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false); + mbedtls_rsa_free(&rsa); + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) { + asn1_ctx_t a6 = {0}; + asn1_find_tag(&ctxi, 0x06, &a6); + if (asn1_len(&a6) <= 0) { + return SW_WRONG_DATA(); + } + mbedtls_ecp_group_id gid = algo == PIV_ALGO_ECCP256 ? MBEDTLS_ECP_DP_SECP256R1 : MBEDTLS_ECP_DP_SECP384R1; + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int r = mbedtls_ecp_read_key(gid, &ecdsa, a6.data, a6.len); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_ecp_mul(&ecdsa.grp, &ecdsa.Q, &ecdsa.d, &ecdsa.grp.G, random_gen, NULL); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = mbedtls_ecp_check_pub_priv(&ecdsa, &ecdsa, random_gen, NULL); + if (r != 0) { + mbedtls_ecdsa_free(&ecdsa); + return SW_EXEC_ERROR(); + } + r = store_keys(&ecdsa, ALGO_ECDSA, key_ref == 0x93 ? EF_PIV_KEY_RETIRED18 : key_ref, false); + mbedtls_ecdsa_free(&ecdsa); + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + else { + return SW_WRONG_DATA(); + } + uint8_t def_pinpol = PINPOLICY_ONCE; + if (key_ref == EF_PIV_KEY_SIGNATURE) { + def_pinpol = PINPOLICY_ALWAYS; + } + uint8_t meta[] = { algo, asn1_len(&aaa) ? aaa.data[0] : def_pinpol, asn1_len(&aab) ? aab.data[0] : TOUCHPOLICY_ALWAYS, ORIGIN_IMPORTED }; + meta_add(key_ref, meta, sizeof(meta)); + return SW_OK(); +} + #define INS_VERIFY 0x20 #define INS_VERSION 0xFD #define INS_SELECT 0xA4 #define INS_YK_SERIAL 0xF8 #define INS_VERIFY 0x20 +#define INS_GET_DATA 0xCB +#define INS_GET_METADATA 0xF7 +#define INS_AUTHENTICATE 0x87 +#define INS_ASYM_KEYGEN 0x47 +#define INS_PUT_DATA 0xDB +#define INS_SET_MGMKEY 0xFF +#define INS_MOVE_KEY 0xF6 +#define INS_CHANGE_PIN 0x24 +#define INS_RESET_RETRY 0x2C +#define INS_SET_RETRIES 0xFA +#define INS_RESET 0xFB +#define INS_ATTESTATION 0xF9 +#define INS_IMPORT_ASYM 0xFE static const cmd_t cmds[] = { { INS_VERSION, cmd_version }, { INS_SELECT, cmd_select }, { INS_YK_SERIAL, cmd_get_serial }, { INS_VERIFY, cmd_verify }, + { INS_GET_DATA, cmd_get_data }, + { INS_GET_METADATA, cmd_get_metadata }, + { INS_AUTHENTICATE, cmd_authenticate }, + { INS_ASYM_KEYGEN, cmd_asym_keygen }, + { INS_PUT_DATA, cmd_put_data }, + { INS_SET_MGMKEY, cmd_set_mgmkey }, + { INS_MOVE_KEY, cmd_move_key }, + { INS_CHANGE_PIN, cmd_change_pin }, + { INS_RESET_RETRY, cmd_reset_retry }, + { INS_SET_RETRIES, cmd_set_retries }, + { INS_RESET, cmd_reset }, + { INS_ATTESTATION, cmd_attestation }, + { INS_IMPORT_ASYM, cmd_import_asym }, { 0x00, 0x0 } }; diff --git a/src/openpgp/version.h b/src/openpgp/version.h index d0d70f8..5266146 100644 --- a/src/openpgp/version.h +++ b/src/openpgp/version.h @@ -29,7 +29,7 @@ #define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff) #define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff) -#define PIV_VERSION 0x0500 +#define PIV_VERSION 0x0507 #define PIV_VERSION_MAJOR ((PIV_VERSION >> 8) & 0xff) #define PIV_VERSION_MINOR (PIV_VERSION & 0xff) diff --git a/tests/docker/jammy/Dockerfile b/tests/docker/jammy/Dockerfile index b6d1092..677968e 100644 --- a/tests/docker/jammy/Dockerfile +++ b/tests/docker/jammy/Dockerfile @@ -22,6 +22,20 @@ RUN apt install -y libccid \ cmake \ vsmartcard-vpcd \ libgcrypt-dev \ + libssl-dev \ + check \ + gengetopt \ && rm -rf /var/lib/apt/lists/* RUN pip3 install pytest pycvc cryptography pyscard +RUN git clone https://github.com/Yubico/yubico-piv-tool +WORKDIR /yubico-piv-tool +RUN git checkout tags/yubico-piv-tool-2.5.1 +ADD tests/docker/jammy/yubico-piv-tool.patch /yubico-piv-tool/yubico-piv-tool.patch +RUN git apply yubico-piv-tool.patch +RUN mkdir build +WORKDIR /yubico-piv-tool/build +RUN cmake .. -DENABLE_HARDWARE_TESTS=1 +RUN make -j`nproc` +RUN make install WORKDIR / +RUN ldconfig diff --git a/tests/docker/jammy/yubico-piv-tool.patch b/tests/docker/jammy/yubico-piv-tool.patch new file mode 100644 index 0000000..028cdf0 --- /dev/null +++ b/tests/docker/jammy/yubico-piv-tool.patch @@ -0,0 +1,68 @@ +diff --git a/lib/tests/api.c b/lib/tests/api.c +index fb7c1a8..b569ec3 100644 +--- a/lib/tests/api.c ++++ b/lib/tests/api.c +@@ -515,7 +515,7 @@ START_TEST(test_pin_policy_always) { + unsigned char rand[128] = {0}; + + size_t sig_len = sizeof(signature); +- size_t padlen = 256; ++ size_t padlen = 512; + unsigned int enc_len; + unsigned int data_len; + +@@ -1009,8 +1009,8 @@ END_TEST + START_TEST(test_pin_cache) { + ykpiv_rc res; + ykpiv_state *local_state; +- unsigned char data[256] = {0}; +- unsigned char data_in[256] = {0}; ++ unsigned char data[512] = {0}; ++ unsigned char data_in[512] = {0}; + int len = sizeof(data); + size_t len2 = sizeof(data); + +@@ -1028,17 +1028,17 @@ START_TEST(test_pin_cache) { + ck_assert_int_eq(res, YKPIV_OK); + + // Verify decryption does not work without auth +- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a); + ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR); + + // Verify decryption does work when authed + res = ykpiv_verify_select(g_state, "123456", 6, NULL, true); + ck_assert_int_eq(res, YKPIV_OK); +- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a); + ck_assert_int_eq(res, YKPIV_OK); + + // Verify PIN policy allows continuing to decrypt without re-verifying +- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a); + ck_assert_int_eq(res, YKPIV_OK); + + // Create a new ykpiv state, connect, and close it. +@@ -1059,7 +1059,7 @@ START_TEST(test_pin_cache) { + // + // Note that you can verify that this fails by rebuilding with + // DISABLE_PIN_CACHE set to 1. +- res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a); ++ res = ykpiv_decipher_data(g_state, data_in, (size_t)len, data, &len2, YKPIV_ALGO_RSA4096, 0x9a); + ck_assert_int_eq(res, YKPIV_OK); + } + END_TEST +diff --git a/tools/confirm.sh b/tools/confirm.sh +index 81c10ac..4ab15c5 100755 +--- a/tools/confirm.sh ++++ b/tools/confirm.sh +@@ -20,7 +20,8 @@ echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WA + echo "******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* ******* *******" >&0 + echo >&0 + echo -n "Are you SURE you wish to proceed? If so, type 'CONFIRM': " >&0 +- ++echo "0" ++exit 0 + read CONFIRM + if [[ "x$CONFIRM" != "xCONFIRM" ]]; then + echo "1" diff --git a/tests/docker_env.sh b/tests/docker_env.sh index c11fcb0..27cca93 100644 --- a/tests/docker_env.sh +++ b/tests/docker_env.sh @@ -49,7 +49,7 @@ : ${MBEDTLS_DOCKER_GUEST:=jammy} -DOCKER_IMAGE_TAG="pico-hsm-test:${MBEDTLS_DOCKER_GUEST}" +DOCKER_IMAGE_TAG="pico-openpgp-test:${MBEDTLS_DOCKER_GUEST}" # Make sure docker is available if ! which docker > /dev/null; then @@ -79,7 +79,7 @@ ${DOCKER} image build \ --cache-from=${DOCKER_IMAGE_TAG} \ --network host \ --build-arg MAKEFLAGS_PARALLEL="-j ${NUM_PROC}" \ - tests/docker/${MBEDTLS_DOCKER_GUEST} + -f tests/docker/${MBEDTLS_DOCKER_GUEST}/Dockerfile . run_in_docker() { diff --git a/tests/scripts/attestation.sh b/tests/scripts/attestation.sh new file mode 100755 index 0000000..e797bbe --- /dev/null +++ b/tests/scripts/attestation.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +source ./tests/scripts/func.sh + +echo -n " Fetch attestation certificate... " +piv read-cert -sf9 -o sf9.pem +test $? -eq 0 && echo -e ".\t${OK}" || exit $? + +algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384") +slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95") +for alg in ${algs[*]}; do + for slot in ${slots[*]}; do + echo " Test attestation with ${alg} in slot ${slot}" + echo -n " Keygen... " + gen_and_check $alg $slot && echo -e ".\t${OK}" || exit $? + + echo -n " Fetch attesting certificate... " + piv attest -s$slot -o attestation.pem + test $? -eq 0 && echo -e ".\t${OK}" || exit $? + + echo -n " OpenSSL verify attestation... " + e=$(openssl verify -CAfile sf9.pem attestation.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q ": OK" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " Key deletion... " + delete_key $alg $slot && echo -e ".\t${OK}" || exit $? + + done +done + +rm -rf cert.pem +rm -rf sf9.pem diff --git a/tests/scripts/cli-test.sh b/tests/scripts/cli-test.sh new file mode 100755 index 0000000..e03f36a --- /dev/null +++ b/tests/scripts/cli-test.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +chmod a+x tests/scripts/*.sh + +echo "======== CLI Test suite ========" +./tests/scripts/yubico-piv-tool.sh diff --git a/tests/scripts/func.sh b/tests/scripts/func.sh new file mode 100755 index 0000000..ef17273 --- /dev/null +++ b/tests/scripts/func.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +OK="\033[32mok\033[0m" +FAIL="\033[31mfail\033[0m" + +READER="u" + +piv() { + yubico-piv-tool -r${READER} -a$@ +} + +gen_and_check() { + e=$(piv generate -s$2 -A$1 -opublic.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q "Successfully generated a new private key" <<< $e && echo -n "." || exit $? + e=$(piv status 2>&1) + e=${e//$'\t'/} + e=${e//$'\n'/} + test $? -eq 0 && echo -n "." || exit $? + grep -q "Slot $2:Algorithm:$1" <<< $e && echo -n "." || exit $? +} +delete_key() { + piv delete-key -s$2 > /dev/null 2>&1 + test $? -eq 0 && echo -n "." || exit $? + piv delete-cert -s$2 > /dev/null 2>&1 + test $? -eq 0 && echo -n "." || exit $? + e=$(piv status 2>&1) + test $? -eq 0 && echo -n "." || exit $? + q=$(grep -q "Slot $2: Algorithm: $1" <<< $e) + test $? -eq 1 && echo -n "." || exit $? + rm -rf public.pem +} +gen_and_delete() { + gen_and_check $1 $2 + test $? -eq 0 && echo -n "." || exit $? + delete_key $1 $2 + test $? -eq 0 && echo -n "." || exit $? +} diff --git a/tests/scripts/keygen.sh b/tests/scripts/keygen.sh new file mode 100755 index 0000000..4edfc93 --- /dev/null +++ b/tests/scripts/keygen.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +source ./tests/scripts/func.sh + +algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384") +slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95") +for alg in ${algs[*]}; do + for slot in ${slots[*]}; do + echo -n " Test ${alg} in slot ${slot}... " + gen_and_delete ${alg} $slot && echo -e ".\t${OK}" || exit $? + done +done diff --git a/tests/scripts/signatures.sh b/tests/scripts/signatures.sh new file mode 100755 index 0000000..3a93c02 --- /dev/null +++ b/tests/scripts/signatures.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +source ./tests/scripts/func.sh + +algs=("RSA1024" "RSA2048" "ECCP256" "ECCP384") +slots=("9a" "9c" "9d" "9e" "82" "83" "84" "85" "86" "87" "88" "89" "8a" "8b" "8c" "8d" "8e" "8f" "90" "91" "92" "93" "94" "95") +for alg in ${algs[*]}; do + for slot in ${slots[*]}; do + echo " Test signature with ${alg} in slot ${slot}" + echo -n " Keygen... " + gen_and_check $alg $slot && echo -e ".\t${OK}" || exit $? + + echo -n " Test request certificate... " + e=$(piv verify -arequest -P123456 -s$slot -S'/CN=bar/OU=test/O=example.com/' -ipublic.pem -ocert.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q "Successfully verified PIN" <<< $e && echo -n "." || exit $? + grep -q "Successfully generated a certificate request" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " OpenSSL verify request... " + e=$(openssl req -verify -in cert.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q " OK" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " Test self-signed certificate... " + e=$(piv verify -aselfsign -P123456 -s$slot -S'/CN=bar/OU=test/O=example.com/' -ipublic.pem -ocert.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q "Successfully verified PIN" <<< $e && echo -n "." || exit $? + grep -q "Successfully generated a new self signed certificate" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " Test signature... " + e=$(piv verify-pin -atest-signature -s$slot -P123456 -icert.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q "Successful" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " OpenSSL verify cert... " + e=$(openssl verify -CAfile cert.pem cert.pem 2>&1) + test $? -eq 0 && echo -n "." || exit $? + grep -q ": OK" <<< $e && echo -e ".\t${OK}" || exit $? + + echo -n " Key deletion... " + delete_key $alg $slot && echo -e ".\t${OK}" || exit $? + + done +done + +rm -rf cert.pem diff --git a/tests/scripts/version.sh b/tests/scripts/version.sh new file mode 100755 index 0000000..5715a01 --- /dev/null +++ b/tests/scripts/version.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +source ./tests/scripts/func.sh + +# Get version +echo -n " Test version... " +e=$(piv version 2>&1) +test $? -eq 0 && echo -n "." || exit $? +grep -q "Application version" <<< $e && echo -n "." || exit $? +grep -q " found" <<< $e && echo -e ".\t${OK}" || exit $? diff --git a/tests/scripts/yubico-piv-test.sh b/tests/scripts/yubico-piv-test.sh new file mode 100755 index 0000000..1f8b197 --- /dev/null +++ b/tests/scripts/yubico-piv-test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +source ./tests/scripts/func.sh +reset +test $? -eq 0 || exit $? + +echo -n " Test PKCS11 tool..." +gen_and_check rsa:2048 +test $? -eq 0 && echo -n "." || exit $? +e=$(pkcs11-tool --test -l --pin 648219 2>&1) +test $? -eq 0 && echo -n "." || exit $? +grep -q "No errors" <<< $e && echo -n "." || exit $? +pkcs11-tool -l --pin 648219 --delete-object --type privkey --id 1 > /dev/null 2>&1 +test $? -eq 0 && echo -e ".\t${OK}" || exit $? +#e=$(pkcs11-tool --test-ec -l --pin 648219 --id 1 --key-type ec:secp256r1 2>&1) +#test $? -eq 0 && echo -n "." || exit $? +#grep -q "==> OK" <<< $e && echo -e ".\t${OK}" || exit $? diff --git a/tests/scripts/yubico-piv-tool.sh b/tests/scripts/yubico-piv-tool.sh new file mode 100755 index 0000000..a4230a4 --- /dev/null +++ b/tests/scripts/yubico-piv-tool.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +source ./tests/scripts/func.sh +echo "==== Test version ====" +./tests/scripts/version.sh +test $? -eq 0 || { + echo -e "\t${FAIL}" + exit 1 +} + +echo "==== Test asymmetric keygen ====" +./tests/scripts/keygen.sh +test $? -eq 0 || { + echo -e "\t${FAIL}" + exit 1 +} + +echo "==== Test self-signed certificates ====" +./tests/scripts/signatures.sh +test $? -eq 0 || { + echo -e "\t${FAIL}" + exit 1 +} + +echo "==== Test attestation ====" +./tests/scripts/attestation.sh +test $? -eq 0 || { + echo -e "\t${FAIL}" + exit 1 +} diff --git a/tests/start-up-and-test.sh b/tests/start-up-and-test.sh index f3fb9be..c17c958 100755 --- a/tests/start-up-and-test.sh +++ b/tests/start-up-and-test.sh @@ -1,7 +1,30 @@ -#!/bin/bash -eu +#!/bin/bash +OK="\t\033[32mok\033[0m" +FAIL="\t\033[31mfail\033[0m" + +fail() { + echo -e "${FAIL}" + exit 1 +} + +echo -n "Start PCSC..." /usr/sbin/pcscd & -sleep 2 -rm -rf memory.flash -./build_in_docker/pico_openpgp > /dev/null & +test $? -eq 0 && echo -e "${OK}" || { + echo -e "${FAIL}" + exit 1 +} +sleep 1 +rm -f memory.flash +echo -n "Start Pico OpenPGP..." +./build_in_docker/pico_openpgp > /dev/null 2>&1 & +test $? -eq 0 && echo -n "." || fail +sleep 1 +ATR="3b:da:18:ff:81:b1:fe:75:1f:03:00:31:f5:73:c0:01:60:00:90:00:1c" +e=$(opensc-tool -an 2>&1) +grep -q "${ATR}" <<< $e && echo -n "." || fail +test $? -eq 0 && echo -e "${OK}" || fail + pytest tests -W ignore::DeprecationWarning + +./tests/scripts/cli-test.sh