LogoLogo
LogoLogo
  • Introduction
    • About Us
    • Notations & Definitions
      • MPC
      • ZK
    • Contribute to this Site!
  • Primitives
    • Multiplication
      • Karatsuba Multiplication
      • Toom-Cook Multiplication
    • NAF (Non-adjacent form)
    • Chinese Remainder Theorem (CRT)
    • Euclidean Algorithm
      • Extended Euclidean Algorithm
      • Binary Euclidean Algorithm
      • Extended Binary Euclidean Algorithm
    • Coding Theory
      • Linear Code
    • Number Theoretic Transform
    • Abstract Algebra
      • Group
        • -Morphisms
        • Batch Inverse
      • Elliptic Curve
        • Weierstrass Curve
          • Coordinate Forms
          • Fast Elliptic Curve Arithmetic and Improved WEIL Pairing Evaluation
        • Edwards Curve
          • Coordinate Forms
          • Twisted Edwards ↔ Short Weierstrass Transformation
        • Batch Inverse for Batch Point Additions
        • Scalar Multiplication
          • Double-and-add
          • GLV Decomposition
        • MSM
          • Pippenger's Algorithm
          • Signed Bucket Index
          • CycloneMSM
          • EdMSM
          • cuZK
        • 2-Chain and 2-Cycle of Elliptic Curves
    • Encryption Scheme
      • ElGamal Encryption
    • Modular Arithmetic
      • Modular Reduction
        • Barrett Reduction
        • Montgomery Reduction
      • Modular Inverse
        • Bernstein-Yang's Inverse
    • Multiset Check
    • Sumcheck
    • Commitment Scheme
      • Fflonk
      • SHPlonk
      • Zeromorph
  • MPC
    • Yao's Garbled Circuits
    • GMW
    • BMR
  • ZK
    • Arithmetization
      • R1CS
      • PLONK
      • AIR
    • Folding
      • LatticeFold
      • Nova
        • Nova over Cycles of Curves
    • Lookup
      • Lasso
      • LogUp-GKR
    • SNARK
      • Groth16
      • HyperPlonk
      • Spartan
        • SPARK
    • STARK
      • Additive NTT
      • Basefold
      • Binius
      • Brakedown
      • CircleSTARK
      • FRI
        • FRI Security Features and Optimizations
      • DEEP FRI
      • STIR
      • WHIR
    • Distributed ZK
      • Ryan's Trick for Distributed Groth16
  • Application
    • zkLogin
    • zkHoldem
    • zkTLS
      • DECO
      • Proxying is enough
  • zkVM
Powered by GitBook
On this page
  • Introduction
  • Background
  • ECDH (Elliptic Curve Diffie-Hellman)
  • MAC-then-Encrypt
  • HMAC (Hash-based Message Authentication Code)
  • AES-CBC-HMAC
  • TLS
  • Protocol Explanation
  • Method 1: Naive Approach
  • Method 2: Three-Party Handshake
  • Method 3: Query Optimization
  • Method 4: AES-CBC-HMAC Optimization
  • Method 5: Context Integrity via Structured Commitments
  • Conclusion
  • References
Export as PDF
  1. Application
  2. zkTLS

DECO

Presentation: https://www.youtube.com/watch?v=cTBqiwAx5jA

PreviouszkTLSNextProxying is enough

Last updated 1 month ago

Introduction

Thanks to TLS, users can access their private data securely with guarantees of end-to-end confidentiality and integrity. However, in the traditional TLS model, it is impossible to prove this data to a third party without relying on trust assumptions. In other words, a user’s private data remains trapped at its origin, with no cryptographically verifiable way to share or prove it externally.

DECO (DECentralized Oracle) is a protocol proposed by researchers at Cornell in 2019 that addresses this problem. It enables privacy-preserving and selective disclosure of data transmitted over TLS, allowing users to prove facts about their data to third parties without revealing the underlying content.

Moreover, DECO is the first oracle protocol to support both TLS 1.2 and TLS 1.3, making it compatible with modern web security standards without requiring any modifications to the server. Prior to DECO, such functionality required either modifying the server or relying on Trusted Execution Environments (TEEs).

Background

ECDH (Elliptic Curve Diffie-Hellman)

Elliptic Curve Diffie-Hellman (ECDH) is a key exchange protocol built on elliptic curve cryptography (ECC). It enables two parties to safely derive a shared secret key over an insecure channel using only public keys.

This is an elliptic-curve variant of the classic Diffie-Hellman Key Exchange (DHKE) protocol.

Let GGG be a generator point on the elliptic curve:

  1. Private key generation: Alice and Bob independently sample secret keys a,ba, ba,b.

  2. Public key computation:

    • Alice: A=a⋅GA = a \cdot GA=a⋅G

    • Bob: B=b⋅GB = b \cdot GB=b⋅G

  3. Key exchange:

    • Alice sends AAA to Bob.

    • Bob sends BBB to Alice.

  4. Shared secret key computation:

    • Alice computes S=a⋅B=a⋅(b⋅G)S = a \cdot B = a \cdot (b \cdot G)S=a⋅B=a⋅(b⋅G)

    • Bob computes S=b⋅A=b⋅(a⋅G)S = b \cdot A = b \cdot (a \cdot G)S=b⋅A=b⋅(a⋅G)

Thus, both parties derive the same shared key without revealing their private inputs.

MAC-then-Encrypt

MAC-then-Encrypt is the authenticated encryption approach used in TLS 1.2 and earlier. It ensures both confidentiality and integrity by:

  1. Computing a message authentication code (MAC) with the key kMACk^\mathsf{MAC}kMAC and the message mmm:

σ=MAC(kMAC,m)\sigma = \mathsf{MAC}(k^{\mathsf{MAC}}, m)σ=MAC(kMAC,m)
  1. Encrypting the message and MAC with the key kEnck^{\mathsf{Enc}}kEnc:

C=Encrypt(kEnc,m∥σ)C = \mathsf{Encrypt}(k^\mathsf{Enc}, m \| \sigma)C=Encrypt(kEnc,m∥σ)

To decrypt and verify CCC:

  1. Decrypt the ciphertext:

(m,σ)=Decompose(Decrypt(kEnc,C))(m, \sigma) = \mathsf{Decompose(Decrypt}(k^\mathsf{Enc},C))(m,σ)=Decompose(Decrypt(kEnc,C))
  1. Verify the MAC:

σ=?MAC(kMAC,m)\sigma \stackrel{?}= \mathsf{MAC}(k^\mathsf{MAC}, m)σ=?MAC(kMAC,m)

HMAC (Hash-based Message Authentication Code)

HMAC is a type of Message Authentication Code (MAC) that uses a hash function to ensure both integrity and authentication of data.

The core idea is to combine a cryptographic hash function with a secret key to generate an authentication code (MAC) for a message. This allows the recipient to verify that the data has not been tampered with and that it was sent by a trusted sender.

HMAC is computed as follows:

HMAC(k,m)→H((k⊕opad)∥(H(k⊕ipad)∥m))\mathsf{HMAC}(k, m) \rightarrow H((k \oplus \mathsf{opad}) \| (H(k \oplus \mathsf{ipad}) \| m))HMAC(k,m)→H((k⊕opad)∥(H(k⊕ipad)∥m))

  • opad\mathsf{opad}opad: outer padding (0x5c)

  • ipad\mathsf{ipad}ipad: inner padding (0x36)

AES-CBC-HMAC

AES-CBC-HMAC is a scheme that combines AES encryption in CBC (Cipher Block Chaining) mode with HMAC-based authentication to ensure both confidentiality and integrity of the message. It follows the MAC-then-Encrypt paradigm.

Encryption

  1. Generate HMAC:

σ=HMAC(kMAC,m)\sigma = \mathsf{HMAC}(k^{\mathsf{MAC}} , m)σ=HMAC(kMAC,m)
  1. Encrypt the message and MAC using AES in CBC mode:

C=AES-CBC.Encrypt(kEnc,m∥σ,IV)C = \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, m \| \sigma, IV)C=AES-CBC.Encrypt(kEnc,m∥σ,IV)
  • IVIVIV is the Initialization Vector, which is sent along with the ciphertext CCC.

Decryption

  1. Decrypt and split the message:

(m,σ)=Decompose(AES-CBC.Decrypt(kEnc,C,IV))(m, \sigma) = \mathsf{Decompose(AES\text{-}CBC.Decrypt}(k^\mathsf{Enc}, C, IV))(m,σ)=Decompose(AES-CBC.Decrypt(kEnc,C,IV))
  1. Verify the MAC:

σ=?HMAC(kMAC,m)\sigma \stackrel{?}= \mathsf{HMAC}(k^{\mathsf{MAC}} , m)σ=?HMAC(kMAC,m)

TODO(chokobole): Add AES-GCM

TLS

Handshake Protocol

The TLS handshake is the process by which a client and a server establish a secure connection. It typically involves the following steps:

  1. Cipher suite negotiation: The client and server agree on which cryptographic algorithms (cipher suite) to use.

  2. Session key generation: The client and server derive symmetric keys:

    • kEnck^{\mathsf{Enc}}kEnc: symmetric encryption key

    • kMACk^{\mathsf{MAC}}kMAC: symmetric authentication key

These keys are used in the following Record Protocol for data transmission.

DECO supports ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) as the key exchange method.

Record Protocol

The TLS Record Protocol operates in the following steps:

  1. Data fragmentation: The sender splits application data D\bm{D}D into fixed-size plaintext records D=(D1,D2,…,Dn)\bm{D} = (D_1, D_2, \dots, D_n)D=(D1​,D2​,…,Dn​). The last block DnD_nDn​​ is padded if necessary.

  2. Optional compression: The data may optionally be compressed before encryption.

  3. Authenticated encryption: Each record is encrypted and authenticated using one of the following modes:

    • TLS ≤ 1.2: MAC-then-Encrypt (e.g., AES-CBC-HMAC)

  4. Decryption and integrity check (receiver side): The receiver decrypts the record and verifies integrity by recomputing the MAC.

  5. Optional decompression and reassembly: If compression was applied, the data is decompressed and multiple records are combined to reconstruct the original application data.

DECO supports both CBC-HMAC and GCM mode for AES-based encryption.

Protocol Explanation

{
  "accounts": [
    { "account_id": 1, "balance": 500 },
    { "account_id": 2, "balance": 2000 },
    { "account_id": 3, "balance": 5000 }
  ]
}

Let’s assume the above JSON message was received from a bank server over TLS. Our goal is to prove in zero-knowledge (ZK) that the account with account_id = 2 has a balance greater than or equal to 1000, without revealing any information about the other accounts.

For simplicity, we assume that the TLS session uses AES-CBC-HMAC (as in TLS 1.2), though this can be extended to AES-GCM as well.

TODO(chokobole): Add how DECO is instantiated with AES-GCM

Method 1: Naive Approach

The most straightforward idea is to construct a circuit like the one below and generate a ZK proof. (We omit AES-CBC's IV for clarity.)

C(x:{C,q},w:{kEnc,kMAC,m}):σ=?HMAC(kMAC,m)∧C=?AES-CBC.Encrypt(kEnc,m∥σ)∧jq.exec(m,q)≥1000C(x: \{ C, q \}, w: \{ k^\mathsf{Enc}, k^\mathsf{MAC}, m \}) : \\ \sigma \stackrel{?}= \mathsf{HMAC}(k^{\mathsf{MAC}} , m) \land C \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, m \| \sigma) \land \mathsf{jq.exec}(m, q) \ge 1000C(x:{C,q},w:{kEnc,kMAC,m}):σ=?HMAC(kMAC,m)∧C=?AES-CBC.Encrypt(kEnc,m∥σ)∧jq.exec(m,q)≥1000

Here, we use the notation from the circuit overview.

  • jq.exec(m,q)\mathsf{jq.exec}(m, q)jq.exec(m,q) denotes applying the jq query qqq to message mmm

❗ Problem

This naive approach has a critical security flaw: The client already knows the MAC key kMACk^{\mathsf{MAC}}kMAC.

This means the client can forge a valid MAC for a tampered message. For example, even if the real balance is only 500, the client could create a fake message like:

{
  "accounts": [
    { "account_id": 1, "balance": 500 },
    { "account_id": 2, "balance": 9999 },
    { "account_id": 3, "balance": 5000 }
  ]
}

and generate a valid MAC on it—making it appear as though the server authenticated this falsified message.

