Skip to main content

@siglo/crypto Security & Cryptography Guide

@siglo/crypto provides zero-dependency, enterprise-grade security primitives for Siglo workflows. Built directly on native Node.js node:crypto and standard Web Crypto APIs, it introduces timing-attack safe webhook signature verification, intra-window replay attack defense, pluggable cryptographic protocols (symmetric HMAC, asymmetric Ed25519, and RSA), AEAD envelope field encryption (aes-256-gcm) across durable checkpoints, multi-key KeyRing rotation, Additional Authenticated Data (AAD) multi-tenant isolation, ephemeral HKDF subkeys, streaming HMAC calculation with disk spooling, and declarative PII context redaction with structural sharing.

1. Executive Summary & Problem Space

Enterprise workflows ingest untrusted external events (webhooks) and process highly sensitive business assets (payment credentials, PII, healthcare records, API secrets). Implementing cryptographic operations manually inside workflow steps introduces severe security vulnerabilities:

2. Architecture & Step Lifecycle Integration

@siglo/crypto relies exclusively on native Node.js node:crypto, introducing zero external npm dependencies.

3. Installation

Install @siglo/crypto alongside @siglo/core:
Peer dependencies:
  • @siglo/core: ^1.0.0
  • Node.js: >= 18.0.0 (native node:crypto)

4. Webhook Security (verifyWebhookStep)

4.1 Constant-Time Comparison (timingSafeCompare)

Standard JavaScript string equality operators (=== or ==) return early as soon as the first non-matching byte is encountered. Attackers measuring sub-microsecond network latencies can deduce the signature byte-by-byte. @siglo/crypto eliminates timing leaks by using crypto.timingSafeEqual preceded by constant-time length verification:
Guards:
  • Returns false immediately if buffer lengths differ (preventing buffer overflow exceptions).
  • Returns false on empty strings or buffers.
  • Gracefully handles invalid encodings without crashing the process.

4.2 Replay Attack Defense & Intra-Window Deduplication

Replay attacks occur when an unauthorized actor intercepts a valid webhook request and resends it to the workflow endpoint. verifyWebhookStep enforces two layers of replay protection:
  1. Timestamp Drift Tolerance (toleranceSeconds):
    • Rejects events where |currentTime - webhookTimestamp| > toleranceSeconds (default: 300 seconds / 5 minutes).
  2. Intra-Window Nonce Deduplication (dedupStore):
    • Prevents an attacker from replaying an intercepted request 500 times within the 300-second window.
    • Computes a deterministic SHA-256 delivery hash: sha256(provider + timestamp + signatureHeader).
    • Atomically claims the key with a TTL matching toleranceSeconds. If already claimed, throws a non-retryable ValidationException.

4.3 Streaming Payloads & Ephemeral Disk Spooling

When ingesting large webhooks (e.g. 50MB–200MB multi-part files or bulk syncs), buffering contiguous payloads in memory causes Node.js heap exhaustion (OOM). verifyWebhookStep natively supports streams and async iterables with memory guardrails:
  • Disk Spooling Protection: Large streams are spooled to temporary files under os.tmpdir() to prevent heap memory exhaustion during receipt. Note that protocol verification subsequently reads the spooled file into memory as a Buffer before HMAC calculation rather than performing a bounded-memory streaming HMAC.
  • Payload Size Limits (maxBodyBytes): Rejects oversized payloads exceeding limits (default: 10MB) by throwing a non-retryable ValidationException (which web adapters map to HTTP 400 Bad Request by default).
  • Disk Spooling (spoolThresholdBytes): Streams exceeding the threshold (default: 5MB) are automatically spooled to temporary disk storage. The step outputs a compact { payloadRef: { storage: 'file', path: '...', bytes: ... } } reference, keeping durable database checkpoints small and fast.

4.4 Pluggable Protocol Architecture

Real-world enterprise systems ingest webhooks from dozens of vendors with fundamentally different headers, signature hashing schemes, and payload formats:

Supported Built-in Presets

4.5 Custom & Asymmetric Webhook Protocols

For a runnable Ed25519 request-authentication example with Express, key lookup, body digests, and nonce protection, see Accept Asymmetrically Signed Orders. Supply an inline protocol object implementing the WebhookProtocol interface:

5. Envelope Field Encryption (encryptFieldsStep & decryptFieldsStep)

Siglo serializes accumulated workflow state to persistent storage (PostgreSQL, Redis) whenever execution pauses (e.g. waitForEvent()). Storing raw SSNs, card numbers, or API tokens violates GDPR and PCI-DSS compliance. @siglo/crypto provides transparent, in-place Envelope Encryption using AES-256-GCM:

5.1 Encrypted Envelope Structure

Target fields are replaced in-place with an EncryptedEnvelope object:

5.2 Key Rotation & Checkpoint Archaeology (KeyRing)

In long-running durable workflows (e.g. waitForEvent('LEGAL_APPROVAL', { timeout: '90d' })), checkpoints encrypted on Day 1 will be resumed after quarterly key rotation. StaticKeyRing enables multi-key rotation without failing checkpoint resurrection:
  • Active Key: Used for encrypting all new fields.
  • Historical Keys: Preserved in the ring for decrypting older envelopes identified by envelope.kid.
  • Lazy Re-Keying: Detects when an envelope was encrypted under an older key, invoking onNeedsRekey or marking $needsRekey = true to allow in-flight re-encryption under the active key before the next checkpoint save.

5.3 Cryptographic Hardening, AAD & HKDF Key Derivation

  1. Context-Bound Ephemeral Subkeys (HKDF): Avoid encrypting millions of records under a static master key. @siglo/crypto supports RFC 5869 HKDF derivation:
  2. Additional Authenticated Data (AAD): Binds tenant identity into the AES-GCM authentication tag. If ciphertext is swapped across tenant boundaries, decryption immediately throws an authentication tag failure:
  3. NIST SP 800-38D Usage Ceiling: Tracks key invocations and warns when a static key crosses 2242^{24} (16,777,21616,777,216) invocations.

6. Deep Path Redaction & Structural Sharing (maskFieldsStep)

maskFieldsStep declaratively sanitizes sensitive fields before telemetry emission to Siglo DevTools (@siglo/monitor) and OpenTelemetry dashboards.

6.1 Dot-Notation Paths & Array Wildcards

Target deeply nested object properties and array collections directly:

6.2 Immutable Structural Sharing (Copy-on-Write)

Unlike shallow object copying ({ ...ctx }), @siglo/crypto uses structural sharing path traversal:
  • Clones only ancestor objects along the modified path (O(d)O(d) wrappers where dd is path depth).
  • Untargeted sibling subtrees (ctx.user.profile, ctx.unrelated) retain exact referential identity (===).
  • Prevents accidental shared-reference mutation and race conditions across parallel combinator branches.
  • Includes deepFreeze(obj) helper for development mode immutability assertions.

7. TypeScript API Reference