# Sign & encrypt as your agent

**One key, already in DNS. Sign a document so anyone can verify it, or encrypt a file so only one agent can open it - no CA, no key exchange, no shared secret to leak.**

Your agent already carries a per-agent EC P-256 key, minted for its own `/128` and pinned in DNSSEC-signed DNS - the same key its [DANE `TLSA`](/docs/dane) record and [per-agent certificate](/docs/per-agent-ca) resolve identity against. Two everyday jobs fall straight out of that one key, one command each, and both anchor in DNSSEC instead of a certificate authority you have to install and trust:

| You want to | Command | The other side | Anchor |
|---|---|---|---|
| Sign a file, PDF or email as your agent | `whisper sign file` | Anyone verifies - **no key** | `SMIMEA` (RFC 8162) |
| Encrypt a file *for* an agent | `whisper encrypt --to` | Only that agent decrypts | `OPENPGPKEY` (RFC 7929) |

The shape mirrors [identity verification](/docs/verifiable-identity): the privileged half needs your key, the public half needs nothing. You sign with your key; a stranger verifies with none. Anyone encrypts to your agent with no credentials at all; only your agent, holding its identity, can open it. Trust is the DNSSEC chain from the IANA root, re-derived in-process - not a CA bundle, not a vendor "trust us" endpoint.

## Sign a document or email

`whisper sign file` issues your per-agent *emailProtection* certificate from the control plane (this half needs your API key) and produces a detached S/MIME (CMS / PKCS#7) signature over the bytes. Hand the `.p7s` to anyone:

```
whisper sign file contract.pdf
# -> signed contract.pdf as agent@a1b2c3.agents.whisper.online -> contract.pdf.p7s
#    verify (no key): whisper sign verify contract.pdf --sig contract.pdf.p7s
```

The same certificate signs email: it carries an `emailProtection` EKU and an `rfc822Name` SAN, so a standard S/MIME client (or `openssl smime -sign`) produces a signed message any mail client can check.

## Verify with no key

Verification is keyless and needs no Whisper software of the signer's - just the ability to validate DNSSEC, which the command does itself from the IANA root:

```
whisper sign verify contract.pdf --sig contract.pdf.p7s
# -> VERIFIED - signature is valid and the signer key is DANE-anchored by the
#    DNSSEC SMIMEA for agent@a1b2c3.agents.whisper.online (spki sha256 54f0afc7...)
#    Trust: DNSSEC/SMIMEA, not a public S/MIME CA.
```

The trust is DANE, end to end. The `SMIMEA` record (RFC 8162, DANE-EE `3 1 1`) pins the signer's *exact* key under the signer's mailbox, DNSSEC-signed; the verifier matches the CMS signer's key to that pin and needs no public S/MIME CA and no pre-installed trust anchor. Tamper with a single byte and the check fails closed (`Message digest mismatch`).

## Encrypt a file for an agent

Encryption is the mirror image - the *public* half needs no key. Point `whisper encrypt` at any agent's name and it seals the data so only that agent can open it:

```
whisper encrypt --to a1b2c3.agents.whisper.online secret.txt
# -> encrypted for a1b2c3.agents.whisper.online -> secret.txt.wenc
```

No API key changes hands. The recipient's public key comes straight from its DNSSEC-signed `OPENPGPKEY` record (RFC 7929) - the same per-agent key the `TLSA` and `SMIMEA` pin - validated from the IANA root before anything is sealed, so you can only ever encrypt to a key the DNSSEC chain vouches for. The encryption is **HPKE** (RFC 9180): `DHKEM(P-256, HKDF-SHA256) + HKDF-SHA256 + AES-256-GCM`. The output is a portable `WHISPER ENCRYPTED MESSAGE` block; recipient and ciphersuite are bound into the message so it cannot be re-targeted or downgraded.

## Decrypt as the agent

The agent opens it with its own identity (fetched from the control plane, or supplied with `--key` for a sole-control identity):

```
whisper decrypt secret.txt.wenc
# -> decrypted secret.txt.wenc -> secret.txt (encrypted for a1b2c3.agents.whisper.online)
```

A message sealed to a different agent won't open, and a corrupted message fails closed rather than returning garbage.

## With stock tools - no Whisper software at all

None of this is Whisper plumbing you have to trust on faith. The agent's keys live in standard DNS records (`SMIMEA` RFC 8162, `OPENPGPKEY` RFC 7929), the signatures are CMS (RFC 5652), and the encryption is HPKE (RFC 9180). Everything the `whisper` CLI does you can do, or independently re-check, with `dig`, `openssl` and `gpg`:

```
# The agent's signing pin and public key are just DNS records, DNSSEC-signed (AD=YES):
dig +dnssec SMIMEA d4f0bc5a29de06b510f9aa428f1eedba926012b591fef7a518e776a7._smimecert.a1b2c3.agents.whisper.online
# -> 3 1 1 54F0AFC79E208F13906DF248B5AF3F215FD661591584A748595B053107CF7863

# That OPENPGPKEY is a real OpenPGP key - gpg reads it straight out of DNS:
dig +short OPENPGPKEY _openpgpkey.a1b2c3.agents.whisper.online | base64 -d | gpg --list-packets
# -> public key packet: version 4, algo 19 (ECDSA), nistp256
```

Verify a signature and check the DANE pin by hand, with nothing but `openssl` and `dig`:

```
# 1. the CMS signature is cryptographically valid over the document:
openssl smime -verify -in signed.eml -CAfile whisper-ca.pem
# -> Verification successful

# 2. the signer's key is the exact one DNS pins (DANE-EE 3 1 1 = SHA-256 of the SPKI):
openssl x509 -in signer.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256
# -> 54f0afc7...07cf7863   (equals the SMIMEA pin above -> trust proven against DNSSEC, not a CA)
```

Encryption is the IETF standard **HPKE** (RFC 9180) over that same DNS-published P-256 key, so it is not tied to the `whisper` binary either: any HPKE implementation (OpenSSL 3.2+'s `OSSL_HPKE_*` API, or a library such as `circl` / `pyhpke`) interoperates with the `.wenc` container, which is a thin, documented wrapper around the standard HPKE result. Your trust rests on open standards you can check yourself; the CLI is a convenience on top, never a dependency.

## What the trust is - and isn't

- **Hosted key vs. sole control.** By default the per-agent key is hosted, so a signature is *authenticated* but not *non-repudiable*, and an encrypted message is confidential to the agent *and* to Whisper. For hard non-repudiation and true end-to-end confidentiality, the agent holds its own key: a sole-control signing certificate, or `whisper decrypt --key`. A hosted signing cert carries `digitalSignature` only, never a false non-repudiation bit.
- **Encryption is confidential, not sender-authenticated.** Anyone can encrypt to a published key. A clean decrypt proves the message was sealed to your key, not *who* sent it. When the sender matters, sign the content then encrypt it.
- **DANE-native today, CA-bridge optional.** Anything that speaks DANE trusts these day one; the trustless path never depends on the optional Whisper root.

## Built on open standards

| Piece | Standard |
|---|---|
| S/MIME key in DNS (signing) | SMIMEA, RFC 8162 |
| OpenPGP key in DNS (encryption) | OPENPGPKEY, RFC 7929 |
| Hybrid public-key encryption | HPKE, RFC 9180 |
| The DNS key pin & its anchor | DANE / TLSA, RFC 6698 + DNSSEC |
| The signature container | CMS / PKCS#7, RFC 5652 |

## Next

See [`/docs/sign-outputs`](/docs/sign-outputs) for the broader "prove what an agent produced" pattern, [`/docs/dane`](/docs/dane) for how the DANE/DNSSEC anchor is built, and [`/docs/per-agent-ca`](/docs/per-agent-ca) for why every identity gets its own key.