Thus, performing MAC verification entirely inside the ZK circuit fails to prevent forgery when the client knows the MAC key.

Method 2: Three-Party Handshake

The limitation of the naive approach lies in the fact that, under the standard TLS model, the client learns the MAC key kMACk^{\mathsf{MAC}}kMAC before receiving the server’s response RRR.

Because the client directly knows the MAC key, it can forge a message and still generate a valid ZK proof, introducing a serious vulnerability.

To resolve this, DECO introduces a Three-Party Handshake involving:

  • P\mathcal{P}P: TLS client and ZK prover

  • V\mathcal{V}V: Verifier

  • S\mathcal{S}S: TLS server

  • The MAC key kMACk^{\mathsf{MAC}}kMAC is split between P\mathcal{P}P and V\mathcal{V}V, so that P\mathcal{P}P cannot compute it alone.

  • P\mathcal{P}P first commits to the received TLS message.

  • Only then does V\mathcal{V}V reveal their MAC key share, preventing the prover from tampering with the message (At this moment, P\mathcal{P}P and V\mathcal{V}V know the full MAC key kMACk^\mathsf{MAC}kMAC).

Step 1: Key Exchange

Step 2: Key Derivation

TODO(chokobole): Add details on how the MAC key is derived from the shared secrets ZPZ_\mathcal{P}ZP​​ and ZVZ_\mathcal{V}ZV​.

❗ Problem

In Method 1, the prover P\mathcal{P}P knows the full MAC key kMACk^{\mathsf{MAC}}kMAC, allowing them to compute HMAC and all related operations locally.

However, from Method 2 onward, the MAC key is only additively shared between P\mathcal{P}P and V\mathcal{V}V, meaning:

  • P\mathcal{P}P cannot reconstruct the full key.

MPC2(HMAC,kPMAC,kVMAC,m)→HMAC(kPMAC+kVMAC,m)\mathsf{MPC}_2(\mathsf{HMAC}, k^\mathsf{MAC}_\mathcal{P}, k^\mathsf{MAC}_\mathcal{V}, m) \rightarrow \mathsf{HMAC}(k^\mathsf{MAC}_\mathcal{P} + k^\mathsf{MAC}_\mathcal{V}, m) MPC2​(HMAC,kPMAC​,kVMAC​,m)→HMAC(kPMAC​+kVMAC​,m)

This introduces a performance challenge: Naively computing HMAC via 2PC is expensive, as HMAC internally invokes the hash function twice, each requiring complex boolean circuits with a high gate count.

Method 3: Query Optimization

Now in order to send a query, the prover P\mathcal{P} P and the verifier V\mathcal{V}V compute the MAC together as follows:

To address the performance bottleneck in Method 2, Method 3 applies an optimization strategy that leverages the structural properties of HMAC and the internal workings of SHA-256.

The goal is to minimize the scope of 2PC computations by offloading as much of the work as possible to local computation.

HMAC(k,m)→H((k⊕opad)∥H((k⊕ipad)∥m)⏟inner hash)⏟outer hash\mathsf{HMAC}(k, m) \rightarrow \underbrace{H((k \oplus \mathsf{opad}) \| \underbrace{H\left((k \oplus \mathsf{ipad}) \| m\right)}_{\text{inner hash}})}_{\text{outer hash}}HMAC(k,m)→outer hashH((k⊕opad)∥inner hashH((k⊕ipad)∥m)​​)​​
H(m1∥m2)=fH(fH(IV,m1),m2)H(m_1 \| m_2) = f_H(f_H(IV, m_1), m_2)H(m1​∥m2​)=fH​(fH​(IV,m1​),m2​)

where:

  • fHf_HfH​ is the compression function of the hash

  • IVIVIV is the initial vector.

Using this structure, HMAC can be split and optimized as follows:

  1. Initial compression via 2PC:

s0=fH(IV,k⊕ipad)s_0 = f_H(IV, k \oplus \mathsf{ipad})s0​=fH​(IV,k⊕ipad)
  1. Compute inner hash locally:

s1=H(s0∥m)s_1 = H(s_0 \| m)s1​=H(s0​∥m)

