Skip to content

Blinding the locking keys in NUT-11 #290

@a1denvalu3

Description

@a1denvalu3

The problem

The current state of NUT-11 provides that we input the public key itself into the well known secret structure:

[
  "P2PK",
  {
    "nonce": "da62796403af76c80cd6ce9153ed3746",
    "data": "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
    "tags": [
      ["sigflag", "SIG_INPUTS"],
    ]
  }
]

This means that, during redemption, we provide the unblinded secret that exposes the public key to the Mint. If this public key represents a Nostr identity or in general is not ephemeral (e.g. is re-used multiple times), the Mint is able to tell who is claiming the payment.

Blinding the key

Let $P$ be the public key 033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e.
Instead of locking to $P$, we could lock to $P' = P + r \cdot G$, where $r$ is a random scalar.

Then the sender can include $r$ together with the payment, for example it could be included in the Proof object:

{
  "amount": int,
  "id": hex_str,
  "secret": str,
  "C": hex_str,
  "r": [hex_str, ...]
}

The receiver can calculate the correct signing key by combining their secret key $p$ and $r$: $k = p + r$.
So as you can see this requires no alterations to the verification logic of the Mint. It's all client side.

Result

With this hack, the Mint is no longer able to see the real public keys the ecash is being locked to.

Update - 2025-10-20

We've discussed the scheme further and improved on it, inspired by silent payments. The new scheme works as follows:

  1. $P$ is still the receiver's public key, and $p$ is the respective private key, known only to the receiver.
  2. The sender draws a random scalar $e \stackrel{\$}\leftarrow \mathbb{Z}_q$, where $q$ is the order of the secp256k1 curve, and sends $E = e \cdot G$ as p2pk_e additional field in the Proof object.
  3. The sender and the receiver (and no one else) can derive a unique public point through a Diffie-Hellman handshake $Z = e \cdot P = p \cdot E$.
  4. Once $Z$ is derived, the blinding factors follow with a KDF function:
    r_i = SHA256(domain_separator || x-only(Z) || keyset_id || i)
    Where $i \in [0, 9]$ such that each key gets its own blinding factor

The result is a new P2BK scheme which is truly silent in the sense that, even with the Mint eavesdropping on public channels, they wouldn't be able to unblind the keys in the secret.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions