feat(session): per-profile symmetric-key derivation (derive_symmetric_key) - #3
Conversation
…024) Placeholder to open a draft PR early (ecosystem §1.8 push-early). Co-Authored-By: Claude <noreply@anthropic.com>
…_key) Add additive `UnlockedIdentity::derive_symmetric_key(label)`: derive a per-profile symmetric key (DEK) from the unlocked identity scalar via HKDF-SHA256, returning the derived key by label while the raw scalar stays inside the facade (#908 preserved). The construction is byte-identical to dig-app's current profile DEK (dig-app-core keystore/secrets.rs `dek_password` + `to_sealed_bytes`): HKDF-SHA256(salt="dig-app:dek-salt:v1", ikm=0x02||identity_scalar, info=label) expanded to 32 bytes — so already-sealed profile blobs stay readable (§5.1). The IKM is the versioned 33-byte layout (0x02 prefix), NOT the bare scalar. Tests: a byte-identity test against an independent reconstruction of dig-app's construction, a frozen golden vector (fixed scalar + label -> exact DEK bytes), and label determinism/uniqueness. DEK + intermediates are Zeroizing. Bump 0.1.1 -> 0.2.0. Closes #1024. Co-Authored-By: Claude <noreply@anthropic.com>
v0.2.0 —
|
MichaelTaylor3d
left a comment
There was a problem hiding this comment.
Correctness gate (fresh context, security-critical: per-profile DEK / key custody).
The crypto is CORRECT and I independently verified byte-identity against dig-app: dig-app-core/src/keystore/secrets.rs derives its DEK as HKDF-SHA256(salt=dig-app:dek-salt:v1, IKM=SEALED_IDENTITY_VERSION(2) || identity_scalar, info=dig-app:profile-dek:v2, 32B), and its identity_scalar is derive_identity_sk(&master).to_bytes() (secrets.rs:103) — the SAME derivation dig-session's expose_secret() returns (proven by C-9 passing). hkdf 0.12 / sha2 0.10 match dig-app's workspace pins. Construction, salt, version prefix, label, output length all match. Tests are meaningful: C-9 (independent reference reconstruction), C-10 (frozen golden literal + scalar-drift assert), varies-by-label + determinism. Secret handling is clean (exact-capacity Zeroizing IKM, Zeroizing DEK, scalar never returned). Additive-only, minor bump 0.2.0, version gate green, CHANGELOG correctly deferred to git-cliff at release. 8/8 checks green, 0 threads.
ONE gating finding (dig-constants HARD RULE): the shared byte-identical DEK construction constants are defined LOCALLY in BOTH dig-app and dig-session instead of in the canonical dig-constants crate. DEK_SALT = b"dig-app:dek-salt:v1", the IKM version byte 2, and the profile-DEK label b"dig-app:profile-dek:v2" are cross-repo byte-identical values a second repo MUST match forever (§5.1 permanent at-rest contract). This PR spreads the local definition to a second repo — exactly the shared-value drift §4.1 forbids. The frozen golden vector catches drift within dig-session but does NOT prevent dig-app and dig-session diverging independently. These belong in dig-constants (00-foundation), published + consumed by version in both repos (analogous to the DIG_ASSET_ID rule). See inline comment.
Verdict: CHANGES-REQUIRED (single blocker). Everything else PASSES.
Replace the locally-duplicated DEK_SALT/IDENTITY_IKM_VERSION/ SYMMETRIC_KEY_LEN literals with the byte-identical consts published in dig-constants 0.7.0 (DEK_SALT, IDENTITY_IKM_VERSION, PROFILE_DEK_LABEL, SYMMETRIC_KEY_LEN), now that dig-app-core also consumes them from there (dig_ecosystem #4.1/#5.1/NC-5 single source of truth). Behaviour-preserving: the values are unchanged, so the derived DEK and the golden vector (C-9, C-10) are unaffected. Co-Authored-By: Claude <noreply@anthropic.com>
|
Resolved the correctness blocker on `src/unlocked.rs:21`: swapped the local DEK-constant literals for the published `dig-constants` 0.7.0 consts. New HEAD: `78050fe` (was `b52a843`) Swap:
`dig-constants = "0.7"` added as a direct crates.io dependency (no git dep). Behaviour-preserving — values are byte-identical, confirmed by re-running the existing golden-vector tests unchanged:
SPEC.md §3.2 DEK section updated to name dig-constants as the source of truth for these constants (satisfies the "dig-session consumes these consts" forward-looking claim the dig-constants reviewer flagged). Version stays 0.2.0 (behaviour-preserving, additive dep only, no API change). |
MichaelTaylor3d
left a comment
There was a problem hiding this comment.
PASS (correctness re-gate, swap-only, HEAD 78050fe).
Re-gated ONLY the local-literal -> dig-constants swap; crypto already cleared on b52a843.
- Behaviour-preserving:
DEK_SALT,IDENTITY_IKM_VERSION,SYMMETRIC_KEY_LENnow imported fromdig_constants; derivation body (HKDF-SHA256, IKM = 0x02 || scalar, salt, info=label, 32-byte expand) is byte-identical to the prior HEAD. No logic change. - Golden vector intact: tests/facade.rs is UNCHANGED across b52a843..78050fe (empty test diff). GOLDEN_DEK/GOLDEN_SCALAR literals frozen; C-9 (byte-identical) + C-10 (golden vector) pass in CI Test Suite -> objective proof the DEK is unchanged.
- Dep hygiene:
dig-constants = "0.7"direct crates.io dep (no git dep);cargo tree -i dig-constantsshows a single source (v0.7.0). Version 0.2.0 retained (behaviour-preserving; correct). - CI: all required checks green on 78050fe (Test Suite, Coverage >=80%, version-increment, commitlint, Analyze/CodeQL).
- SPEC.md correctly cites dig-constants as the DEK-constant source of truth.
Prior blocker (src/unlocked.rs:21, duplicated local literals) is addressed. Resolving that thread. Orchestrator owns merge.
Adds an ADDITIVE
UnlockedIdentity::derive_symmetric_key(label)deriving a per-profile DEK, byte-identical to dig-app's current HKDF-from-identity derivation (keystore/secrets.rs). Release-first for dig-app #1024 Phase 2. v0.1.1 -> v0.2.0.Custody-crypto: byte-exactness is the whole point. Blast radius + golden vector to follow in a comment.
Refs DIG-Network/dig_ecosystem#1024
🤖 Draft — WIP