As s0s_0s0​​ is already known, the prover P\mathcal{P}P can compute the inner hash s1s_1s1​​ locally using the full message mmm.

  1. Compute outer hash via 2PC only:

MPC2(HMAC-opt,kPMAC,kVMAC,s1)→HMAC-opt(kPMAC+kVMAC,s1)\mathsf{MPC}_2(\mathsf{HMAC\text{-}opt}, k^\mathsf{MAC}_\mathcal{P}, k^\mathsf{MAC}_\mathcal{V}, s_1) \rightarrow \mathsf{HMAC\text{-}opt}(k^\mathsf{MAC}_\mathcal{P} + k^\mathsf{MAC}_\mathcal{V}, s_1)MPC2​(HMAC-opt,kPMAC​,kVMAC​,s1​)→HMAC-opt(kPMAC​+kVMAC​,s1​)

where HMAC-opt\mathsf{HMAC\text{-}opt}HMAC-opt is defined as:

HMAC-opt(k,s1)=H((k⊕opad)∥s1)\mathsf{HMAC\text{-}opt}(k, s_1) = H((k \oplus \mathsf{opad}) \| s_1)HMAC-opt(k,s1​)=H((k⊕opad)∥s1​)

By computing the most expensive part of the computation (inner hash) locally, this optimization significantly reduces the cost of 2PC.

Method 4: AES-CBC-HMAC Optimization

As in Method 1, verifying the entire AES-CBC-HMAC process inside a ZK circuit introduces significant computational overhead. To address this, we can apply a circuit-level optimization that leverages the CBC structure and MAC-then-Encrypt semantics.

Assume that the message mmm consists of 1024 AES blocks, and the MAC σ\sigmaσ consists of 3 fixed-size AES blocks:

M=(B1,…,B1024,σ),σ=(B1025,B1026,B1027)\bm{M} = (B_1, \dots, B_{1024}, \sigma), \quad \bm{\sigma} = (B_{1025}, B_{1026}, B_{1027})M=(B1​,…,B1024​,σ),σ=(B1025​,B1026​,B1027​)

After CBC encryption, it transforms into:

M^=(B^1,…,B^1024,σ^),σ^=(B^1025,B^1026,B^1027)\hat{\bm{M}} = (\hat{B}_1, \dots, \hat{B}_{1024}, \hat{\sigma}), \quad \bm{\hat{\sigma}} = (\hat{B}_{1025}, \hat{B}_{1026}, \hat{B}_{1027})M^=(B^1​,…,B^1024​,σ^),σ^=(B^1025​,B^1026​,B^1027​)

If we naively construct the circuit, we would need to perform 1027 AES encryptions inside the circuit:

C(x:{M^},w:{kEnc,M}):⋀i=11027B^i=?AES-CBC.Encrypt(kEnc,Bi)C(x: \{ \bm{\hat{M}} \}, w: \{ k^\mathsf{Enc}, \bm{M} \}) : \bigwedge_{i=1}^{1027} \hat{B}_i \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_i)C(x:{M^},w:{kEnc,M}):i=1⋀1027​B^i​=?AES-CBC.Encrypt(kEnc,Bi​)

Optimization 1: Verifying MAC Blocks Only

Using the MAC-then-Encrypt structure, we can only verify the MAC portion of the CBC-encrypted message in the circuit. The HMAC itself can be checked outside the circuit:

C(x:{σ,σ^},w:{kEnc}):⋀i=10251027B^i=?AES-CBC.Encrypt(kEnc,Bi)C(x: \{ \textcolor{red}{\bm{\sigma}, \bm{\hat{\sigma}}} \}, w: \{ k^\mathsf{Enc} \}) : \textcolor{red}{\bigwedge_{i=1025}^{1027}}\hat{B}_i \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_i)C(x:{σ,σ^},w:{kEnc}):i=1025⋀1027​B^i​=?AES-CBC.Encrypt(kEnc,Bi​)

Then the verifier V\mathcal{V}V checks the HMAC externally:

σ=?HMAC(kMAC,m)\sigma \stackrel{?}= \mathsf{HMAC}(k^\mathsf{MAC}, m)σ=?HMAC(kMAC,m)

This is secure under the assumption that HMAC is collision-resistant.

