diff --git a/.github/workflows/codeql_checks.yml b/.github/workflows/codeql_checks.yml index 9b559d7..7ba871d 100644 --- a/.github/workflows/codeql_checks.yml +++ b/.github/workflows/codeql_checks.yml @@ -16,4 +16,5 @@ jobs: analyse: name: Call Ledger CodeQL analysis uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_codeql_checks.yml@v1 - secrets: inherit + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7479ffe..5d4b5f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,8 +47,8 @@ repos: hooks: # Run the linter. - id: ruff-check - types_or: [ python, pyi ] - args: [ --fix ] + types_or: [python, pyi] + args: [--fix] # Run the formatter. - id: ruff-format - types_or: [ python, pyi ] + types_or: [python, pyi] diff --git a/Makefile b/Makefile index adf4cb3..1fa6a90 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ APPNAME = OpenPGP # Application version APPVERSION_M = 2 APPVERSION_N = 7 -APPVERSION_P = 1 +APPVERSION_P = 2 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" DEFINES += SPEC_VERSION='"3.3.1"' diff --git a/clients/wui/pnpm-lock.yaml b/clients/wui/pnpm-lock.yaml index dee044a..1763580 100644 --- a/clients/wui/pnpm-lock.yaml +++ b/clients/wui/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + undici: '>=7.29.0' + importers: .: @@ -1880,9 +1883,9 @@ packages: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - undici@7.28.0: - resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} - engines: {node: '>=20.18.1'} + undici@8.10.0: + resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} + engines: {node: '>=22.19.0'} update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} @@ -3162,7 +3165,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.1 - undici: 7.28.0 + undici: 8.10.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -3790,7 +3793,7 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.15 - undici@7.28.0: {} + undici@8.10.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: diff --git a/clients/wui/pnpm-workspace.yaml b/clients/wui/pnpm-workspace.yaml index ded913c..45ea65d 100644 --- a/clients/wui/pnpm-workspace.yaml +++ b/clients/wui/pnpm-workspace.yaml @@ -5,3 +5,6 @@ # needs to install its platform binary; the others are harmless to allow. allowBuilds: esbuild: true + +overrides: + undici: ">=7.29.0" diff --git a/src/gpg_data.c b/src/gpg_data.c index 2ab33bf..da49566 100644 --- a/src/gpg_data.c +++ b/src/gpg_data.c @@ -261,6 +261,7 @@ int gpg_apdu_put_data(unsigned int ref) { unsigned int t, l, sw; unsigned int *ptr_l = NULL; unsigned char *ptr_v = NULL; + gpg_key_t *ptr_key = NULL; void *pkey = NULL; cx_aes_key_t aes_key = {0}; cx_err_t error = CX_INTERNAL_ERROR; @@ -555,7 +556,7 @@ int gpg_apdu_put_data(unsigned int ref) { /* ----------------- User -----------------*/ /* Name */ case 0x5B: - if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->name.value)) { + if (G_gpg_vstate.io_length > GPG_NAME_LENGTH) { sw = SWO_WRONG_LENGTH; break; } @@ -569,7 +570,7 @@ int gpg_apdu_put_data(unsigned int ref) { break; /* Login data */ case 0x5E: - if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->login.value)) { + if (G_gpg_vstate.io_length > GPG_EXT_PRIVATE_DO_LENGTH) { sw = SWO_WRONG_LENGTH; break; } @@ -583,7 +584,7 @@ int gpg_apdu_put_data(unsigned int ref) { break; /* Language preferences */ case 0x5F2D: - if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->lang.value)) { + if (G_gpg_vstate.io_length > GPG_LANG_LENGTH) { sw = SWO_WRONG_LENGTH; break; } @@ -597,7 +598,7 @@ int gpg_apdu_put_data(unsigned int ref) { break; /* salutation */ case 0x5F35: - if (G_gpg_vstate.io_length != sizeof(N_gpg_pstate->salutation)) { + if (G_gpg_vstate.io_length != GPG_SALUTATION_LENGTH) { sw = SWO_WRONG_LENGTH; break; } @@ -608,7 +609,7 @@ int gpg_apdu_put_data(unsigned int ref) { break; /* Uniform resource locator */ case 0x5F50: - if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->keys[G_gpg_vstate.slot].url.value)) { + if (G_gpg_vstate.io_length > GPG_EXT_PRIVATE_DO_LENGTH) { sw = SWO_WRONG_LENGTH; break; } @@ -657,10 +658,12 @@ int gpg_apdu_put_data(unsigned int ref) { case 0xC1: ptr_l = &G_gpg_vstate.kslot->sig.attributes.length; ptr_v = G_gpg_vstate.kslot->sig.attributes.value; + ptr_key = &G_gpg_vstate.kslot->sig; goto WRITE_ATTRIBUTES; case 0xC2: ptr_l = &G_gpg_vstate.kslot->dec.attributes.length; ptr_v = G_gpg_vstate.kslot->dec.attributes.value; + ptr_key = &G_gpg_vstate.kslot->dec; #ifdef NO_DECRYPT_cv25519 decKey = true; #endif @@ -668,6 +671,7 @@ int gpg_apdu_put_data(unsigned int ref) { case 0xC3: ptr_l = &G_gpg_vstate.kslot->aut.attributes.length; ptr_v = G_gpg_vstate.kslot->aut.attributes.value; + ptr_key = &G_gpg_vstate.kslot->aut; goto WRITE_ATTRIBUTES; WRITE_ATTRIBUTES: if (G_gpg_vstate.io_length > 12) { @@ -706,6 +710,7 @@ int gpg_apdu_put_data(unsigned int ref) { } if (sw == SWO_SUCCESS) { + nvm_write(ptr_key, NULL, sizeof(gpg_key_t)); nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); nvm_write(ptr_l, &G_gpg_vstate.io_length, sizeof(unsigned int)); } diff --git a/src/gpg_dispatch.c b/src/gpg_dispatch.c index 71281a6..37f0cec 100644 --- a/src/gpg_dispatch.c +++ b/src/gpg_dispatch.c @@ -261,6 +261,10 @@ int gpg_dispatch() { unsigned int tag, t, l; int sw = SWO_UNKNOWN; + if (G_gpg_vstate.ui_pending) { + return SWO_CONDITIONS_NOT_SATISFIED; + } + if ((G_gpg_vstate.io_cla != CLA_APP_DEF) && (G_gpg_vstate.io_cla != CLA_APP_CHAIN) && (G_gpg_vstate.io_cla != CLA_APP_APDU_PIN)) { return SWO_INVALID_CLA; diff --git a/src/gpg_gen.c b/src/gpg_gen.c index 6950e65..2898ee5 100644 --- a/src/gpg_gen.c +++ b/src/gpg_gen.c @@ -187,19 +187,19 @@ static int gpg_read_rsa_kyey(gpg_key_t *keygpg) { gpg_io_mark(); switch (ksz) { case 2048 / 8: - if (keygpg->priv_key.rsa2048.size == 0) { + if (keygpg->priv_key.rsa2048.size != ksz) { return SWO_REFERENCED_DATA_NOT_FOUND; } gpg_io_insert_tlv(0x81, ksz, (unsigned char *) &keygpg->priv_key.rsa2048.n); break; case 3072 / 8: - if (keygpg->priv_key.rsa3072.size == 0) { + if (keygpg->priv_key.rsa3072.size != ksz) { return SWO_REFERENCED_DATA_NOT_FOUND; } gpg_io_insert_tlv(0x81, ksz, (unsigned char *) &keygpg->priv_key.rsa3072.n); break; case 4096 / 8: - if (keygpg->priv_key.rsa4096.size == 0) { + if (keygpg->priv_key.rsa4096.size != ksz) { return SWO_REFERENCED_DATA_NOT_FOUND; } gpg_io_insert_tlv(0x81, ksz, (unsigned char *) &keygpg->priv_key.rsa4096.n); diff --git a/src/gpg_pin.c b/src/gpg_pin.c index 961cd2d..8cd12e6 100644 --- a/src/gpg_pin.c +++ b/src/gpg_pin.c @@ -222,12 +222,14 @@ int gpg_apdu_verify() { case PIN_MODE_SCREEN: // Delegate pin check to ui gpg_io_discard(1); + G_gpg_vstate.ux_pinconfirm_p2 = G_gpg_vstate.io_p2; ui_menu_pinentry_display(0); sw = 0; break; case PIN_MODE_CONFIRM: // Delegate pin check to ui gpg_io_discard(1); + G_gpg_vstate.ux_pinconfirm_p2 = G_gpg_vstate.io_p2; ui_menu_pinconfirm_display(G_gpg_vstate.io_p2); sw = 0; break; @@ -301,6 +303,7 @@ int gpg_apdu_change_ref_data() { if (G_gpg_vstate.io_length == 0) { // Delegate pin change to ui gpg_io_discard(1); + G_gpg_vstate.ux_pinconfirm_p2 = G_gpg_vstate.io_p2; ui_menu_pinentry_display(0); return 0; } diff --git a/src/gpg_types.h b/src/gpg_types.h index 90d86a2..aeac7d9 100644 --- a/src/gpg_types.h +++ b/src/gpg_types.h @@ -47,6 +47,10 @@ #define GPG_KEY_ATTRIBUTES_LENGTH 12 +#define GPG_NAME_LENGTH 39 +#define GPG_LANG_LENGTH 8 +#define GPG_SALUTATION_LENGTH 1 + #define GPG_RSA_DEFAULT_PUB 0x00010001U #ifndef CX_AES_128_KEY_LEN @@ -150,11 +154,11 @@ struct gpg_nv_state_s { /* -- Cardholder Related Data -- */ /* 5B */ - LV(name, 39); + LV(name, GPG_NAME_LENGTH); /* 5F2D */ - LV(lang, 8); + LV(lang, GPG_LANG_LENGTH); /* 5F35 */ - unsigned char salutation[1]; + unsigned char salutation[GPG_SALUTATION_LENGTH]; /* -- Application Related Data -- */ /* 4F */ @@ -266,6 +270,8 @@ struct gpg_v_state_s { char menu[112]; unsigned char ux_pinentry[GPG_MAX_PW_LENGTH]; unsigned char ux_pinLen; + unsigned char ux_pinconfirm_p2; + unsigned char ui_pending; unsigned int ux_key; unsigned int ux_type; diff --git a/src/gpg_ux_nbgl.c b/src/gpg_ux_nbgl.c index ec8005c..c0c6940 100644 --- a/src/gpg_ux_nbgl.c +++ b/src/gpg_ux_nbgl.c @@ -1140,7 +1140,8 @@ static void ui_reset(void) { * */ void pin_confirm_cb(bool confirm) { - gpg_pin_set_verified(G_gpg_vstate.io_p2, confirm); + G_gpg_vstate.ui_pending = 0; + gpg_pin_set_verified(G_gpg_vstate.ux_pinconfirm_p2, confirm); gpg_io_discard(0); gpg_io_insert_u16(confirm ? SWO_SUCCESS : SWO_CONDITIONS_NOT_SATISFIED); @@ -1155,6 +1156,7 @@ void pin_confirm_cb(bool confirm) { * */ void ui_menu_pinconfirm_display(unsigned int value) { + G_gpg_vstate.ui_pending = 1; snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s %x", @@ -1179,10 +1181,11 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { unsigned char *pin1 = NULL; gpg_pin_t *pin = NULL; + G_gpg_vstate.ui_pending = 0; switch (G_gpg_vstate.io_ins) { case INS_VERIFY: - pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); - sw = gpg_pin_check(pin, G_gpg_vstate.io_p2, pinentry, length); + pin = gpg_pin_get_pin(G_gpg_vstate.ux_pinconfirm_p2); + sw = gpg_pin_check(pin, G_gpg_vstate.ux_pinconfirm_p2, pinentry, length); gpg_io_discard(1); if (sw == SWO_AUTH_METHOD_BLOCKED) { gpg_io_insert_u16(sw); @@ -1202,7 +1205,7 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { snprintf(G_gpg_vstate.line, sizeof(G_gpg_vstate.line), "%s PIN", - (G_gpg_vstate.io_p2 == PIN_ID_PW3) ? "ADMIN" : "USER"); + (G_gpg_vstate.ux_pinconfirm_p2 == PIN_ID_PW3) ? "ADMIN" : "USER"); ui_info(G_gpg_vstate.line, "VERIFIED", ui_init, true); break; @@ -1210,8 +1213,8 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { switch (G_gpg_vstate.ux_step) { case 0: // Check Current pin code - pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); - sw = gpg_pin_check(pin, G_gpg_vstate.io_p2, pinentry, length); + pin = gpg_pin_get_pin(G_gpg_vstate.ux_pinconfirm_p2); + sw = gpg_pin_check(pin, G_gpg_vstate.ux_pinconfirm_p2, pinentry, length); gpg_io_discard(1); if (sw == SWO_AUTH_METHOD_BLOCKED) { gpg_io_insert_u16(sw); @@ -1242,7 +1245,7 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { gpg_io_discard(1); ui_info(PIN_DIFFERS, EMPTY, ui_menu_pinentry_cb, false); } else { - pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); + pin = gpg_pin_get_pin(G_gpg_vstate.ux_pinconfirm_p2); sw = gpg_pin_set(pin, G_gpg_vstate.work.io_buffer + 1, length); gpg_io_discard(1); gpg_io_insert_u16(sw); @@ -1250,10 +1253,11 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { if (sw != SWO_SUCCESS) { ui_info("Process Error", EMPTY, ui_init, false); } else { - snprintf(G_gpg_vstate.line, - sizeof(G_gpg_vstate.line), - "%s PIN", - (G_gpg_vstate.io_p2 == PIN_ID_PW3) ? "ADMIN" : "USER"); + snprintf( + G_gpg_vstate.line, + sizeof(G_gpg_vstate.line), + "%s PIN", + (G_gpg_vstate.ux_pinconfirm_p2 == PIN_ID_PW3) ? "ADMIN" : "USER"); ui_info(G_gpg_vstate.line, "CHANGED", ui_init, true); } } @@ -1273,6 +1277,7 @@ static void pinentry_validate_cb(const uint8_t *pinentry, uint8_t length) { * */ static void pinback_cb(void) { + G_gpg_vstate.ui_pending = 0; gpg_io_discard(0); gpg_io_insert_u16(SWO_CONDITIONS_NOT_SATISFIED); gpg_io_do(IO_RETURN_AFTER_TX); @@ -1289,6 +1294,7 @@ void ui_menu_pinentry_display(unsigned int step) { uint8_t minLen; char line[10]; + G_gpg_vstate.ui_pending = 1; // Init the page title explicit_bzero(G_gpg_vstate.line, sizeof(G_gpg_vstate.line)); if (G_gpg_vstate.io_ins == INS_CHANGE_REFERENCE_DATA) { @@ -1314,9 +1320,10 @@ void ui_menu_pinentry_display(unsigned int step) { sizeof(G_gpg_vstate.menu), "%s %s PIN", line, - (G_gpg_vstate.io_p2 == PIN_ID_PW3) ? "Admin" : "User"); + (G_gpg_vstate.ux_pinconfirm_p2 == PIN_ID_PW3) ? "Admin" : "User"); - minLen = (G_gpg_vstate.io_p2 == PIN_ID_PW3) ? GPG_MIN_PW3_LENGTH : GPG_MIN_PW1_LENGTH; + minLen = + (G_gpg_vstate.ux_pinconfirm_p2 == PIN_ID_PW3) ? GPG_MIN_PW3_LENGTH : GPG_MIN_PW1_LENGTH; // Draw the keypad nbgl_useCaseKeypad(G_gpg_vstate.menu, minLen,