Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::protocol::{traits::{Deserialize, Serialize}, utils::reader::Reader};
use crate::context::PrivateContext;
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}, utils::reader::Reader};
use super::tag_secret_source::TagSecretSource;

global NON_INTERACTIVE_HANDSHAKE: u8 = 1;
Expand Down Expand Up @@ -27,6 +28,20 @@ impl ResolvedTaggingStrategy {
self.kind == UNCONSTRAINED_SECRET
}

/// Resolves this strategy into the [`TagSecretSource`] backing the message tag.
pub(crate) fn into_tag_secret_source(
self,
context: &mut PrivateContext,
sender: AztecAddress,
recipient: AztecAddress,
) -> TagSecretSource {
if self.kind == NON_INTERACTIVE_HANDSHAKE {
TagSecretSource::new_non_interactive_handshake(context, sender, recipient)
} else {
TagSecretSource::unconstrained_secret(self.secret)
}
}

/// Validates a raw discriminant, as deserialization must always reject unknown values.
fn from_parts(kind: u8, secret: Field) -> Self {
let resolved = Self { kind, secret };
Expand All @@ -52,16 +67,6 @@ impl Deserialize for ResolvedTaggingStrategy {
}
}

impl From<ResolvedTaggingStrategy> for TagSecretSource {
fn from(strategy: ResolvedTaggingStrategy) -> Self {
if strategy.kind == NON_INTERACTIVE_HANDSHAKE {
TagSecretSource::new_non_interactive_handshake()
} else {
TagSecretSource::unconstrained_secret(strategy.secret)
}
}
}

mod test {
use crate::protocol::traits::{Deserialize, Serialize};
use super::ResolvedTaggingStrategy;
Expand Down
22 changes: 11 additions & 11 deletions noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use crate::context::PrivateContext;
use crate::messages::delivery::{
constrained_delivery::emit_sequence_nullifier,
OnchainDeliveryMode,
tag_derivation::{existing_handshake_secrets_or_else, TagDerivation},
tag_secret_source::TagSecretSource,
Expand All @@ -28,15 +27,17 @@ pub(crate) fn derive_log_tag(
recipient: AztecAddress,
tag_derivation: Option<TagDerivation>,
) -> Field {
let source = tag_derivation.map_or_else(|| default_tag_secret_source(sender, recipient, mode), |derivation| {
derivation.into_tag_secret_source(sender, recipient)
});
let source = tag_derivation.map_or_else(
|| default_tag_secret_source(context, sender, recipient, mode),
|derivation| derivation.into_tag_secret_source(context, sender, recipient),
);
tag_with(source, context, mode, sender, recipient)
}

/// The wallet-resolved default: reuse a handshake already registered for the pair, otherwise let the wallet resolve
/// the tagging secret strategy.
fn default_tag_secret_source(
context: &mut PrivateContext,
sender: AztecAddress,
recipient: AztecAddress,
mode: OnchainDeliveryMode,
Expand All @@ -49,7 +50,7 @@ fn default_tag_secret_source(
// secret before a constrained tag is emitted, so an untrusted strategy can't produce a valid unbacked
// constrained tag.
let strategy = unsafe { resolve_tagging_strategy(sender, recipient, mode) };
strategy.into()
strategy.into_tag_secret_source(context, sender, recipient)
},
)
}
Expand All @@ -61,19 +62,18 @@ fn tag_with(
sender: AztecAddress,
recipient: AztecAddress,
) -> Field {
let secrets = source.obtain_secrets(context, sender, recipient);
let secret = source.tag_secret();

// Safety: the index is untrusted. Constrained delivery constrains it below before emitting the tag; unconstrained
// discovery tolerates gaps, so a wrong index only yields an undiscoverable tag.
let index = unsafe { get_next_tagging_index(secrets.shared, mode) };
let index = unsafe { get_next_tagging_index(secret, mode) };

if mode == OnchainDeliveryMode::onchain_constrained() {
source.constrain_secrets(context, sender, recipient, secrets, index);
emit_sequence_nullifier(context, sender, recipient, secrets, index);
source.constrain_tag_secret(context, sender, recipient, index);
}

// The discovery tag stays derived from the shared secret only, so the recipient still finds every message.
Comment thread
nchamo marked this conversation as resolved.
tag_from_secret_and_index(secrets.shared, index, mode)
// The discovery tag stays derived from the tag secret only, so the recipient still finds every message.
tag_from_secret_and_index(secret, index, mode)
}

fn tag_domain_separator(mode: OnchainDeliveryMode) -> u32 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::context::PrivateContext;
use crate::messages::delivery::{
handshake::get_existing_app_siloed_handshake_secrets, tag_secret_source::TagSecretSource,
};
Expand Down Expand Up @@ -39,13 +40,17 @@ impl TagDerivation {
}

/// Resolves this choice into the [`TagSecretSource`] backing the message tag.
pub(crate) fn into_tag_secret_source(self, sender: AztecAddress, recipient: AztecAddress) -> TagSecretSource {
pub(crate) fn into_tag_secret_source(
self,
context: &mut PrivateContext,
sender: AztecAddress,
recipient: AztecAddress,
) -> TagSecretSource {
if self.kind == NON_INTERACTIVE_HANDSHAKE {
// A fresh handshake is created in a constrained manner in `obtain_secrets`.
existing_handshake_secrets_or_else(
sender,
recipient,
|| TagSecretSource::new_non_interactive_handshake(),
|| TagSecretSource::new_non_interactive_handshake(context, sender, recipient),
)
} else {
// Safety: the secret is untrusted, but address-derived tagging is unconstrained-only, where a wrong
Expand Down Expand Up @@ -73,8 +78,11 @@ pub(crate) fn existing_handshake_secrets_or_else<Env>(
}

mod test {
use crate::context::PrivateContext;
use crate::hash::hash_args;
use crate::messages::delivery::handshake::AppSiloedHandshakeSecrets;
use crate::messages::delivery::tag_secret_source::TagSecretSource;
use crate::oracle::execution_cache;
use crate::protocol::{address::AztecAddress, traits::{FromField, Serialize}};
use crate::test::helpers::test_environment::TestEnvironment;
use super::TagDerivation;
Expand All @@ -88,10 +96,10 @@ mod test {
let env = TestEnvironment::new();
let secret: Field = 7;

env.private_context(|_context| {
env.private_context(|context| {
let _ = OracleMock::mock("aztec_prv_getAppTaggingSecret").returns(Option::some(secret));

let source = TagDerivation::address_derived().into_tag_secret_source(SENDER, RECIPIENT);
let source = TagDerivation::address_derived().into_tag_secret_source(context, SENDER, RECIPIENT);
assert_eq(source, TagSecretSource::unconstrained_secret(secret));
});
}
Expand All @@ -101,11 +109,11 @@ mod test {
let env = TestEnvironment::new();
let random_secret: Field = 999;

env.private_context(|_context| {
env.private_context(|context| {
let _ = OracleMock::mock("aztec_prv_getAppTaggingSecret").returns(Option::<Field>::none());
let _ = OracleMock::mock("aztec_misc_getRandomField").returns(random_secret);

let source = TagDerivation::address_derived().into_tag_secret_source(SENDER, RECIPIENT);
let source = TagDerivation::address_derived().into_tag_secret_source(context, SENDER, RECIPIENT);
assert_eq(source, TagSecretSource::unconstrained_secret(random_secret));
});
}
Expand All @@ -115,27 +123,39 @@ mod test {
let env = TestEnvironment::new();
let secrets = AppSiloedHandshakeSecrets { shared: 7, sender_only: 99 };

env.private_context(|_context| {
env.private_context(|context| {
mock_existing_handshake_secrets(Option::some(secrets));

let source = TagDerivation::non_interactive_handshake().into_tag_secret_source(SENDER, RECIPIENT);
let source = TagDerivation::non_interactive_handshake().into_tag_secret_source(context, SENDER, RECIPIENT);
assert_eq(source, TagSecretSource::existing_handshake(secrets));
});
}

#[test]
unconstrained fn non_interactive_handshake_creates_a_fresh_one_when_none_exists() {
let env = TestEnvironment::new();
let secrets = AppSiloedHandshakeSecrets { shared: 7, sender_only: 99 };

env.private_context(|_context| {
env.private_context(|context| {
mock_existing_handshake_secrets(Option::none());
mock_handshake_creation(context, secrets);

let source = TagDerivation::non_interactive_handshake().into_tag_secret_source(SENDER, RECIPIENT);
assert_eq(source, TagSecretSource::new_non_interactive_handshake());
let source = TagDerivation::non_interactive_handshake().into_tag_secret_source(context, SENDER, RECIPIENT);
assert_eq(source.tag_secret(), secrets.shared);
});
}

unconstrained fn mock_existing_handshake_secrets(maybe_secrets: Option<AppSiloedHandshakeSecrets>) {
let _ = OracleMock::mock("aztec_utl_callUtilityFunction").returns(maybe_secrets.serialize());
}

// The registry's `non_interactive_handshake` call returns the app-siloed secrets via the returns cache.
unconstrained fn mock_handshake_creation(context: &mut PrivateContext, secrets: AppSiloedHandshakeSecrets) {
let returns = secrets.serialize();
let returns_hash = hash_args(returns);
execution_cache::store(returns, returns_hash);
let child_call_end_counter = context.get_side_effect_counter() + 1;
let _ =
OracleMock::mock("aztec_prv_callPrivateFunction").returns((child_call_end_counter, returns_hash)).times(1);
}
}
Loading
Loading