Optimization 2: Include Only Sensitive Block in the Circuit

If the iii-th block (where i∈[1024]i \in [1024]i∈[1024]) contains sensitive data, we can selectively include it in the circuit:

C(x:{σ,σ^,Bi−,Bi+},w:{kEnc,kMAC,Bi}):⋀j=10251027B^j=?AES-CBC.Encrypt(kEnc,Bj)∧σ=?HMAC(kMAC,Bi−∥Bi∥Bi+)C(x: \{ \bm{\sigma}, \bm{\hat{\sigma}}, \textcolor{red}{\bm{B_{i-}}, \bm{B_{i+}}} \}, w: \{ k^\mathsf{Enc}, \textcolor{red}{k^{\mathsf{MAC}}, B_i} \}) : \\ \bigwedge_{j=1025}^{1027} \hat{B}_j \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_j) \land \textcolor{red}{\sigma \stackrel{?}= \mathsf{HMAC}(k^\mathsf{MAC}, \bm{B_{i-}}\|B_i\|\bm{B_{i+}})}C(x:{σ,σ^,Bi−​,Bi+​},w:{kEnc,kMAC,Bi​}):j=1025⋀1027​B^j​=?AES-CBC.Encrypt(kEnc,Bj​)∧σ=?HMAC(kMAC,Bi−​∥Bi​∥Bi+​)

Where:

  • Bi−=(B1,…,Bi−1)\bm{B_{i-}} = (B_1, \dots, B_{i-1})Bi−​=(B1​,…,Bi−1​)

  • Bi+=(Bi+1,…,B1024)\bm{B_{i+}} = (B_{i+1}, \dots, B_{1024})Bi+​=(Bi+1​,…,B1024​)

Optimization 3: Apply HMAC Optimization from Method 3

We can further reduce the circuit by applying the HMAC optimization described in Method 3:

C(x:{σ,σ^,si−1,si},w:{kEnc,Bi}):⋀j=10251027B^j=?AES-CBC.Encrypt(kEnc,Bj)∧si=?fH(si−1,Bi)C(x: \{ \bm{\sigma}, \bm{\hat{\sigma}}, \textcolor{red}{s_{i-1}, s_i} \}, w: \{ k^\mathsf{Enc}, B_i \}) : \\ \bigwedge_{j=1025}^{1027} \hat{B}_j \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_j) \land \textcolor{red}{s_i \stackrel{?}= f_H(s_{i-1}, B_i)}C(x:{σ,σ^,si−1​,si​},w:{kEnc,Bi​}):j=1025⋀1027​B^j​=?AES-CBC.Encrypt(kEnc,Bj​)∧si​=?fH​(si−1​,Bi​)

Then, outside the circuit:

  1. Compute si−1=H(Bi−)s_{i-1} = H(\bm{B_{i-}})si−1​=H(Bi−​)

  2. Verify si−1=?x.si−1s_{i-1} \stackrel{?}{=} x.s_{i-1}si−1​=?x.si−1​

  3. Verify σ=?H((k⊕opad)∥H(si∥Bi+))\sigma \stackrel{?}{=} H((k \oplus \mathsf{opad}) \| H(s_i \| \bm{B_{i+}}))σ=?H((k⊕opad)∥H(si​∥Bi+​))

Final Circuit Structure

C(x:{σ,σ^,si−1,si},w:{kEnc,Bi}):⋀j=10251027B^j=?AES-CBC.Encrypt(kEnc,Bj)∧si=?fH(si−1,Bi)∧extractBalance(Bi)≥1000C(x: \{ \textcolor{red}{\bm{\sigma}, \bm{\hat{\sigma}}, s_{i-1}, s_i} \}, w: \{ k^\mathsf{Enc}, \textcolor{red}{B_i} \}) : \\ \textcolor{red}{\bigwedge_{j=1025}^{1027} \hat{B}_j \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_j) \land s_i \stackrel{?}= f_H(s_{i-1}, B_i) \land \mathsf{extractBalance(}B_i) \ge 1000}C(x:{σ,σ^,si−1​,si​},w:{kEnc,Bi​}):j=1025⋀1027​B^j​=?AES-CBC.Encrypt(kEnc,Bj​)∧si​=?fH​(si−1​,Bi​)∧extractBalance(Bi​)≥1000

