Skip to content

feat(crypto): bind GCM ciphertext to entity context via AAD#83

Merged
scarson merged 1 commit intodevfrom
feat/gcm-aad-binding
Apr 8, 2026
Merged

feat(crypto): bind GCM ciphertext to entity context via AAD#83
scarson merged 1 commit intodevfrom
feat/gcm-aad-binding

Conversation

@scarson
Copy link
Copy Markdown
Owner

@scarson scarson commented Apr 8, 2026

Summary

  • Add Additional Authenticated Data (AAD) parameter to Encrypt, Decrypt, and DecryptWithFallback in internal/crypto/aes.go
  • SSO client secrets bound to org_id, MFA TOTP secrets bound to user_id, doctor sentinel bound to fixed label — prevents ciphertext relocation between database rows
  • Key rotation CLI updated to read org_id per row and pass as AAD during re-encryption
  • Add dev/specs/sso-secret-storage.md documenting the full encryption architecture

Test plan

  • 4 new crypto unit tests: AAD round-trip, AAD mismatch rejected, AAD vs nil rejected, fallback with AAD
  • All 17 crypto unit tests pass
  • SSO CRUD integration tests pass
  • MFA/TOTP integration tests pass
  • OAuth/OIDC integration tests pass
  • Key rotation integration tests pass
  • Doctor encryption sentinel tests pass
  • go build ./... and go vet ./... clean

🤖 Generated with Claude Code

Add Additional Authenticated Data (AAD) to AES-256-GCM encrypt/decrypt,
preventing ciphertext relocation between database rows. SSO client
secrets are bound to org_id, MFA TOTP secrets to user_id, and the
doctor encryption sentinel to a fixed label.

