npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@le-space/iso-webauthn-varsig

v0.3.0

Published

WebAuthn varsig encoding/decoding helpers.

Readme

iso-webauthn-varsig

WebAuthn varsig encoding/decoding helpers for Ed25519 and P-256.

Implements the non-recursive WebAuthn varsig layout per Gozala's suggestion on the ChainAgnostic varsig spec.

Wire Format

All multi-byte integers are unsigned varint-encoded.

webauthn-varsig = webauthn-varsig-header
                 client-data-length client-data-json
                 authenticator-data-length authenticator-data
                 signature-hash-algorithm encoding-info signature-bytes

webauthn-varsig-header = varsig-prefix varsig-version inner-algorithm curve webauthn-marker
varsig-prefix = %x34
varsig-version = %x01
inner-algorithm = %xED / %xEC       ; EdDSA / ECDSA
curve = %xED / %x1200               ; Ed25519 / P-256 (multicodec code,
                                    ; varint-encoded: ED -> ED 01)
webauthn-marker = %x300001           ; private-use multicodec

client-data-length = 1*unsigned-varint
client-data-json = *OCTET
authenticator-data-length = 1*unsigned-varint
authenticator-data = *OCTET
signature-hash-algorithm = 1*unsigned-varint  ; multihash code (default 0x12 = SHA-256)
encoding-info = 1*unsigned-varint             ; payload encoding (default 0x5f = raw)
signature-bytes = *OCTET                      ; raw WebAuthn signature

This layout avoids recursion (no nested varsig-body) and aligns with the dialog-db Go/Rust reference implementation.

Usage

import {
  encodeWebAuthnVarsigV1,
  decodeWebAuthnVarsigV1,
  parseClientDataJSON,
  verifyWebAuthnVarsig,
} from 'iso-webauthn-varsig'

const assertion = {
  authenticatorData: new Uint8Array([/* ... */]),
  clientDataJSON: new Uint8Array([/* ... */]),
  signature: new Uint8Array([/* ... */]),
}

// Encode with defaults (SHA-256 hash, raw encoding)
const varsig = encodeWebAuthnVarsigV1(assertion, 'Ed25519')

// Or with explicit signature metadata
const varsig2 = encodeWebAuthnVarsigV1(assertion, 'P-256', {
  signatureHashAlgorithm: 0x12, // SHA-256
  encodingInfo: 0x5f,           // raw
})

const decoded = decodeWebAuthnVarsigV1(varsig)

const result = await verifyWebAuthnVarsig(decoded, {
  // These must come from *your* configuration and from the challenge you
  // issued — never from the assertion being verified. Reading them out of
  // decoded.clientDataJSON would compare each value against itself and
  // check nothing.
  expectedOrigin: 'https://example.com',
  expectedRpId: 'example.com',
  expectedChallenge: challengeYouIssued,
  // The credential's public key, from registration: 32 raw bytes for Ed25519,
  // or an uncompressed P-256 point (65 bytes, 0x04 || x || y).
  publicKey: storedCredentialPublicKey,
})

console.log(result.valid)

Verifying

verifyWebAuthnVarsig is the function you want: it checks the ceremony type, origin, challenge, rpIdHash and flags and verifies the signature.

verifyWebAuthnAssertion performs only the first half. It is exported for callers who manage key lookup themselves, but on its own a valid: true result says the assertion is well-formed and addressed to you — not that it is authentic. It accepts a fabricated signature.

P-256 signatures are accepted in either the ASN.1 DER form an authenticator emits or raw r||s; the conversion happens internally. WebCrypto only understands the raw form, so passing DER to subtle.verify yourself silently returns false for every genuine signature.

Notes

  • Ed25519 WebAuthn credentials are not supported on all platforms. If your authenticator does not return an Ed25519 key, you must fall back to P-256.
  • Breaking change in v0.2.0: The wire format changed from v0.1.0. Encoded bytes from the old format are not compatible with this version. See issue #1 for details.