Architecture Deep Dive

Read this before touching the crypto layer or the verification path.

Required reading

Before making changes to the core verification logic:

  1. docs/Drs_language&algorithms.md — authoritative reference for language choices and corrected algorithms
  2. docs/Drs_architecture_v2.md — the full DRS 4.0.0 specification
  3. False Positives: What We Tried — the v1 and v2 failures

Module boundaries

Each module has exactly one responsibility. Do not write code that crosses these boundaries:

ModuleResponsibilityDoes NOT do
drs-core/src/crypto/Ed25519 sign/verify, SHA-256JWT parsing, policy evaluation
drs-core/src/chain/Chain hash computation, verify_chainNetwork I/O, caching
drs-core/src/jcs/RFC 8785 canonicalisationSerialisation to non-JSON formats
drs-core/src/capability/Policy evaluation, attenuation checkCrypto operations
drs-core/src/did/did:key decode to public key bytesDID resolution with caching
drs-verify/pkg/resolver/DID resolution + LRU cacheChain verification
drs-verify/pkg/verify/verify_chain (6 blocks)DID resolution, HTTP I/O
drs-verify/pkg/middleware/HTTP request/response handlingVerification logic
drs-verify/pkg/policy/Policy field evaluationSigning, serialisation
drs-sdk/src/sdk/Issuance (sign + build JWTs)Verification
drs-sdk/src/verify/HTTP client to drs-verifyVerification logic itself
drs-sdk/src/cli/CLI command dispatchSDK logic

Data flow

Issuance (TypeScript SDK):
  Policy params → jcsSerialise(payload) → ed.signAsync → JWT string

Verification (Go, calls Rust):
  JWT string → parse header/payload → resolve_did → ed25519_verify_strict
            → check_policy_attenuation → is_revoked → VerifiedChain

DID resolution (Go):
  DID string → base58Decode → strip multicodec prefix [0xed, 0x01]
             → [32]byte public key (constant-time prefix check)

Adding a new algorithm

  1. Write it in Rust first (drs-core/src/)
  2. Write tests with official test vectors from the relevant RFC
  3. If Go middleware needs it: re-implement with the same interface (Go cannot call Rust without CGO complexity — prefer reimplementation for simple algorithms)
  4. TypeScript only calls WASM or the HTTP API — no algorithm logic in TypeScript

Security-sensitive code checklist

Before merging any change to the crypto or verification path:

  • Comparisons on key material use constant-time equality (subtle::ConstantTimeEq / crypto/subtle.ConstantTimeCompare)
  • No unwrap() in Rust library code — use Result<T, E> and propagate
  • No _ on error returns in Go production paths — check and propagate
  • Signing keys are not logged, even in debug
  • New error messages do not expose key material or sensitive internal state