Also adds dev/specs/sso-secret-storage.md documenting the full
encryption architecture for external sharing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@scarson scarson merged commit 866ab4a into dev Apr 8, 2026
@scarson scarson deleted the feat/gcm-aad-binding branch April 8, 2026 05:22
scarson added a commit that referenced this pull request Apr 8, 2026
…tes (#84)

* chore(deps): bump github.com/go-jose/go-jose/v4 from 4.1.3 to 4.1.4 (#71)

Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](go-jose/go-jose@v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Sam Carson <samuel.carson@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump defu from 6.1.4 to 6.1.6 in /web (#72)

Bumps [defu](https://github.com/unjs/defu) from 6.1.4 to 6.1.6.
- [Release notes](https://github.com/unjs/defu/releases)
- [Changelog](https://github.com/unjs/defu/blob/main/CHANGELOG.md)
- [Commits](unjs/defu@v6.1.4...v6.1.6)

---
updated-dependencies:
- dependency-name: defu
  dependency-version: 6.1.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Sam Carson <samuel.carson@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump picomatch in /web (#66)

Bumps  and [picomatch](https://github.com/micromatch/picomatch). These dependencies needed to be updated together.

Updates `picomatch` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/picomatch@4.0.3...4.0.4)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/picomatch@4.0.3...4.0.4)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 4.0.4
  dependency-type: indirect
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Sam Carson <samuel.carson@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump github.com/danielgtaylor/huma/v2 from 2.37.2 to 2.37.3 (#69)

Bumps [github.com/danielgtaylor/huma/v2](https://github.com/danielgtaylor/huma) from 2.37.2 to 2.37.3.
- [Release notes](https://github.com/danielgtaylor/huma/releases)
- [Commits](danielgtaylor/huma@v2.37.2...v2.37.3)

---
updated-dependencies:
- dependency-name: github.com/danielgtaylor/huma/v2
  dependency-version: 2.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump google.golang.org/genai from 1.50.0 to 1.52.0 (#70)

Bumps [google.golang.org/genai](https://github.com/googleapis/go-genai) from 1.50.0 to 1.52.0.
- [Release notes](https://github.com/googleapis/go-genai/releases)
- [Changelog](https://github.com/googleapis/go-genai/blob/v1.52.0/CHANGELOG.md)
- [Commits](googleapis/go-genai@v1.50.0...v1.52.0)

---
updated-dependencies:
- dependency-name: google.golang.org/genai
  dependency-version: 1.52.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump github.com/lib/pq from 1.11.2 to 1.12.0 (#60)

Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/lib/pq/releases)
- [Changelog](https://github.com/lib/pq/blob/master/CHANGELOG.md)
- [Commits](lib/pq@v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump actions/setup-go from 6.3.0 to 6.4.0 (#68)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](actions/setup-go@4b73464...4a36011)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @tailwindcss/vite from 4.2.1 to 4.2.2 in /web (#65)

Bumps [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.2/packages/@tailwindcss-vite)

---
updated-dependencies:
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump yaml from 2.8.2 to 2.8.3 in /web (#67)

Bumps [yaml](https://github.com/eemeli/yaml) from 2.8.2 to 2.8.3.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](eemeli/yaml@v2.8.2...v2.8.3)

---
updated-dependencies:
- dependency-name: yaml
  dependency-version: 2.8.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Sam Carson <samuel.carson@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump vue-router from 5.0.3 to 5.0.4 in /web (#63)

Bumps [vue-router](https://github.com/vuejs/router) from 5.0.3 to 5.0.4.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](vuejs/router@v5.0.3...v5.0.4)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-version: 5.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump eslint from 10.0.3 to 10.1.0 in /web (#64)

Bumps [eslint](https://github.com/eslint/eslint) from 10.0.3 to 10.1.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.0.3...v10.1.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @vitest/eslint-plugin in /web (#62)

Bumps [@vitest/eslint-plugin](https://github.com/vitest-dev/eslint-plugin-vitest) from 1.6.12 to 1.6.13.
- [Release notes](https://github.com/vitest-dev/eslint-plugin-vitest/releases)
- [Commits](vitest-dev/eslint-plugin-vitest@v1.6.12...v1.6.13)

---
updated-dependencies:
- dependency-name: "@vitest/eslint-plugin"
  dependency-version: 1.6.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump github.com/jackc/pgx/v5 from 5.8.0 to 5.9.1 (#59)

Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.8.0 to 5.9.1.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](jackc/pgx@v5.8.0...v5.9.1)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump vite from 7.3.1 to 8.0.1 in /web (#61)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 8.0.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@8.0.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): remove vite-plugin-vue-devtools (no Vite 8 support)

The plugin's transitive dep vite-plugin-inspect doesn't support Vite 8
yet (vuejs/devtools#1071). It was installed but never registered in
vite.config.ts. The Vue DevTools browser extension provides equivalent
functionality. Re-add when upstream updates the peer dep range.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(crypto): bind GCM ciphertext to entity context via AAD (#83)

Add Additional Authenticated Data (AAD) to AES-256-GCM encrypt/decrypt,
preventing ciphertext relocation between database rows. SSO client
secrets are bound to org_id, MFA TOTP secrets to user_id, and the
doctor encryption sentinel to a fixed label.

Also adds dev/specs/sso-secret-storage.md documenting the full
encryption architecture for external sharing.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(deps): bump reka-ui from 2.9.2 to 2.9.3 in /web (#80)

Bumps [reka-ui](https://github.com/unovue/reka-ui) from 2.9.2 to 2.9.3.
- [Release notes](https://github.com/unovue/reka-ui/releases)
- [Commits](unovue/reka-ui@v2.9.2...v2.9.3)

---
updated-dependencies:
- dependency-name: reka-ui
  dependency-version: 2.9.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump vue from 3.5.30 to 3.5.32 in /web (#79)

Bumps [vue](https://github.com/vuejs/core) from 3.5.30 to 3.5.32.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](vuejs/core@v3.5.30...v3.5.32)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump github.com/lib/pq from 1.12.0 to 1.12.3 (#76)

Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.12.0 to 1.12.3.
- [Release notes](https://github.com/lib/pq/releases)
- [Changelog](https://github.com/lib/pq/blob/master/CHANGELOG.md)
- [Commits](lib/pq@v1.12.0...v1.12.3)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-version: 1.12.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump google.golang.org/genai from 1.52.0 to 1.52.1 (#75)

Bumps [google.golang.org/genai](https://github.com/googleapis/go-genai) from 1.52.0 to 1.52.1.
- [Release notes](https://github.com/googleapis/go-genai/releases)
- [Changelog](https://github.com/googleapis/go-genai/blob/main/CHANGELOG.md)
- [Commits](googleapis/go-genai@v1.52.0...v1.52.1)

---
updated-dependencies:
- dependency-name: google.golang.org/genai
  dependency-version: 1.52.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump oxlint and eslint-plugin-oxlint to ~1.58.0

eslint-plugin-oxlint 1.58.0 adds a peerDependency on oxlint ~1.58.0,
so both must be bumped together. Lint and tests verified.

Closes #78.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(deps-dev): bump @types/node from 24.12.0 to 25.5.2 in /web (#77)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.12.0 to 25.5.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.5.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): upgrade TypeScript 5.9 to 6.0

- Bump typescript from ~5.9.3 to ~6.0.2
- Bump @vue/tsconfig from ^0.9.0 to ^0.9.1 (adds TS6 peer support)
- Remove deprecated baseUrl from tsconfig.json and tsconfig.app.json
  (TS6 resolves paths relative to the tsconfig file by default)

Type-check, lint, and all 419 unit tests pass.

Closes #81.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert(deps): revert TypeScript 6 upgrade, keep baseUrl removal

openapi-typescript@7.13.0 requires peer typescript ^5.x with no TS6
support yet. Revert typescript and @vue/tsconfig version bumps.

Keep the baseUrl removal from tsconfig.json and tsconfig.app.json —
paths resolve relative to the tsconfig file without it on TS 5.9 too,
and this prepares for TS6 when the ecosystem catches up.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(lint): disable require-mock-type-parameters rule

New in oxlint 1.58.0 under the correctness category. Requires type
parameters on all vi.fn() calls — a style preference, not a
correctness issue. Disable rather than modifying 147 test call sites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "chore(lint): disable require-mock-type-parameters rule"

This reverts commit 1763dbb.

* fix(lint): add type parameters to all vi.fn() mock calls

oxlint 1.58.0 enables require-mock-type-parameters under correctness.
Untyped vi.fn() returns Mock<(...args: any[]) => any>, silently
discarding type safety on mock arguments and return values.

Add explicit type parameters to all 147 vi.fn() call sites across
28 test files. All tests pass (419/419).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(lint): use precise mock types where generic unknown breaks type-check

Three files needed more specific type parameters than the generic
(...args: unknown[]) => unknown pattern:

- CreateWatchlistDialog: cast mock.calls access for body property access
- client.test.ts: type fetchMock as typeof fetch (assigned to globalThis.fetch)
- CveDetailView: type mockGET first arg as string (used in mockImplementation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(lint): match proxy signature to typed mockGET in CveDetailView test

The spread proxy (...args: unknown[]) can't spread into a
(string, ...unknown[]) parameter. Match the proxy's signature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant