Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2a64452
fix V-028/CWE-345: bind on-chain output keys to the reviewed destination
iartemov-ledger Jun 5, 2026
6d41d62
test V-028/CWE-345: output-key substitution PoC and real tx-prefix st…
iartemov-ledger Jun 5, 2026
47456f4
fix V-028/CWE-345: bind tx public keys in `extra` to the reviewed tra…
iartemov-ledger Jun 5, 2026
6fb9b35
test V-028/CWE-345: stream real tx pubkey in prefix helper for the ex…
iartemov-ledger Jun 5, 2026
87aa5a8
fix V-030/CWE-285: never sign a real transaction the user did not app…
iartemov-ledger Jun 8, 2026
cf3f3a0
fix V-030/CWE-367: injecting 2nd APDU while on confirmation UX
iartemov-ledger Jun 15, 2026
0b852fa
test V-030/CWE-367: tests injecting 2nd APDU while on confirmation UX
iartemov-ledger Jun 8, 2026
30b4f06
test V-030/CWE-285: to make sure that no signing takes place without …
iartemov-ledger Jun 8, 2026
d93356a
tests: opt-in flag to force-run tests marked @pytest.mark.skip
iartemov-ledger Jun 8, 2026
91e7034
fix V-049/CWE-125: bound varint decoder against out-of-bounds read on…
iartemov-ledger Jun 9, 2026
d5241bf
fix V-048/CWE-787: bound integrated payment-ID formatting to prevent …
iartemov-ledger Jun 9, 2026
d9d921e
fix V-036/CWE-372: consume HMAC trailer in spend-key path of monero_i…
iartemov-ledger Jun 9, 2026
6c0709e
fix V-052/CWE-345: bind change-output derivation to device open-tx key R
iartemov-ledger Jun 9, 2026
b757503
test V-052/CWE-345: bind change-output derivation to device open-tx k…
iartemov-ledger Jun 9, 2026
1d2dacc
fix V-051/CWE-345: show non-primary change account (Change account N)…
iartemov-ledger Jun 10, 2026
93e8c49
test V-051/CWE-345: show non-primary change account (Change account N…
iartemov-ledger Jun 10, 2026
c3c1b0c
fix: accept change to non-primary account during signing
iartemov-ledger Jun 10, 2026
cb09186
test: accept change to non-primary account during signing
iartemov-ledger Jun 10, 2026
475975a
test: cover tx_extra shapes in the prefix-hash parser
iartemov-ledger Jun 10, 2026
b726e68
Version dump to 2.2.1
iartemov-ledger Jun 11, 2026
2ff8839
Flipping to opt-in DEBUG=1
iartemov-ledger Jun 11, 2026
7558ca4
Returning real SW error for INS_GET_KEY P1=1 path
iartemov-ledger Jun 11, 2026
90135b4
[auto-update] Update Ragger snapshots
github-actions[bot] Jun 15, 2026
2023c88
Run coding style check through reusable workflow
iartemov-ledger Jun 15, 2026
8084abf
clang-format v21
iartemov-ledger Jun 15, 2026
e06f9f9
clang-format v21: to ignore for git blame
iartemov-ledger Jun 15, 2026
736a781
refactor: split long tx_extra parser switch cases
iartemov-ledger Jun 15, 2026
a281963
test: normalize gen_txout_keys to a fixed 3-tuple return and other te…
iartemov-ledger Jun 15, 2026
26f138f
fix: Allow the zero-amount dummy change output (the second output Mon…
iartemov-ledger Jun 16, 2026
d40f5a5
test: Allow the zero-amount dummy change output (the second output Mo…
iartemov-ledger Jun 16, 2026
acebc67
fix: mlsagH scratch use
iartemov-ledger Jun 16, 2026
5b64426
fix: reject non-canonical short amounts in tx prehash
iartemov-ledger Jun 16, 2026
41361a9
test: reject non-canonical short amounts in tx prehash
iartemov-ledger Jun 16, 2026
adb04e8
[auto-update] Update Ragger snapshots
github-actions[bot] Jun 16, 2026
1e68550
fix: validate main tx pubkey is device-derived (r·G or r·D_dest)
iartemov-ledger Jun 17, 2026
78f3eea
test: validate main tx pubkey is device-derived (r·G or r·D_dest)
iartemov-ledger Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8084abfe092be48b73b0faedb18b21b151229d92
32 changes: 14 additions & 18 deletions .github/workflows/lint-workflow.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
name: Code style check
name: Run coding style check through reusable workflow

# This workflow will run linting checks to ensure a level of uniformization among all Ledger applications.
#
# The presence of this workflow is mandatory as a minimal level of linting is required.
# You are however free to modify the content of the .clang-format file and thus the coding style of your application.
# We simply ask you to not diverge too much from the linting of the Boilerplate application.

on:
workflow_dispatch:
push:
branches:
- master
- main
- develop
pull_request:
branches:
- master
- develop

jobs:
job_lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Clone
uses: actions/checkout@v2

- name: Lint
uses: DoozyX/clang-format-lint-action@v0.5
with:
source: './src'
extensions: 'h,c'
clangFormatVersion: 9
check_linting:
name: Check linting using the reusable workflow
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_lint.yml@v1
with:
source: './src'
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to this project will be documented in this file.


## 2.2.1 - 2026-06-11

- Different security and bug fixes

## 2.2.0 - 2026-05-11

- Different security fixes
Expand Down
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ APPNAME = "Monero"
# Application version
APPVERSION_M = 2
APPVERSION_N = 2
APPVERSION_P = 0
APPVERSION_P = 1
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

SPECVERSION = "1.0"
Expand Down Expand Up @@ -79,9 +79,11 @@ DEFINES += HAVE_LEGACY_PID
DEFINES += U2F_PROXY_MAGIC=\"MOON\"
DEFINES += HAVE_IO_U2F HAVE_U2F

# Enabling debug PRINTF
ifneq ($(DEBUG),0)
# Debug options
# The gate is opt-in: a bare `make` (DEBUG unset) or `make DEBUG=0`
# builds the secure configuration, and only an explicit `make DEBUG=1`
# enables them (used by functional tests)
ifeq ($(DEBUG),1)
# Enabling debug PRINTF
DEFINES += DEBUG_HWDEVICE
DEFINES += BYPASS_COMMITMENT_FOR_TESTS
DEFINES += IODUMMYCRYPT # or IONOCRYPT
Expand Down
40 changes: 39 additions & 1 deletion src/monero_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,13 @@ int monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *ind
size_t D_len, size_t index_len);
int monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, unsigned char *index,
size_t sub_s_len, size_t s_len, size_t index_len);
int monero_check_change_address(const unsigned char *Aout, const unsigned char *Bout);
// On a successful match, *out_major (if non-NULL) receives the change account
// index (0 for the primary address). Change always lands on the account root
// (minor 0), so only the account is reported.
int monero_check_change_address(const unsigned char *Aout, const unsigned char *Bout,
unsigned int *out_major);
// Build the change-review title ("Change" or "Change account <major>").
void monero_format_change_title(char *str, size_t str_len, unsigned int major);

/* ----------------------------------------------------------------------- */
/* --- CRYPTO ---- */
Expand Down Expand Up @@ -257,6 +263,38 @@ static inline int monero_sha256_outkeys_final(unsigned char *out) {
return monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_out_keys, out);
}

