feat(secrets): owner-discriminated AAD (SecretOwner) for group secrets

The crypto foundation for group-owned secrets (the §5.3 'single hardest
correctness detail'), done first and proven before the storage/resolution
layer.

- SecretOwner{App(AppId)|Group(GroupId)}; secret_aad/seal/open take the
  owner. The app AAD is byte-identical to the pre-Phase-3
  'secret:{app_id}:{name}', so every existing v1 row decrypts unchanged;
  group secrets use a disjoint 'secret:group:{group_id}:{name}' namespace
  (the 'group:' infix separates app/group AAD even for equal UUIDs).
- All callers (SDK get/set, secrets_api, apply email-secret read) wrapped
  in SecretOwner::App — behavior identical for app secrets.
- New audit test aad_distinguishes_app_and_group_owner proves the two
  namespaces don't collide (both directions, even reusing the UUID); the
  existing cross-app/cross-name swap audit tests stay green.

16 secrets_service tests pass; clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-24 21:17:18 +02:00
parent 9ee85993d8
commit 6bd0f4699d
4 changed files with 88 additions and 17 deletions

View File

@@ -123,7 +123,7 @@ async fn set_secret(
// (app_id, name). Same path as the SDK secrets::set.
let (ciphertext, nonce, version) = seal(
&s.master_key,
app_id,
crate::secrets_service::SecretOwner::App(app_id),
&input.name,
&input.value,
s.max_value_bytes,