Summary
Two related migration-state findings in the Noir circuit crate (noir/crates/dg1, package noir). Filed for visibility; no PR is attached see "Why no PR" below.
This is a migration-state correctness gap, not an exploitable production issue: production proving is Circom; the Noir crate is pre-alpha. Severity framed accordingly.
Finding 1 — verify_econtent is an empty no-op in the Dg1 path
noir/crates/dg1/src/dg1/dg1.nr:22-29:
// todo
pub fn verify_econtent(
eContent: [u8; MAX_ECONTENT_LEN],
signed_attr: [u8; MAX_SIGNED_ATTR_LEN],
signed_attr_econtent_hash_offset: u8,
eContent_padded_length: u16,
signed_attr_padded_length: u8,
) {}
It is called unconditionally from the main entry point noir/crates/dg1/src/dg1/mod.nr:18-24, so the Noir Dg1() path currently performs no eContent → signedAttr hash binding. The Circom reference enforces this at circuits/circuits/utils/passport/passportVerifier.circom:78-86 (compute SHA256(eContent, eContent_padded_length), assert it equals the ECONTENT_HASH_ALGO_BYTES slice of signed_attr at signed_attr_econtent_hash_offset).
Intended 1:1 parity implementation (mirrors the sibling verify_dg1_and_dg2 in the same file, which already uses std::hash::sha256 directly; avoids depending on the unwired eContent/ module):
let computed = std::hash::sha256::sha256_var(eContent, eContent_padded_length as u64);
for i in 0..HASH_LEN_BYTES {
assert_eq(computed[i], signed_attr[signed_attr_econtent_hash_offset as u32 + i as u32]);
}
(signed_attr_padded_length is unused by this check it belongs to signed-attr hashing / signature verification, which is out of scope for verify_econtent.)
Finding 2 — dg1 crate does not compile under current Noir
Baseline nargo test --package noir fails with 49 errors before any test runs. Dominant cause: Could not resolve 'poseidon' — std::hash::poseidon::bn254::hash_2 / hash_9 no longer resolves (e.g. crates/dg1/src/ofac/ofac_name_dob.nr:34, crates/dg1/src/ofac/ofac_passport_number.nr:16), plus dependent type-inference failures.
Toolchain mismatch: installed nargo 1.0.0-beta.19 (which removed std::hash::poseidon) vs. code targeting older Noir (Nargo.toml: compiler_version >=0.36.0; AGENTS.md pin example: noirup -v v0.31.0). The errors are spread across ofac//utils/ and are unrelated to verify_econtent.
Why no PR
TDD requires a green baseline. With the crate failing to compile, a verify_econtent test cannot be observed failing then passing , the runner aborts on the 49 unrelated errors. Submitting an unverified implementation would violate "no untested code." The parity implementation above is scoped and understood but intentionally not submitted until the crate compiles under a pinned toolchain.
Migration context
Related to #2082 (domain-separation hardening to build into the Noir implementation). Both point to the Noir migration being earlier-stage than the present stubs suggest: the eContent-binding check is absent, and the crate is not buildable on current Noir without a poseidon/std-API port.
Suggested sequencing (for maintainers)
- Pin/port the
dg1 crate to a known-good Noir version (resolve the std::hash::poseidon drift across ofac/).
- Once the crate compiles, land
verify_econtent via the parity implementation above with a failing-then-passing test (happy path + a tamper/negative case proving the binding is enforced).
cc @transphorm @ericnakagawa @michaelelliot
Summary
Two related migration-state findings in the Noir circuit crate (
noir/crates/dg1, packagenoir). Filed for visibility; no PR is attached see "Why no PR" below.This is a migration-state correctness gap, not an exploitable production issue: production proving is Circom; the Noir crate is pre-alpha. Severity framed accordingly.
Finding 1 —
verify_econtentis an empty no-op in theDg1pathnoir/crates/dg1/src/dg1/dg1.nr:22-29:It is called unconditionally from the main entry point
noir/crates/dg1/src/dg1/mod.nr:18-24, so the NoirDg1()path currently performs no eContent → signedAttr hash binding. The Circom reference enforces this atcircuits/circuits/utils/passport/passportVerifier.circom:78-86(computeSHA256(eContent, eContent_padded_length), assert it equals theECONTENT_HASH_ALGO_BYTESslice ofsigned_attratsigned_attr_econtent_hash_offset).Intended 1:1 parity implementation (mirrors the sibling
verify_dg1_and_dg2in the same file, which already usesstd::hash::sha256directly; avoids depending on the unwiredeContent/module):(
signed_attr_padded_lengthis unused by this check it belongs to signed-attr hashing / signature verification, which is out of scope forverify_econtent.)Finding 2 —
dg1crate does not compile under current NoirBaseline
nargo test --package noirfails with 49 errors before any test runs. Dominant cause:Could not resolve 'poseidon'—std::hash::poseidon::bn254::hash_2/hash_9no longer resolves (e.g.crates/dg1/src/ofac/ofac_name_dob.nr:34,crates/dg1/src/ofac/ofac_passport_number.nr:16), plus dependent type-inference failures.Toolchain mismatch: installed
nargo 1.0.0-beta.19(which removedstd::hash::poseidon) vs. code targeting older Noir (Nargo.toml:compiler_version >=0.36.0;AGENTS.mdpin example:noirup -v v0.31.0). The errors are spread acrossofac//utils/and are unrelated toverify_econtent.Why no PR
TDD requires a green baseline. With the crate failing to compile, a
verify_econtenttest cannot be observed failing then passing , the runner aborts on the 49 unrelated errors. Submitting an unverified implementation would violate "no untested code." The parity implementation above is scoped and understood but intentionally not submitted until the crate compiles under a pinned toolchain.Migration context
Related to #2082 (domain-separation hardening to build into the Noir implementation). Both point to the Noir migration being earlier-stage than the present stubs suggest: the eContent-binding check is absent, and the crate is not buildable on current Noir without a poseidon/std-API port.
Suggested sequencing (for maintainers)
dg1crate to a known-good Noir version (resolve thestd::hash::poseidondrift acrossofac/).verify_econtentvia the parity implementation above with a failing-then-passing test (happy path + a tamper/negative case proving the binding is enforced).cc @transphorm @ericnakagawa @michaelelliot