Skip to content

feat: implement SGX attestation verification#31

Merged
yingyangxu2026 merged 2 commits intomainfrom
feat/sgx-attestation
Apr 9, 2026
Merged

feat: implement SGX attestation verification#31
yingyangxu2026 merged 2 commits intomainfrom
feat/sgx-attestation

Conversation

@yingyangxu2026
Copy link
Copy Markdown
Collaborator

@yingyangxu2026 yingyangxu2026 commented Apr 8, 2026

Summary

Implements client-side SGX attestation verification for CDR SDK, replacing the placeholder that threw Error("not yet implemented").

Closes #30

Changes

attestation.ts

  • parseSgxQuote(report): Parse SGX DCAP Quote v3 binary to extract MRENCLAVE, MRSIGNER, ISV SVN
  • verifyAttestation(report, config): Verify extracted fields against user-provided AttestationConfig
  • Field offsets verified against SGXValidationHook.sol in piplabs/story

SGX DCAP Quote v3 layout:

Offset 0-47:    Quote Header (48 bytes)
Offset 48-431:  Enclave Report Body (384 bytes)
  Body+64:      MRENCLAVE  (32 bytes) → quote offset 112
  Body+128:     MRSIGNER   (32 bytes) → quote offset 176
  Body+258:     ISV SVN    (2 bytes)  → quote offset 306
Offset 432+:    Auth Data (variable)

observer.ts

  • getValidatorAttestations(params?): New method to fetch enclaveReport bytes from DKG Registered events, keyed by validator address
  • Includes 7-day lookback with fallback to block 0

Usage

import { verifyAttestation } from "@piplabs/cdr-sdk";

// Get attestation reports from chain
const attestations = await cdr.observer.getValidatorAttestations();

// Verify each validator
for (const [addr, report] of attestations) {
  const result = await verifyAttestation(report, {
    expectedMrEnclave: "0x51c08cf3...",
    minSecurityVersion: 1,
  });
  if (!result.valid) console.warn(`${addr}: ${result.error}`);
}

Note

This is client-side field verification only. The cryptographic signature chain (Intel QE → PCK → root CA) is verified on-chain by SGXValidationHook via Automata DCAP contracts. This provides defense-in-depth for SDK consumers.

Test Plan

  • Unit test: parseSgxQuote with known quote bytes — SEC-08a (mock 432-byte quote, verify exact MRENCLAVE/MRSIGNER/SVN extraction)
  • Unit test: verifyAttestation with matching/mismatching config — SEC-08bf (positive), SEC-08gm (negative: empty, too-short, MRENCLAVE/MRSIGNER/SVN mismatch, short-circuit)
  • E2E: getValidatorAttestations returns non-empty map on devnet — SEC-08n (3 validators, 4734-byte real SGX quotes), SEC-08o (all share MRENCLAVE 0x032821cf1c...), SEC-08p (wrong config rejected)

CI run: piplabs/story-cdr-e2e#84 — 24/24 passed (16 SEC-08 sub-tests + 8 other security tests)

Base automatically changed from fix/sdk-issues to main April 8, 2026 13:05
Parse SGX DCAP Quote v3 binary to extract and verify MRENCLAVE,
MRSIGNER, and ISV SVN against user-provided configuration.

attestation.ts:
- parseSgxQuote(): extract fields from raw SGX quote binary
- verifyAttestation(): check extracted fields against AttestationConfig
- Offsets verified against SGXValidationHook.sol in piplabs/story

observer.ts:
- getValidatorAttestations(): new method to fetch enclaveReport bytes
  from DKG Registered events, keyed by validator address
- Includes 7-day lookback with fallback to block 0

Note: This is client-side field verification only. Cryptographic
signature chain (Intel QE → PCK → root CA) is verified on-chain
by SGXValidationHook via Automata DCAP contracts.
@yingyangxu2026 yingyangxu2026 self-assigned this Apr 9, 2026
@lucas2brh lucas2brh requested a review from wo-o April 9, 2026 07:18
@jinn-agent
Copy link
Copy Markdown

jinn-agent Bot commented Apr 9, 2026

Overall the implementation is clean and well-structured. A few correctness/security findings worth addressing:

  1. Security footgunverifyAttestation(report) with no config always returns { valid: true } as long as the quote parses. The docstring says fields are optional, but in a security-critical path this silent pass is easy to misuse.
  2. Inconsistency in fallback logicgetValidatorAttestations falls back to block 0 when the lookback window is empty; getRegisteredValidators does not. Code that calls both methods may see different validator sets on chains where registrations are older than 7 days.
  3. ISV_SVN_SIZE declared but never used – dead constant.
  4. verifyAttestation is async with no await – breaks nothing functionally, but every caller is forced to await unnecessarily and it obscures the synchronous nature of the checks.

Review iteration 2 · Commit 3fe2c60 · 2026-04-09T07:31:07Z

- attestation.ts: add SGX quote version guard (bytes 0-1 must be 3 for
  DCAP v3). Rejects Quote v4/TDX quotes that have different report-body
  offsets and would produce incorrect MRENCLAVE/MRSIGNER.
- observer.ts: getValidatorAttestations no longer silently falls back to
  block 0 when caller explicitly provides fromBlock. Only auto-fallback
  when using the default lookback window.
@yingyangxu2026
Copy link
Copy Markdown
Collaborator Author

Both addressed in 3fe2c60:

  1. Quote version guard: parseSgxQuote now validates bytes 0-1 (LE) == 3 for DCAP v3. Rejects v4/TDX quotes with Unsupported SGX quote version: N (expected 3 for DCAP v3).

  2. getValidatorAttestations fallback: Now only auto-fallback to block 0 when using the default lookback window. If caller explicitly provides fromBlock, their intent is respected — no silent scope expansion. Matches getRegisteredValidators behavior.

@yingyangxu2026 yingyangxu2026 merged commit 80f0b78 into main Apr 9, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement SGX attestation verification in verifyAttestation()

3 participants