❗ Problem

However, there's still a critical issue: How do we know that BiB_iBi​​ actually corresponds to the field we intend to prove?

For example, if we're trying to prove the balance of account_id = 2, there's nothing in the above circuit that guarantees BiB_iBi​​ isn't actually from account 1 or 3.

Thus, we need an additional mechanism to ensure:

  • Context Integrity — the value is taken from the correct location

Method 5: Context Integrity via Structured Commitments

To address this, the prover P\mathcal{P}P constructs a sanitized version of the TLS response message RRR, denoted R′R'R′, where sensitive data fields are removed.

For example, suppose the original message looks like this:

{
  "accounts": [
    { "account_id": "", "balance": "" },
    { "account_id": "", "balance": "" },
    { "account_id": "", "balance": "" }
  ]
}

The actual values that were removed are committed separately as a vector of scalars:

Z=(1,500,2,2000,3,5000)Z = (1, 500, 2, 2000, 3, 5000)Z=(1,500,2,2000,3,5000)

Verification Process

The verifier proceeds as follows:

Each ZkZ_kZk​​ represents a redacted numeric value from the response, and the remaining static content is encoded as a list of fixed string tokens PPP, representing the prefix/suffix structure of the original JSON:

P = [
  "{\n  \"accounts\": [\n    { \"account_id\": ",
  ", \"balance\": ",
  " }, \"account_id\": ",
  ", \"balance\": ",
  " }, \"account_id\": ",
  ", \"balance\": ",
  "}\n  ]}"
]

Using this setup, the verifier V\mathcal{V}V checks the following conditions:

  1. R′R'R′ is a syntactically valid JSON.

  2. The following circuit holds:

C(x:{[Pk]0≤k≤n,[Zk]0≤k<n,[R]},w:{{Zk}0≤k<n}):[R]=?[P0∥Z0∥…∥Pn−1∥Zn−1∥Pn]∧⋀k=0n−1[Zk]=?Commit(Zk)C(x: \{ [P_k]_{0 \le k \le n}, [Z_k]_{0 \le k < n}, [R] \}, w: \{ \{Z_k\}_{0 \le k < n} \}) : \\ [R] \stackrel{?}= [P_0 \| Z_0 \| \dots \|P_{n-1}\|Z_{n-1}\|P_n] \land \bigwedge_{k=0}^{n-1} [Z_k] \stackrel{?}= \mathsf{Commit}(Z_k)C(x:{[Pk​]0≤k≤n​,[Zk​]0≤k<n​,[R]},w:{{Zk​}0≤k<n​}):[R]=?[P0​∥Z0​∥…∥Pn−1​∥Zn−1​∥Pn​]∧k=0⋀n−1​[Zk​]=?Commit(Zk​)
  • [X][X][X]: commitment of XXX

  • The commitment scheme used must support homomorphism under string concatenation.

TODO(chokobole): Add details on commitment schemes that support this homomorphic property.

Forgery Prevention Examples

If a malicious prover tampers with the response structure, the verification fails due to a mismatch in the number of commitments or string layout:

❌ Missing fields

{
  "accounts": [
    { "account_id": "", "balance": "" },
    { "account_id": "", "balance": "" }
  ]
}

❌ Sensitive data not redacted

{
  "accounts": [
    { "account_id": 1, "balance": 500 },
    { "account_id": "", "balance": "" },
    { "account_id": "", "balance": "" }
  ]
}

Final Circuit Structure

