> Aztec.nr API reference, v6.0.0 (mainnet and testnet). Index: https://docs.aztec.network/aztec-nr-api/v6.0.0/llms.txt

# Function protect_from_forgery

Module: `noir_aztec::messages::delivery::handshake`

```rust
pub fn protect_from_forgery(
    secret: EmbeddedCurvePoint,
    eph_pk: EmbeddedCurvePoint,
    recipient_point: EmbeddedCurvePoint,
) -> EmbeddedCurvePoint
```

Protects a handshake's shared secret from recipient forgery.

The handshake's ECDH shared secret is symmetric: `S = eph_sk * recipient_pub`, where `(eph_sk, eph_pk)` is the sender's ephemeral key pair, also equals `recipient_priv * eph_pk`. The recipient recomputing `S` this way is normal: it is how they derive the handshake's tags when scanning. The attack is minting a second handshake with the same secret: the ephemeral key is unconstrained, so a malicious recipient can register its own handshake crafted to land on `S` again, with a different sender-only secret, and emit logs under the honest handshake's constrained-delivery tag. That breaks the invariant that a constrained-delivery log sequence is tied to a single sender: by colliding on the tag it blocks the honest sender, who can no longer emit the next note in their sequence.

The protection multiplies `S` by the scalar `k = hash(eph_pk, recipient_point)`: a forgery must announce a different `eph_pk`, so the protected secrets diverge. The result is still a point and silos like `S`.

The recipient point must be part of `k`: were `k` independent of it, an attacker choosing the fake recipient point as a multiple of the honest protected secret could solve for the multiplier that makes the forged secret land exactly on it.

Concretely, on the sender's side: S = eph_sk \* recipient_point S' = protect_from_forgery(S, eph_pk, recipient_point) = k \* S

See [`forgery_protected_eph_pk`](/aztec-nr-api/v6.0.0/noir_aztec/messages/delivery/handshake/fn.forgery_protected_eph_pk.md) for how that arrives at the same `S'`.
