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 · Sep 14
23/24 claims passed, 1 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.
The user does not learn the TLS session encryption keys at any point before both TEEs switch to FINISHED phase
AI reasoning
(regression double-check: prior=PASS, chunk-verdict=PARTIAL, reconciled=PARTIAL) Partially verified. TEE_K derives session keys internally and the HandshakeComplete message sent to the client contains only CipherSuite and CBC binding — no key fields. However, after both TEEs reach the FINISHED phase and sign, the SignedMessage sent to the client explicitly includes ServerAppKey (the server application key) and ResponsePackets with per-packet nonces as unsigned fields. This means the client does receive the server application key after the FINISHED phase, not before it — so the timing constraint is met, but the client does ultimately learn the server-side key material.
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 in tee_k/crypto.go constructs TagSecrets as a 32-byte buffer: the first 16 bytes are E_K(0^128) (block.Encrypt of an all-zero block) and the last 16 bytes are E_K(IV||0^31||1) (block.Encrypt of the nonce with the final byte set to 1). This matches the whitepaper's specification exactly for both request encryption and response tag secret generation. (standard aggregation across 6 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 produces a SignedMessage with BodyType_BODY_TYPE_K_OUTPUT (KOutputPayload body + signature), and TEE_T produces a SignedMessage with BodyType_BODY_TYPE_T_OUTPUT (TOutputPayload body + signature). The verification bundle wraps both as teekSigned and teetSigned, which the client validates before submission to the attestor. (standard aggregation across 8 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 operates exclusively on the already-encrypted ciphertext (`encReq.EncryptedData`, which is `Rred,enc` forwarded from TEE_K). It XORs the user-supplied redaction streams back into the masked positions to restore the full ciphertext, then computes the AEAD tag via `ComputeTagFromSecrets`. TEE_T never receives or uses `kenc`, `kdec`, or the IV — only the `TagSecrets` (key-independent values) arrive from TEE_K. No plaintext or key material is exposed to either TEE during this reconstruction. (negation-aware aggregation across 7 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 persistent 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 active tunnel is closed with an ERROR_BGP_ANNOUNCEMENT_OVERLAP error. This listener is started for every new tunnel connection during proof creation. (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 only after receiving a SessionReady message. TEEK's handleRequestConnection validates the port and stores the data before initiating the TLS handshake in handleTCPReady → performTLSHandshakeAndHTTP. No TLS handshake can start before TEEK receives these parameters. (standard aggregation across 11 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. Before sending to TEE_K, the client XORs each sensitive byte range with a cryptographically random stream (crypto/rand) in applyRedaction(), producing a masked request. TEE_K receives only the masked bytes (RedactedRequest envelope with no streams). The random streams are sent separately to TEE_T via sendEnvelopeToTEET(). TEE_K's encryptAndSendRequest() uses rawPlaintext := redactedRequest.RedactedRequest — the masked data only — with no code path to unmask it. (negation-aware aggregation across 10 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 tag secrets are derived by calling EncryptWithoutTag on a SplitAEAD initialized with the session key and IV at the correct sequence number. The SplitAEAD implementation for ChaCha20 generates the Poly1305 one-time key as the first 32 bytes of the ChaCha20 keystream at counter 0, which is exactly the RFC 7539 standard derivation. This is used consistently for both request encryption in TEE_K and response tag verification in TEE_T. (standard aggregation across 7 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() on the reconstructed request data, then packages the result (EncryptedData + AuthTag) in a BatchedEncryptedDataResponse envelope routed back to the client. The client (client/tls.go) receives this response and writes the TLS record containing the encrypted data and tag to the TCP connection toward the website. (standard aggregation across 7 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) handles TLS handshakes and holds encryption keys via DeriveHandshakeKeys/DeriveApplicationKeys, while tee_t/ (TEET) computes authentication tags from TagSecrets only via ComputeTagFromSecrets without access to the encryption keys. Each signs a distinct payload type (KOutputPayload vs TOutputPayload). No third TEE package exists. (negation-aware aggregation across 7 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 derives clientAppKey (kenc), serverAppKey (kdec), and clientAppIV/serverAppIV (IV) via DeriveHandshakeKeys() and DeriveApplicationKeys() in the KeySchedule. The HandshakeComplete envelope sent back to the client contains only the cipher suite and CBC binding — no key fields. Keys remain inside the enclave and are consumed internally by encryptAndSendRequest. (negation-aware aggregation across 7 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 (tee_t/session_handlers.go) runs tag verification via verifyTagForResponse/ComputeTagFromSecrets and only sends BatchedTagVerifications with AllSuccessful=true to TEE_K on success. TEE_K (tee_k/session_handlers.go) calls generateAndSendRedactedDecryptionStreamResponse only from handleRedactionSpec, which is triggered after receiving the redaction spec — not before. The decryption stream is generated by GenerateDecryptionStreamWithNonce in minitls/crypto.go using the server app key (kdec), nonce, and response length, covering both ChaCha20 and AES-CTR paths. No earlier handler produces the decryption stream. (standard aggregation across 7 chunks)
TEEK signs the SSL Certificate of the website received during the TLS handshake as part of its attestation commitment
AI reasoning
Verified. During signature generation, `kPayload.CertificateInfo = session.CertificateInfo` is set before marshaling the `KOutputPayload` protobuf. The marshaled bytes are then signed with `pair.SignData(body)` using Ethereum-style ECDSA, meaning the SSL certificate chain is cryptographically bound inside the signed body. (standard aggregation across 6 chunks)
TEEK verifies the website's SSL certificate
AI reasoning
Verified. The TLS client performs full X.509 certificate chain validation during the handshake. It parses the server's certificate message, checks key usage and extended key usage, verifies the chain against system or custom root CAs using Go's x509.Verify with RFC 6125 hostname checking, and validates that the certificate's public key type matches the negotiated cipher suite's authentication family (RSA or ECDSA). Missing intermediates are fetched via AIA URLs. (standard aggregation across 5/6 chunks; dropped 1 chunk-not-found artifact)
TEEA produces signature sigma_A over the concatenation of Respenc, Tresp, and StrSP
AI reasoning
Verified. TEE_T (tee_t) builds a TOutputPayload containing ConsolidatedResponseCiphertext (Respenc), RequestProofStreams (StrSP), and session metadata. This payload is serialized via proto.Marshal and signed with keyPair.SignData(body), producing sigma_A. The three fields share the same serialized body — there is no per-field signature. (standard aggregation across 6 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) verifies each response record's authentication tag via `verifyTagForResponse()` using `subtle.ConstantTimeCompare` before appending any ciphertext. On failure, the session is terminated with `ReasonCryptoTagVerificationFailed` and an error is returned — no decryption material is released. TEE_K only receives a `BatchedTagVerifications` success signal before generating decryption streams, and there is no code path in TEE_K that releases decryption streams without first observing that success signal. (standard aggregation across 7 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 system immediately closes the active tunnel with an ERROR_BGP_ANNOUNCEMENT_OVERLAP error. The BGP listener monitors RIPE RIS live data, checks each announced prefix against active connection IPs, and triggers tunnel closure — aborting proof creation. (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 copies kOutputPayload.redactedRequest into revealedRequest, then makes a second copy called prettyRequest. It iterates over requestRedactionRanges and, for any range whose type does NOT include 'proof', overwrites bytes in [start, start+length) with REDACTION_CHAR_CODE ('*'). The prettyRequest (with non-proof ranges redacted) is returned as the revealed request for display. (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 SSL certificate chain is validated as a distinct step in two places: (1) in `process-handshake.ts`, `verifyCertificateChain` and `verifyCertificateSignature` are called during handshake processing for both TLS 1.2 and 1.3; (2) in `claimTeeBundle.ts`, `validateTlsCertificate` checks the certificate's hostname and validity period against the claim. The `parseKOutputPayload` function also enforces that `certificateInfo` is present, throwing if missing. (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 server response by XOR-ing the consolidated response keystream (from TEE_K, which is the redacted StrDec,Red) with the consolidated response ciphertext (from TEE_T, which is Respenc). The byte-by-byte XOR loop is explicit in the code, and no full decryption keys are passed — only the pre-computed keystream from TEE_K is used. (standard aggregation across 2 chunks)
TEEK encrypts the redacted request as Rred,enc = Rred XOR Stream(kenc, nonce)
AI reasoning
Verified. TEEK encrypts the redacted request using a keystream XOR operation. In `encryptAndSendRequest`, the redacted request is fragmented, and each fragment is encrypted via `splitAEAD.EncryptWithoutTag(fragmentData, AAD)`, which internally computes `keystream = Stream(kenc, nonce)` then `encryptedData = fragmentData XOR keystream`. The keys (`clientAppKey`) and nonce (`clientAppIV` XOR'd with sequence number) are derived from the TLS key schedule, matching the whitepaper formula Rred,enc = Rred XOR Stream(kenc, nonce). (standard aggregation across 6 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 RedactedRequest (Rred), ConsolidatedResponseKeystream (StrDec,Red), and CertificateInfo (the SSL certificate), marshals it with proto.Marshal, then signs the resulting bytes using Ethereum-style ECDSA via SignData(). All three fields are included in the same signed payload with no separate intermediate signature. (standard aggregation across 6 chunks)
TEEK replaces bytes at user-specified SecretRanges in StrDec with '*' to produce StrDec,Red before signing
AI reasoning
Verified. In `generateAndSendRedactedDecryptionStream`, the code iterates over user-supplied `spec.Ranges` (SecretRanges), generates cryptographically random bytes via `rand.Read` for each overlapping position, and overwrites those bytes in the decryption keystream copy. The resulting redacted streams are stored in `session.RedactedStreams` and the consolidated keystream is placed in `session.ConsolidatedResponseKeystream`, which is then included in `KOutputPayload` and signed via `pair.SignData` in `generateComprehensiveSignatureForSession`. No unredacted stream is ever signed. (standard aggregation across 6 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, SEV-SNP, or Secure Boot), then each signature is verified using the ETH signature provider against the signed body bytes. If either signature fails, an error is thrown and verification cannot succeed. (standard aggregation across 6 chunks)