critical priority high complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

encryptField(plaintext, key) returns a base64-encoded ciphertext string containing the GCM tag and IV/nonce prepended to the ciphertext
decryptField(ciphertext, key) correctly reconstructs the original plaintext given valid inputs
AES-256-GCM is the only cipher used; any other algorithm is rejected at compile time via type enforcement
A 96-bit random nonce is generated per encryption call using a cryptographically secure RNG; no nonce is ever reused
Key derivation uses HKDF-SHA-256 over the authenticated session token; the derived key is 256 bits
EncryptionException is a typed sealed class with subtypes: KeyDerivationFailure, EncryptionFailure, DecryptionFailure, and InvalidCiphertextFormat
No plaintext value, derived key, or raw session token appears in any log output, stack trace, or exception message
All functions are pure (no global side-effects) and can be called from an isolate
Unit tests cover: encrypt→decrypt roundtrip, wrong key yields DecryptionFailure, tampered ciphertext yields DecryptionFailure, empty plaintext encrypts and decrypts correctly, null/empty key throws KeyDerivationFailure
The module exposes a single library file (field_encryption_utils.dart) with no transitive Supabase dependency

Technical Requirements

frameworks
Flutter
Dart
data models
session token (string)
EncryptionException (sealed class)
performance requirements
encrypt + decrypt roundtrip must complete in under 20 ms on a mid-range device (tested via flutter_test Stopwatch)
Key derivation must not block the UI thread; wrap in compute() if benchmarks exceed 8 ms
security requirements
Use the pointycastle or encrypt Dart package with explicit AES/GCM/NoPadding mode — do not use ECB or CBC
Nonce must be generated with dart:math SecureRandom or equivalent CSPRNG
Derived key must never be stored in SharedPreferences, Hive, or any persistent store
Session token must be zeroed from memory after key derivation (overwrite the Uint8List bytes)
Disable Dart reflection/mirrors on the module to prevent inadvertent logging of private fields
GDPR compliance: the module must not retain any plaintext beyond the function call stack

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use the encrypt (^5.0.0) Dart package which wraps PointyCastle with a cleaner API. For HKDF, use the cryptography (^2.7.0) package's Hkdf class — it is pure Dart and safe for isolates. Store the nonce as the first 12 bytes of the stored ciphertext blob; append the 16-byte GCM authentication tag as output by the library (most Dart GCM implementations append the tag automatically). The public API should be: `Future encryptField(String plaintext, Uint8List key)` and `Future decryptField(String encoded, Uint8List key)`, where the String is base64url.

Key derivation function signature: `Future _deriveKey(String sessionToken)` — mark private. Avoid try/catch inside the encrypt/decrypt core; instead let exceptions propagate and wrap at the public API boundary into typed EncryptionExceptions. This makes the error surface explicit and prevents silent swallowing.

Testing Requirements

Write unit tests exclusively with flutter_test. Test file: test/field_encryption_utils_test.dart. Required test groups: (1) encrypt/decrypt roundtrip for short strings, long strings (>4 KB), and strings with special UTF-8 characters; (2) authentication tag verification — flip one byte in ciphertext and assert DecryptionFailure is thrown; (3) nonce uniqueness — call encryptField 1000 times and assert all nonces are distinct; (4) key derivation determinism — same session token must always produce the same key; (5) error type assertions — every error path must throw the exact EncryptionException subtype documented. Target 100% branch coverage of the utils file.

No mocks required; use real crypto primitives.

Component
Encrypted Field Display Widget
ui high
Epic Risks (3)
medium impact medium prob scope

The encrypted-field-display confirmation dialog adds interaction steps that may frustrate coordinators who access sensitive fields frequently, leading to requests to bypass the flow or skip read-receipt logging, which would violate Blindeforbundet's compliance requirements.

Mitigation & Contingency

Mitigation: Design the confirmation dialog to be as minimal as possible (one clear sentence, single confirm action) and ensure it does not reappear for the same field within a single screen session. Validate the UX with Blindeforbundet coordinators during the TestFlight pilot before finalising.

Contingency: If coordinators raise strong objections, escalate to Blindeforbundet's data protection officer to determine whether a lighter confirmation pattern (e.g., biometric confirmation instead of dialog) satisfies their compliance obligation.

medium impact medium prob technical

The activity-history-list infinite scroll requires paginated Supabase queries. Contacts with hundreds of activities (e.g., an HLF peer mentor with 380 annual registrations) could cause slow page loads or memory pressure on older devices if pagination boundaries are set too large.

Mitigation & Contingency

Mitigation: Use a page size of 20 records with cursor-based pagination. Implement list item recycling using Flutter's ListView.builder. Benchmark memory usage with 400+ item simulation on a low-end test device before TestFlight release.

Contingency: If performance degrades on older devices, reduce page size to 10 and add a time-window filter (last 30 days, last 6 months, all) so the default view loads a manageable record count for most coordinators.

low impact high prob scope

The cognitive load rule engine (from the Cognitive Accessibility feature) mandates no more than 7 fields per screen section. If a contact model has more than 7 editable fields, the edit-contact-screen layout must be split into sections, adding complexity not accounted for in the initial scope.

Mitigation & Contingency

Mitigation: Audit the full contact field list from all four organisations before implementation. Group fields into logical sections (personal info, contact details, affiliation) so no single section exceeds 7 fields. Use the cognitive-load-rule-engine component if it is already delivered by the Cognitive Accessibility feature.

Contingency: If the rule-engine component is not yet available, implement a simple manual section layout with accordion-style expansion for less-frequently edited fields to stay within the 7-field guideline without blocking delivery.