Skip to content

fix(attestation): verify attested signatures from did:web authorities#43

Open
frrist wants to merge 1 commit into
mainfrom
frrist/fix-attestation-didweb-authority
Open

fix(attestation): verify attested signatures from did:web authorities#43
frrist wants to merge 1 commit into
mainfrom
frrist/fix-attestation-didweb-authority

Conversation

@frrist

@frrist frrist commented Jun 26, 2026

Copy link
Copy Markdown
Member

Summary

Fixes attested-signature verification when the attesting authority is a
did:web service
(e.g. did:web:upload). This is the bug behind the
post–Attested-Signatures guppy login failure, where claiming the account
delegation failed with:

proof … does not have a valid signature from "did:mailto:…"
  Tried: did:mailto:…#did:web:upload: signature mismatch

Root cause

attestation/verifier.go's Verifier.Verify re-validated the inner attestation
invocation via:

validator.ValidateInvocation(v.ctx, inv)   // no options

ValidateInvocation takes its DID resolver from options, and with none it
defaults to key.Resolverdid:key only. So for a did:web authority it
can't resolve the authority's document, verifyTokenSignature fails, and the
attestation is rejected as a signature mismatch.

did:key authorities resolve fine under the default, so the existing
TestSigner (which uses a did:key authority) passed and the gap shipped. The
Verifier already holds the resolved authorityVerifier, but Verify never
used it.

Fix

Verify the attestation invocation's signature directly with the
already-resolved authorityVerifier, instead of re-resolving the authority:

- if validator.ValidateInvocation(v.ctx, inv) != nil {
+ if !token.VerifySignature(inv, v.authorityVerifier) {
      return false
  }

Testing

  • New TestSigner_WebAuthority exercises a did:web authority resolved via its
    generated DID document — the path the existing did:key test never covered.
    Without this fix it fails with the same did:mailto:…#did:web:…: signature mismatch error seen in the running stack; with it, it passes.
  • Full libforge test suite passes.

Confirmed end-to-end in smelt

Verified against the full forge stack (smelt), not just unit tests:

  • With every service on latest main, guppy login fails with the exact
    signature mismatch above.
  • The verifier in the login path is sprue (upload)/access/claim has
    audience did:web:upload, so sprue validates the attested account-delegation
    proof, and the InvalidSignature is returned in sprue's receipt (guppy only
    relays it). Rebuilding guppy alone against this fix left login failing
    identically; rebuilding sprue against it is what fixed it.
  • With sprue rebuilt against this branch and deployed to smelt, guppy login
    succeeds: Successfully logged in as test@example.com! (exit 0).

Rollout — all services must update after merge

This is a verification-side fix in a shared library, so merging it is not
enough on its own: every service that validates attested signatures must bump
its libforge dependency and rebuild to pick it up — confirmed for sprue /
upload
(login path), and likewise needed for delegator, indexer, and
piri for their attested flows. Services that only sign or serve their
own did:web document are unaffected, but bumping uniformly is simplest.

Notes

  • Separately, identity.DIDDocument() emits a malformed verification-method id
    (#%23key-0 instead of #key-0, from doc.Fragment("#key-0")). It's cosmetic
    for verification (matching is by VM Material, not id) and is not fixed
    here; worth a follow-up for spec-correct did:web documents.

🤖 Generated with Claude Code

The attestation Verifier re-validated the attestation invocation with
validator.ValidateInvocation(v.ctx, inv) and no options. ValidateInvocation
reads its DID resolver from options, defaulting to key.Resolver (did:key
only) — so it could not resolve a did:web authority (e.g. did:web:upload),
and attested-signature verification failed with "signature mismatch". did:key
authorities (and the existing TestSigner) resolved fine, which is why this
slipped through.

Verify the attestation invocation's signature with the authority verifier the
Verifier already holds (v.authorityVerifier) instead of re-resolving the
authority. Add TestSigner_WebAuthority covering a did:web authority resolved
via its DID document; without this change it fails with the same
"signature mismatch" seen in the wild (e.g. `guppy login`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@frrist frrist requested review from Peeja and alanshaw June 27, 2026 04:34
@frrist frrist self-assigned this Jun 27, 2026
@frrist frrist added the bug Something isn't working label Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant