How sealed-box encryption works
Mailbox Keys mentions that mail sealed to the
wallet — the default, no published key required — uses libsodium’s
crypto_box_seal. This page goes into what that actually does and why it’s
a good fit for encrypting straight to a Solana wallet address.
Why a “sealed box”?
Ordinary public-key encryption (a “box” in libsodium’s terms) is built for two people who both hold keypairs and want to authenticate each other: sender and recipient each contribute their own secret key, so the recipient can tell the message really came from that sender. A sealed box drops the sender’s half entirely. Sealing needs only the recipient’s public key — nothing the sender has is checked or provable afterward. That’s the right shape for mail delivery: any MX server should be able to encrypt to any recipient’s published wallet address without holding a keypair of its own, and the resulting ciphertext shouldn’t reveal who sent it.
From a wallet address to an encryption key
A Solana wallet address is an Ed25519 public key — the curve Solana uses for transaction signatures. Sealed boxes need an X25519 key instead, the curve used for key exchange. Both curves are two different coordinate systems over the same underlying curve (Curve25519), and there’s a standard, one-way conversion from an Ed25519 point to its X25519 counterpart. SithBit performs that conversion on the fly — no separate key is published on-chain for the default case:
- Encrypting: any sender’s mail server converts your wallet address (public) into the X25519 public key to seal to.
- Decrypting: only you can perform the matching conversion on your
secret side, because it needs the seed in your wallet keypair file — the
same file
solana-keygenorsithbit walletproduce, never published.
An address that isn’t a real Ed25519 point at all — notably a Program Derived Address, which has no private key — has no valid conversion and so can never receive sealed mail. This is also why delegated keys exist: hardware and browser wallets can sign with their Ed25519 key but never export the seed the conversion needs, so they publish a self-generated X25519 keypair instead and skip the conversion step entirely.
Sealing a message
Every time a message is sealed — regardless of whether the destination public key came from a wallet conversion or a published delegated key — the same steps run:
- Generate a brand-new X25519 keypair, used for this one message only.
- Run Elliptic-Curve Diffie-Hellman (ECDH) between that ephemeral secret key and the recipient’s X25519 public key. Both sides of an ECDH exchange land on the same point without either one ever transmitting its secret key — that shared point becomes the encryption key.
- Encrypt the plaintext with that shared secret using the XSalsa20-Poly1305 stream cipher, producing ciphertext plus a 16-byte authentication tag.
- Discard the ephemeral secret key. It is never stored or reused.
Throwing away the ephemeral key after one use means the exact same plaintext seals to different ciphertext every time, and nobody — not even the sender, moments later — can reconstruct that message’s shared secret again. It also means the sealed box carries no reusable identity: two messages from the same sender to the same recipient share nothing an observer could link together.
Opening a sealed box
The recipient runs the mirror image of sealing:
- Read the ephemeral public key from the front of the sealed box (see the wire format below — it’s always the first 32 bytes after the header).
- Run ECDH between their own X25519 secret key and that ephemeral public key. This lands on exactly the same point the sender computed in step 2 above — that’s the whole point of Diffie-Hellman: both sides derive an identical shared secret from different halves of the same exchange.
- Use the shared secret to verify the Poly1305 tag and decrypt.
If the tag doesn’t verify — wrong key, or the bytes were altered in transit — decryption fails outright rather than returning corrupted plaintext.
sithbit mail decrypt <file> --keypair <path to wallet keypair>
The wire format
A sealed envelope is a flat, self-describing byte string — no separate key exchange step, no round trip, nothing beyond the message itself needs to reach the recipient:
The ciphertext is exactly as long as the plaintext — sealed boxes use a stream cipher, not a block cipher, so there’s no padding to account for. This whole envelope is what actually gets pinned to IPFS; see IPFS storage: benefits to users for why encrypting before pinning matters given that anyone holding a CID can fetch the raw bytes.