The empty seat

The Ring’s auditor key is a public key with no private half. Anyone can recompute it from one sentence.

The auditor key in a custom ring

The Ring is a custom ring on Solana Privacy Rings. A custom ring fixes an auditor key when it is initialized, and every transfer, deposit and merge carries a proof that it is sealed to that key.

Whoever holds the private half of an auditor key can open the assets, amounts and owners of everything in that ring, and on an anonymous ring the hidden sender too. The auditor key reads. It does not move funds.

The sentence

Offrecord’s auditor key derives from this sentence:

The sentence
Offrecord seats no auditor. Nobody holds the key to this Ring.

The sentence is a constant in the Ring program, in the SDK as AUDITOR_SENTENCE, and on this site.

From sentence to key

SHA-256 over the UTF-8 bytes of the sentence gives 32 bytes. Those 32 bytes are read as an X25519 public key. X25519 reads a public key as 255 bits, so the top bit of the last byte is cleared. The result is the auditor key.

Sentence
Offrecord seats no auditor. Nobody holds the key to this Ring.
Derivation
SHA-256, read as an X25519 public key
Public half
ee766099e0a71187667175083067904c23178166396bf6d42eb95e211dbeee1d
Private half
The auditor key has a public half, computed here from the sentence. The seat for its private half is empty.

Why no private half exists

An X25519 key pair starts from a private scalar, and the public key is computed from it. The auditor key was made from the other side: its public bytes are the output of a hash, and no scalar was generated at any point.

A private half would be a scalar that multiplies the Curve25519 base point to exactly this hash output. Finding it is the discrete logarithm problem the curve is built on.

Every transfer in the Ring is still sealed and still proven well formed. The reader it is sealed to does not exist.

Fixed for the Ring’s life

initialize writes the auditor key to the Ring account and to the Ring’s enrollment registry. The program has no instruction that replaces it.

Recompute it

Web Crypto is enough. This runs in any browser console:

recompute.ts
const sentence = 'Offrecord seats no auditor. Nobody holds the key to this Ring.'

const digest = new Uint8Array(
  await crypto.subtle.digest('SHA-256', new TextEncoder().encode(sentence)),
)
// X25519 reads 255 bits: clear the top bit of the last byte.
digest[31] &= 0x7f

const key = Array.from(digest, (b) => b.toString(16).padStart(2, '0')).join('')

Then compare it with the key on the Ring account. The SDK exports the same derivation as auditorKeyFromSentence:

match.ts
import { AUDITOR_SENTENCE, auditorKeyFromSentence, createOffrecord } from '@offrecord/sdk'

const offrecord = createOffrecord({ apiUrl })
const ring = await offrecord.ring()

const match = (await auditorKeyFromSentence(AUDITOR_SENTENCE)) === ring.auditor.key

ring.auditor.key is the 32 bytes as lowercase hex. The Ring account stores them behind a one-byte tag, 0x04.