From 750eed368cc10069a7b7190e5ab96189628174a1 Mon Sep 17 00:00:00 2001 From: Theo Madzou Date: Wed, 15 Apr 2026 15:40:11 +0200 Subject: [PATCH 1/3] check correctness of end padding in country inclusion circuit --- .../lib/inclusion-check/country/src/lib.nr | 33 +++++++++++ .../lib/inclusion-check/country/src/tests.nr | 56 ++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/noir/lib/inclusion-check/country/src/lib.nr b/src/noir/lib/inclusion-check/country/src/lib.nr index ac6bda3f4..545b04c2e 100644 --- a/src/noir/lib/inclusion-check/country/src/lib.nr +++ b/src/noir/lib/inclusion-check/country/src/lib.nr @@ -29,6 +29,31 @@ pub unconstrained fn unsafe_get_index( index } +/// Check that the country list is correctly padded at the end with the empty +/// country code (three null characters). Once an empty country code is encountered, +/// all subsequent entries must also be empty. This prevents injection of country +/// codes after the first padding entry. +/// +/// # Arguments +/// +/// * `country_list`: The list of countries +pub fn check_country_list_padding(country_list: [Alpha3CountryCode; N]) { + let empty = EMPTY_ALPHA3_COUNTRY_CODE.as_bytes(); + let mut found_padding = false; + for i in 0..N { + let is_empty = country_list[i].as_bytes() == empty; + if is_empty { + found_padding = true; + } + if found_padding { + assert( + is_empty, + "Country list cannot contain country codes after the padding", + ); + } + } +} + /// Check if the nationality from the MRZ is in the country list /// /// # Arguments @@ -36,6 +61,10 @@ pub unconstrained fn unsafe_get_index( /// * `dg1`: The MRZ /// * `country_list`: The list of countries pub fn check_nationality_inclusion(dg1: DG1Data, country_list: [Alpha3CountryCode; N]) { + // Make sure the country list is properly padded with empty country codes at the end + // so that no additional country code can be injected after the first padding entry + check_country_list_padding(country_list); + let nationality_bytes = get_nationality_from_mrz(dg1); // Safety: get the index of the country in the list from an unconstrained function @@ -59,6 +88,10 @@ pub fn check_issuing_country_inclusion( dg1: DG1Data, country_list: [Alpha3CountryCode; N], ) { + // Make sure the country list is properly padded with empty country codes at the end + // so that no additional country code can be injected after the first padding entry + check_country_list_padding(country_list); + let issuing_country_bytes = get_issuing_country_from_mrz(dg1); // Safety: get the index of the country in the list from an unconstrained function diff --git a/src/noir/lib/inclusion-check/country/src/tests.nr b/src/noir/lib/inclusion-check/country/src/tests.nr index 0e1ac97e3..5af9882ea 100644 --- a/src/noir/lib/inclusion-check/country/src/tests.nr +++ b/src/noir/lib/inclusion-check/country/src/tests.nr @@ -1,4 +1,7 @@ -use super::{check_issuing_country_inclusion, check_nationality_inclusion, unsafe_get_index}; +use super::{ + check_country_list_padding, check_issuing_country_inclusion, check_nationality_inclusion, + unsafe_get_index, +}; use utils::{constants::{EMPTY_ALPHA3_COUNTRY_CODE, SAMPLE_DG1}, types::Alpha3CountryCode}; fn pad_country_list(country_list: [Alpha3CountryCode; N]) -> [Alpha3CountryCode; 200] { @@ -47,3 +50,54 @@ fn test_check_issuing_country_inclusion_fail() { let country_list = pad_country_list(["FRA", "NZL", "USA"]); check_issuing_country_inclusion(SAMPLE_DG1, country_list); } + +#[test] +fn test_check_country_list_padding_padded() { + let country_list = pad_country_list(["AUS", "NZL", "USA"]); + check_country_list_padding(country_list); +} + +#[test] +fn test_check_country_list_padding_full_list() { + let country_list: [Alpha3CountryCode; 3] = ["AUS", "NZL", "USA"]; + check_country_list_padding(country_list); +} + +#[test] +fn test_check_country_list_padding_fully_empty() { + let country_list: [Alpha3CountryCode; 3] = + [EMPTY_ALPHA3_COUNTRY_CODE, EMPTY_ALPHA3_COUNTRY_CODE, EMPTY_ALPHA3_COUNTRY_CODE]; + check_country_list_padding(country_list); +} + +#[test(should_fail_with = "Country list cannot contain country codes after the padding")] +fn test_check_country_list_padding_injection_after_padding() { + let country_list: [Alpha3CountryCode; 4] = + ["AUS", EMPTY_ALPHA3_COUNTRY_CODE, "USA", EMPTY_ALPHA3_COUNTRY_CODE]; + check_country_list_padding(country_list); +} + +#[test(should_fail_with = "Country list cannot contain country codes after the padding")] +fn test_check_country_list_padding_starts_with_empty() { + let country_list: [Alpha3CountryCode; 3] = + [EMPTY_ALPHA3_COUNTRY_CODE, "AUS", EMPTY_ALPHA3_COUNTRY_CODE]; + check_country_list_padding(country_list); +} + +#[test(should_fail_with = "Country list cannot contain country codes after the padding")] +fn test_check_nationality_inclusion_fail_injection_after_padding() { + let mut country_list: [Alpha3CountryCode; 200] = [EMPTY_ALPHA3_COUNTRY_CODE; 200]; + country_list[0] = "AUS"; + // Inject a country code after the padding has started + country_list[5] = "USA"; + check_nationality_inclusion(SAMPLE_DG1, country_list); +} + +#[test(should_fail_with = "Country list cannot contain country codes after the padding")] +fn test_check_issuing_country_inclusion_fail_injection_after_padding() { + let mut country_list: [Alpha3CountryCode; 200] = [EMPTY_ALPHA3_COUNTRY_CODE; 200]; + country_list[0] = "AUS"; + // Inject a country code after the padding has started + country_list[5] = "USA"; + check_issuing_country_inclusion(SAMPLE_DG1, country_list); +} From 22100ccfbd21713c47afba92f76e23a43f6e9d65 Mon Sep 17 00:00:00 2001 From: Theo Madzou Date: Mon, 27 Apr 2026 08:49:23 +0200 Subject: [PATCH 2/3] format --- src/noir/lib/commitment/common/src/lib.nr | 6 +++--- src/noir/lib/inclusion-check/country/src/lib.nr | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/noir/lib/commitment/common/src/lib.nr b/src/noir/lib/commitment/common/src/lib.nr index 5f4c5eb57..a2f753fe8 100644 --- a/src/noir/lib/commitment/common/src/lib.nr +++ b/src/noir/lib/commitment/common/src/lib.nr @@ -219,9 +219,9 @@ pub fn hash_salt_country_signed_attr_dg1_e_content_private_nullifier( packed_e_content[i]; } hash_inputs[3 - + (SIGNED_ATTRS_LENGTH + 30) / 31 - + ((DG1_LENGTH + 30) / 31) - + ((ECONTENT_LENGTH + 30) / 31)] = private_nullifier; + + (SIGNED_ATTRS_LENGTH + 30) / 31 + + ((DG1_LENGTH + 30) / 31) + + ((ECONTENT_LENGTH + 30) / 31)] = private_nullifier; Poseidon2::hash( hash_inputs, diff --git a/src/noir/lib/inclusion-check/country/src/lib.nr b/src/noir/lib/inclusion-check/country/src/lib.nr index 545b04c2e..127b9301d 100644 --- a/src/noir/lib/inclusion-check/country/src/lib.nr +++ b/src/noir/lib/inclusion-check/country/src/lib.nr @@ -46,10 +46,7 @@ pub fn check_country_list_padding(country_list: [Alpha3CountryCode; found_padding = true; } if found_padding { - assert( - is_empty, - "Country list cannot contain country codes after the padding", - ); + assert(is_empty, "Country list cannot contain country codes after the padding"); } } } From d1f4c85c2c2825be2cf8be86c8f5e493d1a7efa5 Mon Sep 17 00:00:00 2001 From: Theo Madzou Date: Mon, 27 Apr 2026 09:05:19 +0200 Subject: [PATCH 3/3] fix formatting issue --- src/noir/lib/commitment/common/src/lib.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/noir/lib/commitment/common/src/lib.nr b/src/noir/lib/commitment/common/src/lib.nr index a2f753fe8..5f4c5eb57 100644 --- a/src/noir/lib/commitment/common/src/lib.nr +++ b/src/noir/lib/commitment/common/src/lib.nr @@ -219,9 +219,9 @@ pub fn hash_salt_country_signed_attr_dg1_e_content_private_nullifier( packed_e_content[i]; } hash_inputs[3 - + (SIGNED_ATTRS_LENGTH + 30) / 31 - + ((DG1_LENGTH + 30) / 31) - + ((ECONTENT_LENGTH + 30) / 31)] = private_nullifier; + + (SIGNED_ATTRS_LENGTH + 30) / 31 + + ((DG1_LENGTH + 30) / 31) + + ((ECONTENT_LENGTH + 30) / 31)] = private_nullifier; Poseidon2::hash( hash_inputs,