> ## Documentation Index
> Fetch the complete documentation index at: https://woku.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Encryption at rest and integrity signatures

> How Woku protects sensitive stored data and how it ensures that critical documents cannot be modified without detection

Woku uses two cryptographic primitives to protect the data it
stores in its database:

* **AES-256-GCM encryption** for sensitive fields at rest.
* **HMAC-SHA256** to sign critical documents and detect
  any later modification.

Both primitives live in a single `EncryptionService` on the backend and
use the same master key (`ENCRYPTION_KEY`) derived via `scrypt`.

## Encryption at rest

### When encryption happens

Every field that contains personal information or credentials is encrypted
before being persisted in MongoDB. In particular:

* **Client data** in anonymous responses: email and phone are
  stored encrypted (`form-submission`).
* **Integration credentials** and OAuth tokens (when a
  company connects Woku with Salesforce, Sperant, etc.).
* **Webhook secrets** that the company configures to receive
  events.

### How it works

|                  | Value                                              |
| ---------------- | -------------------------------------------------- |
| Algorithm        | AES-256-GCM (built-in authentication via auth tag) |
| Key              | Derived with `scrypt(ENCRYPTION_KEY, salt, 32)`    |
| IV               | Random 12 bytes for each encryption                |
| Persisted format | `<iv-hex>:<authTag-hex>:<ciphertext-hex>`          |

Each encryption uses a new IV, so encrypting the same value twice
produces two different ciphertexts (equality cannot be inferred by
inspection). The `authTag` is validated on every decryption: if the
ciphertext was altered, decryption throws an error instead of
returning garbage.

### Search over encrypted fields

To be able to search by email without decrypting every record, Woku
stores a parallel SHA-256 hash (`generateSearchHash`) in lowercase.
The search compares hash against hash, not text against text.

## Integrity signatures (HMAC-SHA256)

### What they are for

When a company requires that its form responses cannot
be modified after being stored (legal auditing, compliance
in a regulated sector), Woku can sign each document with
**HMAC-SHA256** at write time and verify the signature when
reading it. If someone modifies the document in the database directly,
verification fails and the read returns an error.

### How it works

```
signature = HMAC-SHA256(ENCRYPTION_KEY, "<scope>:<canonical json of the doc>")
```

|              | Detail                                   |
| ------------ | ---------------------------------------- |
| Algorithm    | HMAC-SHA256                              |
| Key          | The same `ENCRYPTION_KEY` derived above  |
| Input        | Concatenation `<scope>:<json>`           |
| Output       | 64 hex characters                        |
| Verification | Constant-time (`crypto.timingSafeEqual`) |

**Scope = domain separation**: each use case signs with a
different scope (e.g.: `form-submission`, `form-response`). A signature
generated for one scope **is not valid** in another, so an attacker
cannot move signatures between document types.

**Canonical JSON**: Woku recursively sorts the keys of the
objects before signing, so two logically equal representations
of the same document produce the same signature (regardless of the
order in which the API received them or Mongo wrote them). Arrays are NOT
reordered, positional order is semantically significant.

### Enable it for your company

There is a per-company flag: `integrityHashEnabled`. When it is enabled,
new writes of critical documents are signed; reads
verify and fail if the signature does not match.

> **Current status:** the primitive (`signDocument` / `verifySignature`)
> is already available on the backend and the flag exists in the company
> model. Automatic integration with `form-submission` and
> `form-response` (when `integrityHashEnabled` is active) arrives
> in a later release. Customers with strict compliance who
> need this sooner can coordinate it with support.

## Key management

* `ENCRYPTION_KEY` lives in SSM Parameter Store as a `SecureString`
  under `/shared/prod/secrets/ENCRYPTION_KEY` and is injected into the
  container at startup (see [Secrets management](/docs/en/seguridad/secrets-management)).
* Key rotation is manual today. It involves:
  1. Generating the new key.
  2. Re-encrypting all records (migration script) with the new
     key before switching `ENCRYPTION_KEY` in SSM, during a maintenance
     window.
  3. Discarding the old one.
* There is no HSM in V1; the key lives in the process environment
  variables (in memory) and is never logged.

## Guarantees

* **Authenticated encryption**: if an attacker modifies the ciphertext in
  the database, the `authTag` does not validate and decryption throws an error, so
  the altered record is not returned as if it were valid.
* **Constant-time signature**: `verifySignature` uses comparison
  resistant to timing attacks.
* **Domain separation**: the scope in HMAC prevents reuse of
  signatures between document types.
* **Deterministic JSON**: canonicalization removes ambiguity
  from key order.

## Known limitations

* Rotating `ENCRYPTION_KEY` requires a maintenance window
  (there is no key versioning yet).
* Signing of `form-submission` / `form-response` is not wired
  automatically; it arrives in the Stage 10 release.
* Applying it to third-party integration credentials depends
  on those integrations being built (Stage 6).
