Skip to content

Instantly share code, notes, and snippets.

@jeffro256
Created December 12, 2024 02:17
Show Gist options
  • Save jeffro256/9b2bdb8f40ed1103d3041f02708ba3ec to your computer and use it in GitHub Desktop.
Save jeffro256/9b2bdb8f40ed1103d3041f02708ba3ec to your computer and use it in GitHub Desktop.
SOME CATS

Short On-chain Memos Encrypted in Carrot Anchors in Two-out Selfsends

What

SOME CATS is a scheme to send 16-byte transaction memos in 2-out transactions using Carrot. SOME CATS memos are:

  • Encrypted - Memos are encrypted to the receiver, optionally visible to the sender as well
  • Indistinguishable - Transactions containing a SOME CATS memo are indistinguishable from normal Carrot transactions
  • Available on-chain - The memo will always be available on-chain as long as its associated transaction is
  • Receiver agnostic - The receiver doesn't need to support SOME CATS to spend funds sent in a SOME CATS transaction, so long as they support the Carrot addressing protocol

Notation

See the Carrot notation section.

We also introduce two functions: cipher(k, p) and decipher(k, c) which perform symmetric block cipher operations on 16-byte (128-bit) blocks. One implementation we could use in practice is Twofish. They have the relationship:

decipher(k, cipher(k, p)) = p
cipher(k, decipher(k, c)) = c

How

Sender Construction

Given 16-byte message m and receiver address (Ksj, Kvj):

  1. If the sender wants to remember the memo, she sets anchorrecv = SecretDerive("somecats sender-view memo anchor" || svb || input_context), otherwise she sets anchorrecv = RandBytes(16)
  2. Sender derives the memo cipher secret smemo = SecretDerive("somecats cipher secret" || anchorrecv || input_context || Ksj || Kvj || pid)
  3. Sender sets mc = cipher(smemo, m)
  4. Sender constructs external enote to the receiver E1 with anchor = anchorrecv, instead of the regular anchor = RandBytes(16)
  5. Sender constructs internal selfsend enote E2 with anchorenc = mc, skipping regular anchor encryption
  6. Sender constructs transaction containing enotes E1 and E2

Reading the Memo

  1. During the enote scan process, while checking against Janus attacks, the receiver will decrypt anchor
  2. Receiver derives the memo cipher secret smemo = SecretDerive("somecats cipher secret" || anchor || input_context || Ksj || Kvj || pid)
  3. Receiver deciphers the memo m = decipher(smemo, anchorencother), where anchorencother is the encrypted anchor on the other enote

If the sender chose to derive anchor deterministically, then they can obtain anchor = SecretDerive("somecats sender-view memo anchor" || svb || input_context), replacing step 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment