diff --git a/.github/workflows/codeql_checks.yml b/.github/workflows/codeql_checks.yml
index ff59de9c..9b559d7e 100644
--- a/.github/workflows/codeql_checks.yml
+++ b/.github/workflows/codeql_checks.yml
@@ -14,31 +14,6 @@ on:
jobs:
analyse:
- name: Analyse
- strategy:
- fail-fast: false
- matrix:
- sdk: ["$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK", "$APEX_P_SDK"]
- # 'cpp' covers C and C++
- language: ['cpp']
- runs-on: ubuntu-latest
- container:
- image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest
-
- steps:
- - name: Clone
- uses: actions/checkout@v4
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: ${{ matrix.language }}
- queries: security-and-quality
-
- # CodeQL will create the database during the compilation
- - name: Build
- run: |
- make BOLOS_SDK=${{ matrix.sdk }}
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ name: Call Ledger CodeQL analysis
+ uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_codeql_checks.yml@v1
+ secrets: inherit
diff --git a/.github/workflows/python_client_checks.yml b/.github/workflows/python_client_checks.yml
index e86f3d4b..3640fd55 100644
--- a/.github/workflows/python_client_checks.yml
+++ b/.github/workflows/python_client_checks.yml
@@ -14,27 +14,11 @@ on:
jobs:
lint:
- name: Client linting
- runs-on: ubuntu-latest
- steps:
- - name: Clone
- uses: actions/checkout@v4
- - name: Installing PIP dependencies
- run: |
- pip install pylint
- pip install -r tests/ragger/requirements.txt
- - name: Lint Python code
- run: pylint --rc tests/ragger/setup.cfg tests/ragger/application_client/
-
- mypy:
- name: Type checking
- runs-on: ubuntu-latest
- steps:
- - name: Clone
- uses: actions/checkout@v4
- - name: Installing PIP dependencies
- run: |
- pip install mypy
- pip install -r tests/ragger/requirements.txt
- - name: Mypy type checking
- run: mypy tests/ragger/application_client/
+ name: Call Ledger Python linters
+ uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_python_checks.yml@v1
+ with:
+ run_linter: pylint
+ run_type_check: true
+ src_directory: application_client
+ setup_directory: tests/ragger
+ req_directory: tests/ragger
diff --git a/.github/workflows/python_tool_checks.yml b/.github/workflows/python_tool_checks.yml
index 01d31c84..2c948b91 100644
--- a/.github/workflows/python_tool_checks.yml
+++ b/.github/workflows/python_tool_checks.yml
@@ -14,29 +14,12 @@ on:
jobs:
lint:
- name: Client linting
- runs-on: ubuntu-latest
- steps:
- - name: Clone
- uses: actions/checkout@v4
- - name: Installing PIP dependencies
- run: |
- sudo apt-get update && sudo apt-get install -y libpcsclite-dev
- pip install pylint
- pip install -r pytools/requirements.txt
- - name: Lint Python code
- run: pylint --rc pytools/setup.cfg pytools/
-
- mypy:
- name: Type checking
- runs-on: ubuntu-latest
- steps:
- - name: Clone
- uses: actions/checkout@v4
- - name: Installing PIP dependencies
- run: |
- sudo apt-get update && sudo apt-get install -y libpcsclite-dev
- pip install mypy
- pip install -r pytools/requirements.txt
- - name: Mypy type checking
- run: mypy pytools/
+ name: Call Ledger Python linters
+ uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_python_checks.yml@v1
+ with:
+ run_linter: pylint
+ run_type_check: true
+ src_directory: .
+ setup_directory: pytools
+ req_directory: pytools
+ additional_packages: libpcsclite-dev
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index 3dd30a67..acacc350 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -10,49 +10,8 @@ on:
jobs:
job_unit_test:
- name: Unit test
- runs-on: ubuntu-latest
- container:
- image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest
-
- steps:
- - name: Clone
- uses: actions/checkout@v4
-
- - name: Build unit tests
- run: |
- cd tests/unit
- cmake -Bbuild -H. && make -C build
-
- - name: Run the tests
- run: |
- cd tests/unit
- make -C build test
-
- - name: Generate code coverage
- run: |
- cd tests/unit
- lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base
- lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture
- lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info
- lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/tests/unit/*' -o coverage.info
- genhtml coverage.info -o coverage
-
- - uses: actions/upload-artifact@v4
- with:
- name: code-coverage
- path: tests/unit/coverage
-
- - name: Install codecov dependencies
- run: apt install --no-install-recommends -y curl gpg
-
- - name: Upload to codecov.io
- uses: codecov/codecov-action@v5
- env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- with:
- files: ./tests/unit/coverage.info
- flags: unittests
- name: codecov-app-openpgp
- fail_ci_if_error: true
- verbose: true
+ name: Call Ledger unit_test
+ uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_unit_tests.yml@v1
+ secrets: inherit
+ with:
+ test_directory: tests/unit
diff --git a/Makefile b/Makefile
index 59f7b852..c132b8e9 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ APPNAME = OpenPGP
# Application version
APPVERSION_M = 2
APPVERSION_N = 5
-APPVERSION_P = 0
+APPVERSION_P = 1
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"
DEFINES += SPEC_VERSION='"3.3.1"'
diff --git a/doc/user/0001-plist.patch b/doc/user/0001-plist-ubuntu22.patch
similarity index 100%
rename from doc/user/0001-plist.patch
rename to doc/user/0001-plist-ubuntu22.patch
diff --git a/doc/user/0001-plist-ubuntu24.patch b/doc/user/0001-plist-ubuntu24.patch
new file mode 100644
index 00000000..af368843
--- /dev/null
+++ b/doc/user/0001-plist-ubuntu24.patch
@@ -0,0 +1,35 @@
+--- libccid_Info.plist_org 2025-09-23 10:35:20.962252106 +0200
++++ libccid_Info.plist 2025-09-23 10:54:39.042011175 +0200
+@@ -499,6 +499,10 @@
+ 0x2C97
+ 0x2C97
+ 0x2C97
++ 0x2C97
++ 0x2C97
++ 0x2C97
++ 0x2C97
+ 0x17EF
+ 0x17EF
+ 0x17EF
+@@ -1096,6 +1100,10 @@
+ 0x1009
+ 0x4009
+ 0x5009
++ 0x5000
++ 0x6000
++ 0x7000
++ 0x8000
+ 0x6007
+ 0x6055
+ 0x6111
+@@ -1693,6 +1701,10 @@
+ Ledger Nano S
+ Ledger Nano X
+ Ledger Nano S Plus
++ Ledger Nano S Plus Legacy
++ Ledger Flex
++ Ledger Stax
++ Ledger Apex P
+ Lenovo Lenovo USB Smartcard Keyboard
+ Lenovo Lenovo USB Smartcard Keyboard
+ Lenovo Lenovo Smartcard Wired Keyboard II
diff --git a/doc/user/app-openpgp.rst b/doc/user/app-openpgp.rst
index b084a834..02dc7a5d 100644
--- a/doc/user/app-openpgp.rst
+++ b/doc/user/app-openpgp.rst
@@ -75,16 +75,52 @@ Linux
You have to add your devices to ``/etc/libccid_Info.plist``
-MAC
-~~~
+macOS
+~~~~~
+
+Two supported approaches:
+
+**A: Recommended: enable the bundled IFD CCID driver (no SIP required)**
+
+On recent macOS releases (Sonoma 14 and Sequoia 15), Apple ships both its own CCID driver and Ludovic Rousseau’s **IFD CCID** (included by Apple; 1.5.1 in current builds). If your Ledger is not detected or behaves oddly with the Apple driver, enable IFD CCID:
+
+.. code-block:: bash
+
+ # Enable IFD CCID system-wide
+ sudo defaults write /Library/Preferences/com.apple.security.smartcard useIFDCCID -bool yes
+
+Verify the setting (``1`` means IFD CCID is enabled; “does not exist” means Apple driver is used):
+
+.. code-block:: bash
+
+ defaults read /Library/Preferences/com.apple.security.smartcard.plist useIFDCCID
+
+To revert to the Apple driver later:
+
+.. code-block:: bash
+
+ sudo defaults delete /Library/Preferences/com.apple.security.smartcard useIFDCCID
+ # or
+ sudo defaults write /Library/Preferences/com.apple.security.smartcard useIFDCCID -bool no
+
+Unplug/replug the device after changing the setting. A reboot is rarely necessary.
+
+**B: Legacy: manual device list (advanced; may require disabling SIP)**
+
+Only if you must maintain a custom device list for IFD CCID. Edit:
+
+``/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist``
-1. First it is necessary to disable SIP, that forbid editing files in ``/usr/``.
-2. You have to add your devices to ``/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist``
-3. Enable SIP
+.. warning::
+ Editing system files may be blocked by SIP (System Integrity Protection) and is discouraged unless strictly necessary.
+ Prefer the single-command switch above whenever possible.
-Note: See https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html
+.. note::
+ If you need to disable/enable SIP to edit system files, see Apple’s documentation:
+ https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection
-TBC...
+See the **Manual update of CCID** section below for the required XML keys (``ifdVendorID``, ``ifdProductID``, ``ifdFriendlyName``) and Ledger device values.
+We intentionally keep those details centralized in that section.
Windows
~~~~~~~
diff --git a/pytools/gpgapp/gpgcard.py b/pytools/gpgapp/gpgcard.py
index b436a177..abe2d5e2 100644
--- a/pytools/gpgapp/gpgcard.py
+++ b/pytools/gpgapp/gpgcard.py
@@ -22,15 +22,14 @@
from hashlib import sha1
from typing import Optional, Tuple
from dataclasses import dataclass
+# pylint: disable=import-error
from Crypto.PublicKey.RSA import construct
+from ledgercomm import Transport # type: ignore
+# pylint: enable=import-error
from gpgapp.gpgcmd import DataObject, ErrorCodes, KeyTypes, PassWord, PubkeyAlgo # type: ignore
from gpgapp.gpgcmd import KEY_OPERATIONS, KEY_TEMPLATES, USER_SALUTATION # type: ignore
-# pylint: disable=import-error
-from ledgercomm import Transport # type: ignore
-# pylint: enable=import-error
-
APDU_MAX_SIZE: int = 0xFE
APDU_CHAINING_MODE: int = 0x10
diff --git a/src/gpg_ux_nbgl.c b/src/gpg_ux_nbgl.c
index 5c752b29..269e7dc9 100644
--- a/src/gpg_ux_nbgl.c
+++ b/src/gpg_ux_nbgl.c
@@ -1168,12 +1168,6 @@ void ui_menu_pinconfirm_display(unsigned int value) {
/* ------------------------------ PIN ENTRY UX ----------------------------- */
-// clang-format off
-enum {
- TOKEN_PIN_ENTRY_BACK = FIRST_USER_TOKEN,
-};
-// clang-format on
-
static void ui_menu_pinentry_cb(void);
/**
@@ -1288,22 +1282,6 @@ static void pinback_cb(void) {
ui_init();
}
-#ifdef SCREEN_SIZE_WALLET
-/**
- * @brief Pin Entry Action callback
- *
- * @param[in] token button Id pressed
- * @param[in] index widget index on the page
- *
- */
-static void pinentry_cb(int token, uint8_t index) {
- UNUSED(index);
- if (token == TOKEN_PIN_ENTRY_BACK) {
- pinback_cb();
- }
-}
-#endif // SCREEN_SIZE_WALLET
-
/**
* @brief Pin Entry page display
*
@@ -1343,23 +1321,13 @@ void ui_menu_pinentry_display(unsigned int step) {
minLen = (G_gpg_vstate.io_p2 == PIN_ID_PW3) ? GPG_MIN_PW3_LENGTH : GPG_MIN_PW1_LENGTH;
// Draw the keypad
-#ifdef SCREEN_SIZE_WALLET
- nbgl_useCaseKeypadPIN(G_gpg_vstate.menu,
- minLen,
- GPG_MAX_PW_LENGTH,
- TOKEN_PIN_ENTRY_BACK,
- false,
- TUNE_TAP_CASUAL,
- pinentry_validate_cb,
- pinentry_cb);
-#else // SCREEN_SIZE_WALLET
- nbgl_useCaseKeypadPIN(G_gpg_vstate.menu,
- minLen,
- GPG_MAX_PW_LENGTH,
- false,
- pinentry_validate_cb,
- pinback_cb);
-#endif // SCREEN_SIZE_WALLET
+ nbgl_useCaseKeypad(G_gpg_vstate.menu,
+ minLen,
+ GPG_MAX_PW_LENGTH,
+ false,
+ true,
+ pinentry_validate_cb,
+ pinback_cb);
}
/**
diff --git a/tests/ragger/snapshots/apex_p/test_menu_settings/00011.png b/tests/ragger/snapshots/apex_p/test_menu_settings/00011.png
index bd6b4f30..ffaaf168 100644
Binary files a/tests/ragger/snapshots/apex_p/test_menu_settings/00011.png and b/tests/ragger/snapshots/apex_p/test_menu_settings/00011.png differ
diff --git a/tests/ragger/snapshots/apex_p/test_menu_settings/00012.png b/tests/ragger/snapshots/apex_p/test_menu_settings/00012.png
index 7695c425..9605256f 100644
Binary files a/tests/ragger/snapshots/apex_p/test_menu_settings/00012.png and b/tests/ragger/snapshots/apex_p/test_menu_settings/00012.png differ