Architecture Deep Dive
Read this before touching the crypto layer or the verification path.
Required reading
Before making changes to the core verification logic:
docs/Drs_language&algorithms.md— authoritative reference for language choices and corrected algorithmsdocs/Drs_architecture_v2.md— the full DRS 4.0.0 specification- 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:
| Module | Responsibility | Does NOT do |
|---|---|---|
drs-core/src/crypto/ | Ed25519 sign/verify, SHA-256 | JWT parsing, policy evaluation |
drs-core/src/chain/ | Chain hash computation, verify_chain | Network I/O, caching |
drs-core/src/jcs/ | RFC 8785 canonicalisation | Serialisation to non-JSON formats |
drs-core/src/capability/ | Policy evaluation, attenuation check | Crypto operations |
drs-core/src/did/ | did:key decode to public key bytes | DID resolution with caching |
drs-verify/pkg/resolver/ | DID resolution + LRU cache | Chain verification |
drs-verify/pkg/verify/ | verify_chain (6 blocks) | DID resolution, HTTP I/O |
drs-verify/pkg/middleware/ | HTTP request/response handling | Verification logic |
drs-verify/pkg/policy/ | Policy field evaluation | Signing, serialisation |
drs-sdk/src/sdk/ | Issuance (sign + build JWTs) | Verification |
drs-sdk/src/verify/ | HTTP client to drs-verify | Verification logic itself |
drs-sdk/src/cli/ | CLI command dispatch | SDK 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
- Write it in Rust first (
drs-core/src/) - Write tests with official test vectors from the relevant RFC
- If Go middleware needs it: re-implement with the same interface (Go cannot call Rust without CGO complexity — prefer reimplementation for simple algorithms)
- 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 — useResult<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