critical priority high complexity backend pending backend specialist Tier 1

Acceptance Criteria

A createTamperEvidentBlob(encryptedBlob, orgId, timestamp, schemaVersion) function exists and returns a TamperEvidentBlob containing the EncryptedBlob plus an hmacSignature (32-byte Uint8List)
HMAC-SHA256 is computed over a canonical byte string composed of: ciphertext || nonce || orgId (UTF-8) || timestamp (ISO-8601 UTF-8) || schemaVersion (UTF-8) || keyVersion (big-endian uint32) — fields delimited by a fixed separator byte (0x00)
A verifyBlobIntegrity(tamperEvidentBlob) function exists and returns true only when the recomputed HMAC matches the stored hmacSignature using a constant-time comparison (crypto.subtle.timingSafeEqual or equivalent)
verifyBlobIntegrity returns false (not an exception) when a single bit of the ciphertext has been flipped
verifyBlobIntegrity returns false when the orgId field in the blob metadata has been changed
verifyBlobIntegrity returns false when the timestamp field has been modified
verifyBlobIntegrity returns false when the hmacSignature itself has been truncated or corrupted
The decryption path in the encryption service always calls verifyBlobIntegrity before attempting AES-GCM decryption — if verification fails, decryption is aborted and an IntegrityViolationError is returned
HMAC signing key is derived separately from the encryption key using a distinct HKDF context (e.g. info = 'hmac-signing' instead of 'aes-encryption') to ensure key separation
Unit tests achieve 100% branch coverage of createTamperEvidentBlob and verifyBlobIntegrity

Technical Requirements

frameworks
Supabase Edge Functions (Deno)
apis
Supabase Edge Functions REST API
data models
confidentiality_declaration
declaration_acknowledgement
performance requirements
HMAC-SHA256 computation over a 10KB payload must complete in under 10ms server-side
verifyBlobIntegrity must use constant-time comparison to prevent timing side-channel attacks
security requirements
HMAC signing key derived via HKDF-SHA256 with a distinct info parameter ('declaration-hmac-v1') separate from the AES encryption key derivation — prevents key reuse across cryptographic primitives
Constant-time comparison (crypto.subtle.timingSafeEqual) mandatory for HMAC verification — standard equality operators are forbidden to prevent timing attacks
Integrity verification must occur BEFORE decryption is attempted — fail-fast to prevent oracle attacks on the AES-GCM layer
Canonical message format must be deterministic and length-delimited or field-separated with a fixed delimiter to prevent message extension attacks
Blob schema version field included in HMAC input to ensure future schema changes invalidate old signatures

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Build on top of the EncryptedBlob type from task-001. The TamperEvidentBlob type wraps EncryptedBlob and adds hmacSignature: Uint8Array. For canonical serialization, concatenate fields in a fixed order with 0x00 delimiter bytes — document this order precisely in a comment because any change breaks all existing blobs. Use SubtleCrypto.sign('HMAC', hmacKey, canonicalBytes) for signing and SubtleCrypto.verify('HMAC', hmacKey, signature, canonicalBytes) for verification — both are available natively in Deno.

Do NOT use a JS equality operator (===) for comparing signatures — always SubtleCrypto.timingSafeEqual. Store schemaVersion as a string like '1.0' in the blob metadata to allow future evolution. Treat this as a security-critical code path: have at least one other team member review the canonicalization logic before merging.

Testing Requirements

Deno unit tests covering: valid blob produces verifyBlobIntegrity = true; single-bit flip in ciphertext produces false; orgId replacement produces false; timestamp mutation produces false; hmac truncation produces false; hmac zeroed-out produces false (not crash). Test that constant-time comparison is used by asserting the function does not throw on mismatched lengths. Integration test: full round-trip — create blob, tamper with one byte, verify returns false; create blob without tampering, decrypt succeeds. Run via `deno test` in the Edge Functions directory as part of CI.

Component
Declaration Encryption Service
service high
Epic Risks (4)
high impact medium prob security

Org-scoped encryption key management is complex. If keys are not correctly isolated per organization, a breach in one org's key could expose another org's declarations. Additionally, key rotation is not specified but may be needed for compliance, and the current implementation may not support it.

Mitigation & Contingency

Mitigation: Use Supabase Vault or a dedicated secrets management approach for org-scoped key storage. Define the key derivation strategy (per-org master key) in a security design document reviewed before implementation begins. Include key isolation tests in the test suite.

Contingency: If a full per-org key management system cannot be safely implemented within the sprint, fall back to a single platform-level encryption key with strict RLS isolation as a temporary measure, flagging the key rotation gap as a security debt item with a defined resolution milestone.

medium impact medium prob integration

Push notification delivery to drivers depends on FCM token availability and device connectivity. If a driver has not granted notification permissions or has an expired FCM token, the declaration delivery notification will silently fail, leaving the coordinator unaware and the declaration unacknowledged.

Mitigation & Contingency

Mitigation: Implement delivery status tracking in declaration-notification-service. Fall back to in-app notification and SMS (if configured) when push delivery fails. Expose delivery failure status in the declaration status badge so coordinators can identify and manually follow up.

Contingency: If push delivery proves unreliable, implement a polling-based in-app notification fallback where drivers see pending declarations on next app open, ensuring the workflow can complete even without push notifications.

medium impact medium prob technical

The acknowledgement service is meant to validate that the driver has fully scrolled through the declaration before confirming. Implementing reliable scroll completion detection in Flutter across different screen sizes and font sizes is technically non-trivial and could be bypassed.

Mitigation & Contingency

Mitigation: Implement scroll position tracking using ScrollController with a threshold (e.g., 95% of content height reached) and record the validated state server-side before allowing acknowledgement submission. Document the approach in the legal sign-off checkpoint noted in the feature documentation.

Contingency: If reliable scroll detection cannot be implemented within the sprint, add a mandatory reading delay timer (e.g., estimated reading time based on word count) as an alternative validation mechanism, pending legal review of the approach.

medium impact low prob dependency

The driver assignment service must coordinate with the threshold-based expense approval workflow for fees above configured thresholds. If the expense approval workflow interface changes or is not yet stable, the integration point could break or produce incorrect routing behavior.

Mitigation & Contingency

Mitigation: Define a clear interface contract between driver-assignment-service and the expense approval workflow before implementation. Use dependency injection so the expense workflow client can be mocked in tests. Monitor the expense approval feature for interface changes.

Contingency: If the expense approval workflow interface is not stable, implement a direct database insert to the expense records table as a temporary bypass, with a flag indicating manual review is needed, until the stable interface is available.