feat: mask all ciphertext fields with Poseidon2-derived values#21009
feat: mask all ciphertext fields with Poseidon2-derived values#21009nchamo merged 8 commits intomerge-train/fairiesfrom
Conversation
| unconstrained fn derive_field_mask(shared_secret: Point, index: u32) -> Field { | ||
| poseidon2_hash_with_separator( | ||
| [shared_secret.x, shared_secret.y], | ||
| DOM_SEP__CIPHERTEXT_FIELD_MASK + index, |
There was a problem hiding this comment.
We are adding the index to the separator instead of the inputs to be consistent with extract_many_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_poseidon2_unsafe (another function in this file that does something similar)
…ask-all-ciphertext-fields # Conflicts: # docs/docs-developers/docs/resources/migration_notes.md
…ask-all-ciphertext-fields # Conflicts: # docs/docs-developers/docs/resources/migration_notes.md
nventuro
left a comment
There was a problem hiding this comment.
Almost there! Feel free to merge once you've addressed my last comments. Great work!
| let mask = unsafe { derive_field_mask(ciphertext_shared_secret, i as u32) }; | ||
| ciphertext[1 + i] += mask; | ||
| // Pad with random fields so that padding is indistinguishable from masked data fields. | ||
| for i in (1 + message_bytes_as_fields.len())..MESSAGE_CIPHERTEXT_LEN { |
There was a problem hiding this comment.
This is why let mut offset was useful 🫠
| for i in (1 + message_bytes_as_fields.len())..MESSAGE_CIPHERTEXT_LEN { | ||
| // Safety: we assume that the sender wants for the message to be private - a malicious one could simply | ||
| // reveal its contents publicly. It is therefore fine to trust the sender to provide random padding. | ||
| ciphertext[i] = unsafe { random() }; |
There was a problem hiding this comment.
See how scary it seems that here we do ciphertext[i] but above we did [i + 1]
There was a problem hiding this comment.
I wasn't that scared to be honest 😅
There was a problem hiding this comment.
``
for i in 0..len
ciphertext[offset + i]
😭
There was a problem hiding this comment.
It was literally like that before my PR 😅
|
|
||
| /// Removes the Poseidon2-derived mask from a ciphertext field. Returns the unmasked value if it fits in 31 bytes | ||
| /// (a content field), or zero if it doesn't (random padding). | ||
| fn unmask_field(shared_secret: Point, index: u32, masked: Field) -> Field { |
There was a problem hiding this comment.
I'd make this unconstrained (so that we dont accidentally call it in constranined fns) and have it return Option<Field>, and then explicitly above do unmask_field(...).unwrap_or(0), instead of hiding how we handle this here. Handling the failure to unmask above by explaining that those are fields that we'll probably ignore anyway is easier to follow/audit.
There was a problem hiding this comment.
You are right, that is much better
|
❌ Failed to cherry-pick to |
BEGIN_COMMIT_OVERRIDE chore: remove prefund env var in non local networks (#21095) feat: aztec new supporting multiple contract crates (#21007) feat!: Expose offchain effects when simulating/sending txs (#20563) chore: exclude auto-generated dirs from VS Code search (#20881) feat: improve oracle name prefixes (#21101) fix(pxe): correct contract class log DA gas metering from +2 to +1 (#21102) chore: remove stale aes comments (#21133) chore: add warning on invalid recipients (#21134) feat: mask ciphertext fields with Poseidon2-derived values (#21009) END_COMMIT_OVERRIDE
BEGIN_COMMIT_OVERRIDE chore: chonk proof compression poc (#20645) feat: Update L1 to L2 message APIs (#20913) fix: adapt chonk proof compression for v4 Translator layout (#21067) fix: omit bigint priceBumpPercentage from IPC config in testbench worker (#21086) feat: standby mode for prover broker (#21098) fix(p2p): remove default block handler in favor of block handler (#21105) chore: prepare barretenberg-rs for crates.io publishing (#20496) feat: reenable function selectors + additional validation in public setup allowlist (backport #20909, #21122) (#21129) chore: remove stale aes comments (#21133) chore: remove auto-tag job (#21127) feat: calldata length validation of public setup function allowlist (#21139) feat: run AVM NAPI simulations on dedicated threads instead of libuv pool (#21138) feat: Remove non-protocol contracts from public setup allowlist (#21154) feat!: Expose offchain effects when simulating/sending txs (backport #20563) (#21110) chore: bump minor version (#21171) chore: backport #21161 (tally slashing pruning improvements) to v4 (#21166) chore: More updated Alpha configuration (backport #21155) (#21165) fix(p2p): report most severe failure in runValidations (#21185) feat: add ergonomic conversions for Noir's `Option<T>` (#21107) docs: clarifying Noir fields vs struct fields in event metadata (#21172) fix: bump lighthouse consensus client v7.1.0 -> v8.0.1 (#21170) fix: update dependencies (#20997) chore: New alpha-net environment (#20800) (#21202) chore: code decuplication + refactor (public setup allowlist) (#21200) feat: mask all ciphertext fields with Poseidon2-derived values (backport #21009) (#21140) chore: disable sponsored FPC in testnet (#21235) feat!: exposing pub event pagination on wallet (#21197) refactor(pxe): narrow tryGetPublicKeysAndPartialAddress return type (backport #21208) (#21236) feat: orchestrator enqueues via serial queue (#21247) feat: rollup mana limit gas validation (#21219) chore: deploy SPONSORED_FPC in test networks (#21254) fix(sequencer): fix log when not enough txs (#21297) END_COMMIT_OVERRIDE --------- Co-authored-by: ledwards2225 <ledwards2225@users.noreply.github.com> Co-authored-by: PhilWindle <PhilWindle@users.noreply.github.com> Co-authored-by: ludamad <adam.domurad@gmail.com> Co-authored-by: mrzeszutko <mrzeszutko@users.noreply.github.com> Co-authored-by: spalladino <spalladino@users.noreply.github.com> Co-authored-by: johnathan79717 <johnathan79717@users.noreply.github.com> Co-authored-by: nventuro <nventuro@users.noreply.github.com> Co-authored-by: alexghr <alexghr@users.noreply.github.com> Co-authored-by: AztecBot <AztecBot@users.noreply.github.com> Co-authored-by: Martin Verzilli <martin@aztec-labs.com> Co-authored-by: PhilWindle <60546371+PhilWindle@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: mverzilli <mverzilli@users.noreply.github.com> Co-authored-by: benesjan <benesjan@users.noreply.github.com> Co-authored-by: danielntmd <danielntmd@users.noreply.github.com> Co-authored-by: deffrian <deffrian@users.noreply.github.com> Co-authored-by: benesjan <janbenes1234@gmail.com>
BEGIN_COMMIT_OVERRIDE chore: chonk proof compression poc (#20645) feat: Update L1 to L2 message APIs (#20913) fix: adapt chonk proof compression for v4 Translator layout (#21067) fix: omit bigint priceBumpPercentage from IPC config in testbench worker (#21086) feat: standby mode for prover broker (#21098) fix(p2p): remove default block handler in favor of block handler (#21105) chore: prepare barretenberg-rs for crates.io publishing (#20496) feat: reenable function selectors + additional validation in public setup allowlist (backport #20909, #21122) (#21129) chore: remove stale aes comments (#21133) chore: remove auto-tag job (#21127) feat: calldata length validation of public setup function allowlist (#21139) feat: run AVM NAPI simulations on dedicated threads instead of libuv pool (#21138) feat: Remove non-protocol contracts from public setup allowlist (#21154) feat!: Expose offchain effects when simulating/sending txs (backport #20563) (#21110) chore: bump minor version (#21171) chore: backport #21161 (tally slashing pruning improvements) to v4 (#21166) chore: More updated Alpha configuration (backport #21155) (#21165) fix(p2p): report most severe failure in runValidations (#21185) feat: add ergonomic conversions for Noir's `Option<T>` (#21107) docs: clarifying Noir fields vs struct fields in event metadata (#21172) fix: bump lighthouse consensus client v7.1.0 -> v8.0.1 (#21170) fix: update dependencies (#20997) chore: New alpha-net environment (#20800) (#21202) chore: code decuplication + refactor (public setup allowlist) (#21200) feat: mask all ciphertext fields with Poseidon2-derived values (backport #21009) (#21140) chore: disable sponsored FPC in testnet (#21235) feat!: exposing pub event pagination on wallet (#21197) refactor(pxe): narrow tryGetPublicKeysAndPartialAddress return type (backport #21208) (#21236) feat: orchestrator enqueues via serial queue (#21247) feat: rollup mana limit gas validation (#21219) chore: deploy SPONSORED_FPC in test networks (#21254) fix(sequencer): fix log when not enough txs (#21297) fix: Simulate gas in n tps test. Set min txs per block to 1 (backport #21312) (#21329) fix(log): do not log validation error if unregistered handler (#21111) fix(node): fix index misalignment in findLeavesIndexes (#21327) fix: limit parallel blocks in prover to max AVM parallel simulations (#21320) fix: use native sha256 to speed up proving job id generation (#21292) fix(validator): wait for l1 sync before processing block proposals (#21336) fix(txpool): cap priority fee with max fees when computing priority (#21279) chore: reduce severity of errors due to HA node not acquiring signature (#21311) fix: (A-643) add buffer to maxFeePerBlobGas for gas estimation and fix bump loop truncation (#21323) END_COMMIT_OVERRIDE
Why we are doing this
AES-encrypted logs pack byte data into fields at 31 bytes per field. This means no field ever contains a value larger than 2^248, while other log types might use full field values (up to ~2^254). An external observer can exploit this to identify which logs are AES-encrypted just by checking whether all field values fit in 248 bits, breaking the indistinguishability goal.
The same problem applies to trailing padding fields. Previously these were filled with random 31-byte values (via unconstrained oracles), which also never exceeded 2^248.
Our fix
Content fields (those carrying actual packed message bytes) are masked by adding a Poseidon2-derived field element keyed on the ECDH shared secret and field index:
Padding fields (trailing fields that fill the ciphertext to a fixed 15-field size) use
random()instead, since their values are irrelevant to the recipient and discarded during decryption.The result: all 15 output fields (except the ephemeral public key) appear as uniformly random
Fieldvalues to any observer without knowledge of the shared secret.Migration
get_random_byteshas been removed fromaztec::utils::random. Replace with direct calls to therandomoracle fromaztec::oracle::random.Fixes F-369
Fixes #12749