Repository navigation
fix(auth): select secp256k1 signing under self-certifying DIDs - #9
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 wasdid:willow:eth:<addr>/did:willow:<alg>:<pubkey>).detectAlgorithm()(src/auth/index.ts) is used bysetIdentity()→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-keytype(e.g.EcdsaSecp256k1VerificationKey2019→secp256k1,Ed25519→Ed25519) to the algorithm. This is the authoritative signal for self-certifying ids; the SDK already stamps these types increateDidFromPublicKey/createDidFromWallet.WillowClient.init()derives the algorithm from the resolved DID document's key type. It already fetches the document when nopublicKeyIdis passed, so this is zero extra work on the common path.setIdentity(did, privateKey, publicKeyId, algorithm?)andinit(privateKey?, publicKeyId?, algorithm?)gain an optional trailingalgorithmargument, defaulting to the legacydetectAlgorithmfallback.Public API stays backward compatible (new optional trailing params only).
algorithmFromKeyTypeis exported from the package root.Tests
tests/auth.test.tsadds:ethers.verifyMessagerecovers to the wallet address (proving secp256k1 was used), and asserts the old heuristic would have picked Ed25519 for the same raw-hex key;verifyEd25519);algorithmFromKeyTypemapping unit tests.Full suite: 480 passed / 11 skipped / 0 failed.
tsc --noEmit,npm run build, and eslint all clean.🤖 Generated with Claude Code