Skip to main content

Memo Block Encryption

The main purpose of the memo block is to publish transaction details. Due to the secret nature of a transaction, the list of admitted users able to access data is strictly restricted. There are two critical data components encrypted in the memo block: an output account and output notes.

Only the transaction sender can access the output account and notes. The output account is used by the sender to maintain the wallet state, and notes are used to retrieve the outgoing history of transactions. The output account should not be available to the transaction receiver. Shared secrets are used to implement this feature.

There are three encrypted entities in the memo block.

  • Account Encryption
  • Notes Encryption
  • Shared Secrets Encryption

Account Encryption

An output account in the memo block is encrypted with the random key keyakey_a by the symmetric algorithm ChaCha20Poly1305:

  • Generate a random symmetric 256-bit key: keya=random()key_a = random()
  • Encrypt output account: accenc=ChaCha20Poly1305keyanonce(acc)acc^{enc} = ChaCha20Poly1305_{key_a}^{nonce}(acc)
info

Nonce for ChaCha20Poly1305ChaCha20Poly1305 is a fixed value. It's the first 12 bytes of the keccak256("ZeroPool")keccak256("ZeroPool"): 0x5bbdffc6fe73c460f1b2b85d

To decrypt output account user should obtain keyakey_a from the memo block. The transaction sender only can access this key.

Notes Encryption

Output notes are encrypted with the ephemeral keys. Steps to encrypt a NoteiNote_i:

  • Generate a random 256-bit ephemeral secret key: ai=random()a_i = random()
  • Calculate an ephemeral public key for the NoteiNote_i: Ai=aiToSubGroupHashE(Fr)(Notei.d)A_i = a_i \text{ToSubGroupHash}_{E(F_r)}(Note_i.d)
  • Derive a symmetric encryption key for the note: keyi=keccak256(aiNotei.Pd)key_i = keccak256(a_i Note_i.P_d)
  • Encrypt note: Noteienc=ChaCha20Poly1305keyinonce(Notei)Note_i^{enc} = ChaCha20Poly1305_{key_i}^{nonce}(Note_i)
  • There are two public values for the each note in the memo block: AiA_i and NoteiencNote_i^{enc}

To decrypt a note the user obtains the corresponding keyikey_i. There are two ways to get it:

  • to obtain keyikey_i from the shared secrets (a sender case)
  • to derive keyikey_i from the AiA_i and account's key η\eta (a receiver case): keyi=keccak256(Aiη)key_i = keccak256(A_i \eta)

Shared secrets encryption

Shared secrets block contains symmetric keys for account and notes encryption: keys=(keya,key1,key2,...)keys = (key_a, key_1, key_2, ...). The following actions are used to encrypt these keys:

  • Generate a random 256-bit key: ap=random()a_p = random()
  • Calculate an ephemeral public key for the shared secrets: Ap=apGA_p = a_p G
  • Derive a symmetric encryption key for the shared secrets: keyp=keccak256(Apacc.η)key_p = keccak256(A_p acc.\eta)
  • Encrypt keyskeys: keysenc=ChaCha20Poly1305keypnonce(keys)keys^{enc} = ChaCha20Poly1305_{key_p}^{nonce}(keys)
  • Put (Ap,keysenc)(A_p, keys^{enc}) in the shared secrets block
info

keysenckeys^{enc} could be decrypted with the η\eta key only.