Our guarantee
Every claim in the Reclaim Protocol whitepaper is re-verified monthly against the open-source code that runs in production. The verdict and the source files are published per claim.
The threat model
How it’s enforced
How you verify
What you still trust

Reclaim Protocol Whitepaper
View on Google Drive
Today · Aug 28
24/24 claims passed, 0 partial · 0 cached from previous run
Every statement we make in our whitepaper is re-verified monthly against the open-source code. For each claim below you can see the AI's reasoning and open the exact files it inspected on GitHub.
For AES-GCM, TagSecrets passed from TEEK to TEEA consists of EK(0^128) and EK(IV||0^31||1)
AI reasoning
Verified. For AES-GCM, the code allocates a 32-byte tagSecrets buffer and fills it in two halves: the first 16 bytes are E_K(0^128) via block.Encrypt on an all-zero block, and the second 16 bytes are E_K(IV||0^31||1) via block.Encrypt on a 16-byte block containing the 12-byte nonce with the final byte set to 1. This matches the whitepaper specification exactly. (standard aggregation across 4 chunks)
The proof output Pi consists of the two signatures (sigma_K, sigma_A) and the session-related data that was signed
AI reasoning
Verified. The proof output consists of exactly two signed payloads: TEE_K emits a SignedMessage with BodyType BODY_TYPE_K_OUTPUT (containing KOutputPayload body + signature), and TEE_T emits a SignedMessage with BodyType BODY_TYPE_T_OUTPUT (containing TOutputPayload body + signature). The client collects both as teekSignedMessage and teetSignedMessage, then packages them into a VerificationBundle as bundle.TeekSigned and bundle.TeetSigned — no third signature or extra payload is added. (standard aggregation across 5 chunks)
All sensitive data masked by the user is correctly reapplied when TEEA reconstructs the full request ciphertext, ensuring no sensitive bytes are exposed to either TEE
AI reasoning
Verified. TEE_T's `reconstructFullRequestWithStreams` function takes the already-encrypted redacted ciphertext and XORs each redaction stream back into the corresponding byte ranges, restoring the full ciphertext without ever decrypting it. TEE_T has no access to encryption keys (kenc/kdec/IV), which stay in TEE_K — only the ciphertext and the user-supplied redaction streams cross to TEE_T for tag computation. (negation-aware aggregation across 4 chunks)
The protocol monitors BGP announcements via RIPE RIS to detect traffic rerouting between the attestor and the website during proof creation
AI reasoning
Verified. The attestor opens a WebSocket to RIPE RIS Live (wss://ris-live.ripe.net/v1/ws/?client=reclaim-hijack-detector), subscribes to BGP UPDATE announcements, and checks each announced prefix against registered target IPs using CIDR containment. When an overlap is detected, the tunnel for that host is closed with an ERROR_BGP_ANNOUNCEMENT_OVERLAP error. This listener is started for every new tunnel connection. (standard aggregation across 2 chunks)
User sends connection parameters (hostname, port, SNI, ALPN) to TEEK at the start of the TLS handshake phase
AI reasoning
Verified. The client builds connection parameters (hostname, port, SNI, ALPN) in RequestHTTP() before any TLS bytes are exchanged, stores them as a pending request, and sends them to TEEK via a RequestConnection protobuf envelope once the session ID is received. TEEK's handleRequestConnection stores these params and only starts the TLS handshake after receiving a TCPReady message, confirming the ordering claim. (standard aggregation across 8 chunks)
All sensitive portions of the request are masked by the user before being sent to TEEK, ensuring TEEK never sees any raw sensitive data
AI reasoning
Verified. The client generates random XOR streams per redaction range using crypto/rand, applies them to mask sensitive bytes before sending to TEE_K (which only receives the masked request). Streams are sent separately to TEE_T via a different channel. TEE_K's encryptAndSendRequest uses rawPlaintext = redactedRequest.RedactedRequest with no unmasking code path. (negation-aware aggregation across 7 chunks)
For ChaCha20-Poly1305, TagSecrets consists of the first 32 bytes of the encryption stream derived at counter value 0
AI reasoning
Verified. For ChaCha20-Poly1305, the code generates TagSecrets by creating a ChaCha20 cipher with the session key and nonce, setting the counter to 0, then XOR-ing 32 zero bytes to extract the first 32 bytes of the keystream — exactly the standard Poly1305 one-time key per RFC 7539. This is implemented in `generateChaChaTagSecrets` in minitls/crypto.go. (standard aggregation across 4 chunks)
TEEA computes authentication tag T = AuthTag(Renc, TagSecrets) and sends it to the user for forwarding to the website
AI reasoning
Verified. TEEA (TEE_T) computes the authentication tag using ComputeTagFromSecrets() in minitls/crypto.go, called from processEncryptedRequestWithStreams() in tee_t/session_handlers.go. The result is packaged in a BatchedEncryptedDataResponse protobuf envelope and routed to the client via RouteToClient(). The client (client/websocket.go handleBatchedEncryptedRequest) then assembles the encrypted data and auth tag into a TLS record and writes it to the TCP connection to the website. (standard aggregation across 4 chunks)
The user does not learn the TLS session encryption keys at any point before both TEEs switch to FINISHED phase
AI reasoning
Verified. TEE_K derives session keys (kenc, kdec, IV) internally during the TLS handshake and never returns them to the client. The HandshakeComplete message sent to the client contains only CipherSuite and Success fields — no key material. The SignedMessage (KOutput) sent after the FINISHED phase includes the server app key for client-side keystream verification, but this only occurs after both TEEs have completed their signing, consistent with the FINISHED-phase timing requirement. (negation-aware aggregation across 4 chunks)
The protocol uses exactly two separate TEEs: TEEK (holds TLS keys) and TEEA (computes auth tags without key access)
AI reasoning
Verified. The codebase contains exactly two TEE packages: tee_k/ (TEEK) which holds TLS keys and performs the handshake via performTLSHandshakeAndHTTP, and tee_t/ (TEET) which computes authentication tags from TagSecrets only via ComputeTagFromSecrets without access to encryption keys. TEEK signs KOutputPayload and TEET signs TOutputPayload as separate signed messages. No third TEE package exists. (negation-aware aggregation across 4 chunks)
TEEK derives three session keys (kenc, kdec, IV) during the TLS handshake and does not share them with the user
AI reasoning
Verified. During the TLS handshake, TEEK's minitls derives clientAppKey (kenc), serverAppKey (kdec), and clientAppIV/serverAppIV via DeriveHandshakeKeys and DeriveApplicationKeys in the KeySchedule. These keys live inside the TLS client state within the enclave. The HandshakeComplete envelope sent back to the client contains only a success flag and cipher suite — no key fields exist in the proto message. Keys are consumed internally by encryptAndSendRequest and never transmitted outside TEEK. (negation-aware aggregation across 4 chunks)
TEEK generates the decryption stream as StrDec = Stream(kdec, nonceresp, |Respenc|) only after receiving a success signal from TEEA
AI reasoning
Verified. TEE_T's handleBatchedTagSecrets verifies all response tags via verifyTagForResponse (ComputeTagFromSecrets) and only sends AllSuccessful=true to TEE_K if all pass. TEE_K's handleRedactionSpec then calls generateAndSendRedactedDecryptionStreamResponse, which internally calls GenerateDecryptionStreamWithNonce using the server app key (kdec), the per-record nonce, and the response length — exactly matching the claimed sequence. (standard aggregation across 4 chunks)
TEEK signs the SSL Certificate of the website received during the TLS handshake as part of its attestation commitment
AI reasoning
Verified. During the TLS handshake, certificate info is extracted and stored in the session. When generating the attestation commitment, `kPayload.CertificateInfo = session.CertificateInfo` is set before the payload is marshalled with `proto.Marshal(kPayload)` and signed with `keyPair.SignData(body)`. The SSL certificate chain is therefore cryptographically bound inside the signed body. (standard aggregation across 4 chunks)
TEEK verifies the website's SSL certificate
AI reasoning
Verified. The minitls package implements full SSL/TLS certificate verification. In cert_validation.go, verifyCertificateChain() calls x509.Certificate.Verify() with the server hostname, checking the full chain against system root CAs, validating key usage flags (digitalSignature required), and performing RFC 6125 hostname verification. Both TLS 1.2 (processServerCertificateTLS12) and TLS 1.3 paths invoke this validation before the handshake proceeds. (standard aggregation across 3/4 chunks; dropped 1 chunk-not-found artifact)
TEEA produces signature sigma_A over the concatenation of Respenc, Tresp, and StrSP
AI reasoning
Verified. TEEA (TEE_T) signs over all three required fields. In `checkFinishedCondition`, a `TOutputPayload` proto is built with `ConsolidatedResponseCiphertext` (Respenc), `RequestProofStreams` (StrSP), and the session ID. The response tag (Tresp) is verified earlier via `verifyTagForResponse` before signing. The payload is serialized with `proto.Marshal` and signed via `keyPair.SignData(body)` using an Ethereum secp256k1 key, producing sigma_A over the concatenation of all three fields in a single signed body. (standard aggregation across 4 chunks)
TEEA verifies the website response authentication tag before any decryption material is released; the session aborts if verification fails
AI reasoning
Verified. TEEA (TEE_T) calls verifyTagForResponse() for each response record, which computes the authentication tag via ComputeTagFromSecrets() and compares it using subtle.ConstantTimeCompare(). On failure, it calls terminateSessionWithError() with ReasonCryptoTagVerificationFailed and returns an error before any decryption material is released. On success, it appends ciphertext to teetState and sends BatchedTagVerifications with AllSuccessful=true to TEE_K. TEE_K's handleBatchedTagVerificationResponse only generates decryption streams after receiving this success signal — there is no code path in TEE_K that releases decryption streams without first observing a verified-success from TEE_T. (standard aggregation across 4 chunks)
If a potentially malicious BGP announcement is detected, the current connection and proof creation are dropped
AI reasoning
Verified. When a BGP announcement overlaps with a target IP, the listener emits an event that triggers `tunnel.close()` with an `ERROR_BGP_ANNOUNCEMENT_OVERLAP` error, immediately dropping the connection and aborting proof creation. The `checkForBgp` function in `createTunnel.ts` registers this handler for all resolved IPs of the target host. (standard aggregation across 2 chunks)
Verifier reconstructs the revealed request by copying Rred and replacing bytes in non-proof redaction ranges (requestRedactionRanges without 'proof' type) with redaction characters for display
AI reasoning
Verified. The `reconstructRequest` function in the transcript reconstruction module copies `kOutputPayload.redactedRequest` into both `revealedRequest` and `prettyRequest`. It then iterates over `requestRedactionRanges`, and for any range where `range.type` does NOT include `'proof'`, it overwrites bytes at positions `[start, start+length)` with `REDACTION_CHAR_CODE` ('*'). The function returns `prettyRequest`, exactly matching the described behavior. (negation-aware aggregation across 2 chunks)
Attestor validates the website's SSL Certificate as a distinct step during proof verification
AI reasoning
Verified. After both TEE signatures are verified, the `parseKOutputPayload` function explicitly checks for `certificateInfo` presence and throws if missing. In `claimTeeBundle.ts`, `validateTlsCertificate` then checks the certificate's hostname (CN and SANs with wildcard support) against the claimed URL and validates the validity period — a distinct step separate from TEE attestation. (standard aggregation across 3 chunks)
Attestor reconstructs the revealed response as reconstructedResponse = consolidated_response_ciphertext (Respenc) XOR consolidated_response_keystream (StrDec,Red from TEEK) directly using redacted keystream (no full decryption keys)
AI reasoning
Verified. The attestor reconstructs the response by XOR-ing the consolidated keystream from TEE_K (the redacted stream, never the full decryption key) with the consolidated ciphertext from TEE_T, byte by byte. No full decryption keys are passed — only the pre-computed keystream output is used. (standard aggregation across 2 chunks)
TEEK encrypts the redacted request as Rred,enc = Rred XOR Stream(kenc, nonce)
AI reasoning
Verified. TEEK receives the redacted request (Rred) and encrypts it using a split AEAD. For AES-GCM, it uses AES-CTR mode (cipher.NewCTR) to XOR the plaintext with a keystream derived from kenc and nonce, producing Rred,enc = Rred XOR Stream(kenc, nonce). For ChaCha20, it uses chacha20.NewUnauthenticatedCipher with counter=1 to XOR the plaintext. The nonce is constructed as clientAppIV XOR seqNum for TLS 1.3, or implicit_iv||seqNum for TLS 1.2 AES-GCM. The encrypted fragments are batched and forwarded to TEE_T. (standard aggregation across 4 chunks)
TEEK produces signature sigma_K over the concatenation of Rred, StrDec,Red, and the website's SSL Certificate
AI reasoning
Verified. TEEK builds a KOutputPayload containing the redacted request (Rred), the consolidated response keystream (StrDec,Red), and the SSL certificate info, then marshals it with proto.Marshal and signs the resulting bytes via SignData(), which uses Ethereum-style ECDSA over the marshalled payload. All three fields are included in the same signed body. (standard aggregation across 4 chunks)
TEEK replaces bytes at user-specified SecretRanges in StrDec with '*' to produce StrDec,Red before signing
AI reasoning
Verified. In generateAndSendRedactedDecryptionStream(), TEEK iterates over user-supplied SecretRanges and overwrites the corresponding bytes of the decryption keystream with cryptographically random bytes via rand.Read(). The resulting redacted streams are stored in session.RedactedStreams and session.ConsolidatedResponseKeystream, then signed as part of KOutputPayload by keyPair.SignData in generateComprehensiveSignatureAndSendTranscript(). No code path signs an unredacted stream. (standard aggregation across 4 chunks)
Proof verification checks both ECDSA signatures against the public keys of TEEK (pkK) and TEEA (pkT)
AI reasoning
Verified. The code checks both TEE_K and TEE_T ECDSA signatures in sequence. Public keys are extracted from hardware attestations (GCP Confidential Space or SEV-SNP) or embedded ETH addresses in standalone mode. Each signature is verified using the ETH signature provider, and any failure throws an error — there is no path that succeeds if either signature is invalid. (standard aggregation across 6 chunks)