Skip to main content

Under the Hood

This section is the engineer's view of SPECTER. It follows one payment from the keys that exist before anything happens to the signature that finally moves the funds, and it names the exact primitive at every step.

If you want the plain-language model first, read How SPECTER works. If you want to call this from code, read the SDK reference. This section sits between the two: it explains what the SDK and the API are actually doing.

The pipeline at a glance

A payment passes through five cryptographic stages. Each page below takes one stage apart.

1. Key generation

How the spending and viewing ML-KEM-768 keypairs are produced, and why there are two.

2. Shared secret

Encapsulation and decapsulation: how sender and recipient reach the same 32 bytes.

3. Stealth derivation

Turning the shared secret into a one-time address on Ethereum and Sui.

4. Scanning and spending

The view tag filter, the decapsulation check, and recovering the key that signs.

The objects and their sizes

Every artifact in SPECTER has a fixed size. Knowing them makes the rest of this section concrete.

ObjectSizeLivesSecret
Spending public key1,184 BIn the meta-addressNo
Spending secret key2,400 BRecipient deviceYes
Viewing public key1,184 BIn the meta-addressNo
Viewing secret key2,400 BRecipient deviceYes
Meta-address (serialized)2,369 BPublishedNo
Announcement ciphertext1,088 BOn-chain or in the registryNo
Shared secret32 BDerived on both sidesYes
View tag1 BIn the announcementNo
Stealth Ethereum address20 BOn-chainNo
Stealth Sui address32 BOn-chainNo
Recovered Ethereum private key32 BRecipient deviceYes

The public key and ciphertext sizes are ML-KEM-768 as standardized in FIPS 203.

The flow as one diagram

Two derivations from one secret

Almost everything after encapsulation comes from the 32-byte shared secret, expanded with domain-separated SHAKE-256 so that two different uses of the same secret never collide:

view_tag = SHAKE-256("SPECTER_VIEW_TAG" || shared_secret)[0]
stealth_key = SHAKE-256("SPECTER_STEALTH_PK" || shared_secret) combined with spending_pk

The domain strings are the separator. The same shared secret feeds both, but the prefixes guarantee the view tag tells you nothing about the stealth key. The exact constructions live in the SPECTER Rust core; this section describes what each one produces and why.