C(x:{σ,σ^,si−1,si,{[Pk]}0≤k≤n,{[Zk]}0≤k<n,[R],Zm},w:{kEnc,Bi,{Zk}0≤k<n}):⋀j=10251027B^j=?AES-CBC.Encrypt(kEnc,Bj)∧si=?fH(si−1,Bi)∧isCorrectBlock(Bj,Zm)∧[R]=?[P0∥Z0∥…∥Pn−1∥Zn−1∥Pn]∧⋀k=0n−1[Zk]=?Commit(Zk)∧Zm≥1000C(x: \{ \bm{\sigma}, \bm{\hat{\sigma}}, s_{i-1}, s_i, \textcolor{red}{\{[P_k]\}_{0 \le k \le n}, \{[Z_k]\}_{0 \le k < n}, [R], Z_m} \}, w: \{ k^\mathsf{Enc}, B_i, \textcolor{red}{\{Z_k\}_{0 \le k < n}} \}) : \\ \bigwedge_{j=1025}^{1027} \hat{B}_j \stackrel{?}= \mathsf{AES\text{-}CBC.Encrypt}(k^\mathsf{Enc}, B_j) \land s_i \stackrel{?}= f_H(s_{i-1}, B_i) \land \textcolor{red}{\mathsf{isCorrectBlock}(B_j, Z_m) \land [R] \stackrel{?}= [P_0 \| Z_0 \| \dots \|P_{n-1}\|Z_{n-1}\|P_n] \land \bigwedge_{k=0}^{n-1} [Z_k] \stackrel{?}= \mathsf{Commit}(Z_k) \land Z_m \ge 1000}C(x:{σ,σ^,si−1​,si​,{[Pk​]}0≤k≤n​,{[Zk​]}0≤k<n​,[R],Zm​},w:{kEnc,Bi​,{Zk​}0≤k<n​}):j=1025⋀1027​B^j​=?AES-CBC.Encrypt(kEnc,Bj​)∧si​=?fH​(si−1​,Bi​)∧isCorrectBlock(Bj​,Zm​)∧[R]=?[P0​∥Z0​∥…∥Pn−1​∥Zn−1​∥Pn​]∧k=0⋀n−1​[Zk​]=?Commit(Zk​)∧Zm​≥1000
  • mmm: the expected index of the scalar vector Z\bm{Z}Z.

TODO(chokobole): The paper and Chainlink blog do not provide detailed implementation of how isCorrectBlock\mathsf{isCorrectBlock}isCorrectBlock is constructed.

Conclusion

DECO enables verifiable web data disclosure, a capability that traditional TLS protocols do not support. It overcomes the inherent limitation of web data being "private-by-design" and trapped at the origin, using zero-knowledge proofs (ZK).

Its core contributions are as follows:

  • Through a Three-Party Handshake architecture, DECO securely splits the TLS session key (especially the MAC key) between the prover P\mathcal{P}P and verifier V\mathcal{V}V, preventing the prover from forging TLS responses.

  • It is designed to verify both TLS message integrity and application-level data predicates inside a ZK circuit, enabling selective and private disclosure.

  • The context integrity problem for DECO is solved through grammar-aware parsing and enabling position binding proofs in ZK—for example, proving a specific value comes from a structured key-value JSON field without revealing the entire context.

References

Server authentication: The server presents a digital certificate (usually ) to prove its identity. Client authentication is optional (e.g., in non-mutual TLS setups).

TLS ≥ 1.3: AEAD modes (e.g., AES-, )

qqq is the query: .accounts[] | select(.account_id == 2) | .balance

Core Idea

HMAC must be computed via a Two-Party MPC (2PC) protocol. In order to send a query, the prover P\mathcal{P} P and the verifier V\mathcal{V}V compute the MAC together as follows(We use the notation defined in ) :

SHA-256 follows the construction, processing messages in blocks. It computes hashes in the following recursive manner:

This method is based on the and aims to resolve a key limitation identified in Method 4—namely, the inability to verify that a block truly corresponds to the intended field.

In the above example, Z3Z_3Z3​​ corresponds to the balance for account_id = 2, solving the ambiguity issue from .

DECO leverages the structure of SHA-256, the MAC-then-Encrypt construction, and parsing-aware validation techniques to minimize 2-Party Computation (2PC) overhead and optimize ZK proof efficiency.

Written by from

💡
X.509
GCM
ChaCha20-Poly1305
jq
Merkle–Damgård
Chainlink blog post on DECO
Merkle–Damgård
https://arxiv.org/pdf/1909.00938
https://blog.chain.link/deco-introduction/
https://blog.chain.link/deco-provenance-and-authenticity/
https://blog.chain.link/deco-parsing-the-response/
https://blog.chain.link/hiding-secret-lengths/
Method 4
A41
ryan Kim
here
Source:
https://en.wikipedia.org/wiki/Authenticated_encryption#MAC-then-Encrypt_(MtE)