Skip to content

fix(auth): select secp256k1 signing under self-certifying DIDs - #9

Merged
pauldelucia merged 1 commit into
masterfrom
fix/secp256k1-algorithm-detection
Jul 2, 2026
Merged

pauldelucia merged 1 commit into
masterfrom
fix/secp256k1-algorithm-detection

Conversation

@pauldelucia

Copy link
Copy Markdown
Contributor

Problem

Willow DIDs are now self-certifying: did:willow:z<base58btc(SHA3-256(multicodec||pubkey))>. They no longer embed the key algorithm in the id string (the old form was did:willow:eth:<addr> / did:willow:<alg>:<pubkey>).

detectAlgorithm() (src/auth/index.ts) is used by setIdentity() → signRequest() to pick the per-request signing algorithm. Its primary signal parses :eth: / :eip155: out of the DID string — now dead code for self-certifying ids. The only remaining signal is a private-key heuristic that recognizes secp256k1 only when the key is ethers' 0x-prefixed 66-char form.

Result: a secp256k1 (Ethereum/wallet) identity whose key is supplied as raw hex (no 0x, 64 chars — a valid and common representation) silently falls through to Ed25519. Per-request auth then signs with the wrong algorithm and the request is rejected. Ed25519-first flows are unaffected (they hit the default), so only secp256k1 users are broken.

Fix

Without reintroducing the algorithm into the DID string:

  • algorithmFromKeyType(type) — maps a DID-document verificationMethod / public-key type (e.g. EcdsaSecp256k1VerificationKey2019 → secp256k1, Ed25519 → Ed25519) to the algorithm. This is the authoritative signal for self-certifying ids; the SDK already stamps these types in createDidFromPublicKey / createDidFromWallet.
  • WillowClient.init() derives the algorithm from the resolved DID document's key type. It already fetches the document when no publicKeyId is passed, so this is zero extra work on the common path.
  • setIdentity(did, privateKey, publicKeyId, algorithm?) and init(privateKey?, publicKeyId?, algorithm?) gain an optional trailing algorithm argument, defaulting to the legacy detectAlgorithm fallback.

Public API stays backward compatible (new optional trailing params only). algorithmFromKeyType is exported from the package root.

Tests

tests/auth.test.ts adds:

  • a secp256k1 identity under a self-certifying DID produces a signature that ethers.verifyMessage recovers to the wallet address (proving secp256k1 was used), and asserts the old heuristic would have picked Ed25519 for the same raw-hex key;
  • Ed25519 still defaults correctly (verifiable via verifyEd25519);
  • an explicit algorithm overrides the private-key heuristic;
  • algorithmFromKeyType mapping unit tests.

Full suite: 480 passed / 11 skipped / 0 failed. tsc --noEmit, npm run build, and eslint all clean.

🤖 Generated with Claude Code

Self-certifying Willow DIDs (`did:willow:z<base58btc(SHA3-256(multicodec||pubkey))>`)
no longer encode the key algorithm in the id string. `detectAlgorithm`'s primary
signal — parsing `:eth:` / `:eip155:` out of the DID — is therefore dead for these
ids, leaving only a fragile private-key heuristic that recognizes secp256k1 solely
when the key is ethers' `0x`-prefixed 66-char form. A raw-hex secp256k1 key silently
falls through to Ed25519, so per-request auth signs with the wrong algorithm and a
secp256k1 (Ethereum/wallet) identity is rejected.

Fix without reintroducing the algorithm into the DID string:
- Add `algorithmFromKeyType()` mapping a DID-document verificationMethod / key
  `type` (e.g. `EcdsaSecp256k1VerificationKey2019`) to the signature algorithm —
  the authoritative signal for self-certifying ids.
- `WillowClient.init()` derives the algorithm from the resolved DID document's
  key type (it already fetches the document when no `publicKeyId` is supplied).
- Thread an optional `algorithm` argument through `setIdentity()` and `init()`,
  defaulting to the legacy `detectAlgorithm` fallback for backward compatibility.

Public API stays backward compatible (new optional trailing params only).

Adds tests proving a secp256k1 identity produces a wallet-recoverable secp256k1
signature and that Ed25519 still defaults correctly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pauldelucia
pauldelucia merged commit a50b58d into master Jul 2, 2026
3 checks passed
@pauldelucia
pauldelucia deleted the fix/secp256k1-algorithm-detection branch July 2, 2026 11:48
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.

2 participants