feat(session)!: migrate DID resolver to dig-identity v0.4 BLS-G1 (resolve_bls_public_key, 48B)#62
Conversation
…2 #1171) WIP stub for the C2 resolver migration (epic #1169). Bumps the dig-identity git-dep tag to v0.4.0 (BLS-G1-only) and the versions; resolver migration follows. Refs #1171 Co-Authored-By: Claude <noreply@anthropic.com>
…olve_bls_public_key, 48B) Migrate the engine's production DID signing-key resolver (ChainDidSigningKeyResolver, #778/#1080) to dig-identity v0.4.0's BLS-only key model (epic #1169): - resolve_signing_key([u8;32]) -> resolve_bls_public_key([u8;48]) (slot 0x0010 is now a 48-byte BLS12-381 G1 identity key; the v1 Ed25519 slot + X25519 0x0011 are retired) - ResolveError::NoSigningKey -> NoIdentityKey (describe() + tests) - resolver-only tests: with_schema_v2, BLS_G1_PUBLIC_KEY, 48B key Authority semantics preserved (#778/#1080): chain-authenticated lineage-membership resolution, fail-closed, no key echo, engine holds no user key. Only the key TYPE changes (32B Ed25519 -> 48B BLS G1). The DidSigningKeyResolver trait + SigningPublicKey/Signature/verify are owned by dig-ipc-protocol; the 48B key can only be returned through that seam once C5b (#1173) widens SigningPublicKey to 48B G1 + BLS G2 verify and republishes. The end-to-end attach tests (still Ed25519) are marked TODO(#1173) accordingly. BLOCKED on C5b -> stays draft. Refs #1171, #1169, #1173, #778, #1080 Co-Authored-By: Claude <noreply@anthropic.com>
…ocol 0.2.0 Bumps dig-ipc-protocol 0.1 -> 0.2.0 (crates.io), whose SigningPublicKey widened to 48-byte BLS12-381 G1 and Signature to 96-byte G2 AugScheme. This unblocks the resolver's 48-byte return type at session.rs:85 (the one remaining production error under dig-identity v0.4) and lets the two end-to-end attach tests exercise real BLS G2 signing. - Migrate honest_attach + attach_is_refused (and key_from/profile_with_key) from ring Ed25519 to chia-bls G2 signing; drop the TODO(#1173) block. - Add chia-bls dev-dep; profiles publish the slot-0x0010 BLS G1 key via schema v2 (standard::BLS_G1_PUBLIC_KEY). - Pin dig-ipc-protocol 0.2.0 in the lock without disturbing the frozen bare-git peer-stack revs (dig-constants/dig-nat HEADs have drifted ahead of dig-gossip's <0.5/<0.4 bounds; a full re-resolve would break them). Authority semantics unchanged (#778/#1080 lineage-membership): only the key TYPE changed 32->48B; the engine still resolves the chain-authorized pubkey and holds no user key. Fail-closed + KeyMismatch paths intact. Refs #1171, #1169, #1173 Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d
left a comment
There was a problem hiding this comment.
loop-reviewer verdict: CHANGES-REQUIRED
The code migration itself is clean, correct, and fully gated (all CI green: Rustfmt, Clippy -D warnings, Test+coverage, CodeQL/Analyze, version-increment, commitlint, per-OS builds). Verified independently against a fresh clone of feat/dig-identity-v0.4-bls @ e5b8170:
What is correct (PASS on these):
- Resolver correctly swaps
dig_identity::resolve_signing_key->resolve_bls_public_keyreturning the 48-byte slot-0x0010 BLS G1 identity key;SigningPublicKey::new([u8;48])now type-checks against dig-ipc-protocol 0.2.0. - Error mapping complete:
NoSigningKey/no-signing-keyfully replaced byNoIdentityKey/no-identity-key;describe_covers_every_resolve_error_variantexercises every arm. - No stale 32B/Ed25519 session path remains reachable (grep clean): the only remaining
resolve_signing_keyis the dig-ipc-protocol TRAIT method (out of scope, owned there); the only remaining Ed25519 is peer.rs mTLS transport identity (unrelated to session auth). - Tests exercise REAL BLS keys, verify-success AND refusal:
honest_attach_binds_the_session_to_the_chain_resolved_keysigns with a realchia_bls::SecretKeyvia the G2 AugScheme and requires a genuine verify (open_sessions==1);attach_is_refused_when_the_did_advertises_a_key_it_did_not_publishproves the resolved!=presented KeyMismatch fail-closed (open_sessions==0). KAT lengths updated to 48/96. - Custody boundary preserved: engine resolves PUBLIC key only, requires resolved==presented, fails closed on every ambiguity. Adversarial lenses (custody / key-substitution / scheme-confusion / replay-domain-separation / length-confusion) all clear.
- dig-constants check: (1) nothing shared-canonical is defined LOCALLY here — key lengths/domain tags/
SIGNING_KEY_LEN/SIGNATURE_LENcome from the correct SSOT (dig-ipc-protocol, the byte-identical session contract crate), not hardcoded; (2) nothing here hardcodes a value dig-constants already publishes. Clears. - Version bump 0.11->0.12 / 0.45->0.46 is a correct pre-1.0 minor-for-breaking bump.
BLOCKING finding (see inline): the repo's own normative SPEC.md §7.15 was NOT updated in this unit of work and now contradicts the shipped code for a security-critical auth path. §4.2 makes a SPEC-describing-old-behavior change INCOMPLETE. Fix the stale 32B/Ed25519 language, then this is a clean PASS.
MichaelTaylor3d
left a comment
There was a problem hiding this comment.
CHANGES-REQUIRED (loop-reviewer, fresh independent context — custody-adjacent key-type migration).
The code migration itself is CORRECT and I verified the security-critical properties are preserved:
- Key-type migration correct.
resolve_signing_key([u8;32])→resolve_bls_public_key([u8;48])atsession.rs:84; the seam returnsSigningPublicKey::new(key)over the 48B G1 pubkey;SIGNING_KEY_LEN 48/SIGNATURE_LEN 96KATs asserted;NoSigningKey→NoIdentityKeyindescribe. dig-ipc-protocol 0.2.0 (widened SigningPublicKey 48B / Signature 96B G2) consumed correctly; Cargo.lock shows it published from registry. - Authority PRESERVED (#778/#1080). The resolver body only swaps the delegated fn; the lineage-membership walk, fail-closed-on-every-error mapping, no-key-echo, and KeyMismatch-on-substitution logic are byte-unchanged. Every authority test survives — invalid DID, melted singleton, chain-source error, no-key, honest_attach,
attach_is_refused_when_the_did_advertises_a_key_it_did_not_publish(KeyMismatch), describe-covers-every-variant. - Attach tests migrated genuinely.
key_fromnow derives achia_bls::SecretKey::from_seed(hashed seed, no literal); profiles publish slot-0x0010 viawith_schema_v2+BLS_G1_PUBLIC_KEY; challenges signed withchia_bls::sign(96B G2), pub isto_bytes()(48B). Real BLS attach/verify exercised end-to-end. - Deps / version. dig-ipc-protocol 0.2, dig-identity v0.4.0 tag, chia-bls 0.26 dev-dep; ed25519-dalek/curve25519 removed from the lock. 0.45.0→0.46.0 / 0.11.0→0.12.0 feat! breaking bump correct. dig-node is modules/apps (nightlies) → git dep on dig-identity OK.
- Gates green (clippy -D, fmt, tests incl. 2 BLS attach, coverage). Beautiful-code: doc-comments updated to the BLS model, reads cleanly.
The one blocker is the already-open coherence thread at session.rs:84 (§4.2): the repo's own normative SPEC.md still describes the OLD auth path and now contradicts the shipped code —
SPEC.md:1340"delegating todig_identity::resolve_signing_key" → nowresolve_bls_public_keySPEC.md:1364"the engine validates the DID + 32-byte hex key" → now 48-byte BLS G1SPEC.md:1369"Ed25519-verifies the challenge signature" → now BLS12-381 G2 AugScheme
I re-verified against branch head feat/dig-identity-v0.4-bls: these lines are still stale. An independent reimplementer reading the current spec would build an incompatible 32B-Ed25519 verifier for the session-attach custody boundary — exactly the §4.2 half-migration this gate forbids. Update §7.15 (+ confirm top-level vs crates/dig-node-core/SPEC.md agree) in this same unit of work, then re-request. No authority weakening beyond the type change was found — no adversarial escalation needed; this is purely the spec-coherence gap.
…2 AugScheme verify) Co-Authored-By: Claude <noreply@anthropic.com>
|
SPEC.md §7.15 is now aligned with BLS-G1 auth migration:
No other stale Ed25519 or resolve_signing_key references found in session-auth context (dig-node-core/SPEC.md clean). Spec now describes the actual shipped BLS-only key model per dig-ipc-protocol 0.2.0. |
C2 of epic #1169 (issue #1171)
Migrates dig-node's engine DID signing-key resolver (
ChainDidSigningKeyResolver, #778/#1080) to dig-identity v0.4.0 (BLS-G1-only key model):resolve_signing_key -> [u8;32]becomesresolve_bls_public_key -> [u8;48], slot 0x0010 is now a 48-byte BLS12-381 G1 key (Ed25519 slot + X25519 0x0011 retired), andResolveError::NoSigningKeybecomesNoIdentityKey.Authority semantics preserved (#778/#1080): chain-authenticated lineage-membership resolution, fail-closed on every ambiguity/staleness/mismatch, no key echo, engine holds no user key. Only the KEY TYPE changes (32B Ed25519 -> 48B BLS G1); the authority logic is unchanged.
BLOCKED on C5b (#1173) — release-first ordering
The resolver implements
dig_ipc_protocol::DidSigningKeyResolver, whoseresolve_signing_key -> SigningPublicKey([u8;32])type is owned by dig-ipc-protocol (crates.io, latest v0.1.2, still Ed25519 32B). A 48-byte G1 key cannot be returned through that seam until dig-ipc-protocol widensSigningPublicKeyto 48B G1 (+Signatureto 96B G2, verify to BLS) and republishes — that is C5b (#1173, in-progress). So the decider'sC2 ∥ C5bmust be C5b -> C2. This PR stays DRAFT until the new dig-ipc-protocol version ships; then it is a version-bump-and-merge.Version: workspace 0.45.0 -> 0.46.0, dig-node-core 0.11.0 -> 0.12.0.
Refs #1171, #1169, #1173, #778, #1080
Co-Authored-By: Claude noreply@anthropic.com