static inline void monero_sha256_out_eph_init(void) {
monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_out_eph);
}

static inline int monero_sha256_out_eph_update(const unsigned char *buf, size_t len) {
return monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_out_eph, buf, len);
}

static inline int monero_sha256_out_eph_final(unsigned char *out) {
return monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_out_eph, out);
}

static inline void monero_sha256_addk_init(void) {
monero_hash_init_sha256((cx_hash_t *)&G_monero_vstate.sha256_addk);
}

static inline int monero_sha256_addk_update(const unsigned char *buf, size_t len) {
return monero_hash_update((cx_hash_t *)&G_monero_vstate.sha256_addk, buf, len);
}

static inline int monero_sha256_addk_final(unsigned char *out) {
return monero_hash_final((cx_hash_t *)&G_monero_vstate.sha256_addk, out);
}

/* Reset the tx-prefix output-key parser before streaming a new prefix. */
void monero_prefix_outkeys_reset(void);

/* Feed the next tx-prefix chunk (vin/vout/extra, as streamed over
* INS_PREFIX_HASH P2) to the output-key check. Returns an error if the signed
* outputs don't match the ones derived in INS_GEN_TXOUT_KEYS. */
int monero_prefix_outkeys_parse(const unsigned char *buf, size_t len);

/*
* check 1<s<N, else throw
*/
Expand Down
13 changes: 7 additions & 6 deletions src/monero_blind.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
* limitations under the License.
*****************************************************************************/

#include "os.h"
#include "cx.h"
#include "monero_types.h"
#include "monero_api.h"
#include "monero_types.h"
#include "monero_vars.h"
#include "os.h"

/* ----------------------------------------------------------------------- */
/* --- --- */
Expand Down Expand Up @@ -89,8 +89,9 @@ int monero_apdu_blind() {
/* ----------------------------------------------------------------------- */
/* --- --- */
/* ----------------------------------------------------------------------- */
int monero_unblind(unsigned char *v, unsigned char *k, unsigned char *AKout,
unsigned int short_amount, size_t v_len, size_t k_len, size_t AKout_len) {
int monero_unblind(unsigned char* v, unsigned char* k, unsigned char* AKout,
unsigned int short_amount, size_t v_len, size_t k_len,
size_t AKout_len) {
int error;
if (short_amount == 2) {
error = monero_genCommitmentMask(k, AKout, k_len, AKout_len);
Expand Down Expand Up @@ -150,8 +151,8 @@ int monero_apdu_unblind() {

monero_io_discard(1);

err = monero_unblind(v, k, AKout, G_monero_vstate.options & 0x03, sizeof(v), sizeof(k),
sizeof(AKout));
err = monero_unblind(v, k, AKout, G_monero_vstate.options & 0x03, sizeof(v),
sizeof(k), sizeof(AKout));
if (err) {
goto end;
}
Expand Down
22 changes: 15 additions & 7 deletions src/monero_clsag.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
* limitations under the License.
*****************************************************************************/

#include "os.h"
#include "cx.h"
#include "monero_types.h"
#include "monero_api.h"
#include "monero_types.h"
#include "monero_vars.h"
#include "os.h"

/* ----------------------------------------------------------------------- */
/* --- --- */
Expand Down Expand Up @@ -132,7 +132,8 @@ int monero_apdu_clsag_hash() {
monero_io_fetch(msg, 32);
monero_io_discard(1);

G_monero_vstate.clsag_match_history = (G_monero_vstate.clsag_match_history << 1) & 0x07;
G_monero_vstate.clsag_match_history =
(G_monero_vstate.clsag_match_history << 1) & 0x07;
if (memcmp(msg, G_monero_vstate.mlsagH, 32) == 0) {
G_monero_vstate.clsag_match_history |= 1;
}
Expand Down Expand Up @@ -191,6 +192,13 @@ int monero_apdu_clsag_sign() {
unsigned char mu_C[32];
int err = 0;

// Never sign a real transaction the user did not approve on screen.
if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL &&
!G_monero_vstate.user_approved_tx) {
err = SW_SECURITY_USER_NOT_APPROVED;
goto end;
}

if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_FAKE) {
monero_io_fetch(a, 32);
monero_io_fetch(p, 32);
Expand Down Expand Up @@ -257,8 +265,8 @@ int monero_apdu_clsag_sign() {
goto end;
}

err = monero_reduce(G_monero_vstate.c, G_monero_vstate.c, sizeof(G_monero_vstate.c),
sizeof(G_monero_vstate.c));
err = monero_reduce(G_monero_vstate.c, G_monero_vstate.c,
sizeof(G_monero_vstate.c), sizeof(G_monero_vstate.c));
if (err) {
goto end;
}
Expand All @@ -285,8 +293,8 @@ int monero_apdu_clsag_sign() {
goto end;
}
// mu_P = c * (p*mu_P + mu_C*z)
err = monero_multm(mu_P, G_monero_vstate.c, s, sizeof(mu_P), sizeof(G_monero_vstate.c),
sizeof(s));
err = monero_multm(mu_P, G_monero_vstate.c, s, sizeof(mu_P),
sizeof(G_monero_vstate.c), sizeof(s));
if (err) {
goto end;
}
Expand Down
Loading
Loading