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

http-fields-signatures

v0.1.0

Published

RFC 9421 HTTP Message Signatures — sign and verify HTTP requests and responses, built on http-fields (RFC 8941/9651 Structured Field Values) and WebCrypto

Readme

http-fields-signatures

RFC 9421 HTTP Message Signatures for JavaScript — sign and verify HTTP requests and responses.

Built on http-fields (RFC 8941/9651 Structured Field Values) for all header parsing/serialization, and on WebCrypto for the cryptography. Runtime-agnostic: Node.js ≥ 20, browsers, and edge runtimes with globalThis.crypto.subtle.

Validated against the complete RFC 9421 Appendix B test suite: all six signature bases are reproduced byte-exactly, the RFC's RSA-PSS and ECDSA example signatures verify, and HMAC/Ed25519 signing reproduces the RFC's exact signature bytes.

Installation

npm install http-fields-signatures

Quick Start

Signing a request

import { signMessage } from "http-fields-signatures";

const request = {
  method: "POST",
  url: "https://example.com/foo?param=Value&Pet=dog",
  headers: {
    Date: "Tue, 20 Apr 2021 02:07:55 GMT",
    "Content-Type": "application/json",
  },
};

const { signatureInput, signature } = await signMessage(request, {
  alg: "ed25519",
  key: privateJwk, // JWK, PEM (PKCS#8), or CryptoKey
  keyid: "my-key",
  components: ["@method", "@path", "@authority", "content-type"],
});

request.headers["Signature-Input"] = signatureInput;
request.headers["Signature"] = signature;
// Signature-Input: sig1=("@method" "@path" "@authority" "content-type");created=...;keyid="my-key"
// Signature: sig1=:...base64...:

Verifying

import { verifyMessage } from "http-fields-signatures";

const results = await verifyMessage(request, {
  getKey: ({ keyid }) => ({ alg: "ed25519", key: publicKeys[keyid] }),
});
// [{ label: "sig1", verified: true, params: {...}, components: [...] }]

Structural problems (unparseable headers) throw; cryptographic mismatches and policy failures (expired signatures, unknown keys) report as {verified: false, reason} so one bad signature doesn't mask others.

Signing a response (with request binding)

const response = { status: 200, headers: {...}, request };

await signMessage(response, {
  alg: "ecdsa-p256-sha256",
  key: privateJwk,
  keyid: "my-key",
  components: [
    "@status",
    "content-type",
    { id: "@method", params: { req: true } }, // bind to the request's method
  ],
});

Supported algorithms (RFC 9421 §3.3)

| Name | WebCrypto | | --- | --- | | rsa-pss-sha512 | RSA-PSS, SHA-512, salt 64 | | rsa-v1_5-sha256 | RSASSA-PKCS1-v1_5, SHA-256 | | ecdsa-p256-sha256 | ECDSA P-256, SHA-256 | | ecdsa-p384-sha384 | ECDSA P-384, SHA-384 | | ed25519 | Ed25519 | | hmac-sha256 | HMAC, SHA-256 |

Keys are accepted as CryptoKey, JWK objects, PEM strings (PKCS#8 or SEC1 private / SPKI public), or raw Uint8Array bytes (HMAC).

WebCrypto itself cannot import SEC1 EC PRIVATE KEY PEMs (the format RFC 9421 uses for its P-256 example key), so importKey transparently re-wraps them: PKCS#8 is just an ASN.1 envelope around the SEC1 payload, so the conversion is pure DER re-packaging with no cryptography involved. The transcoder is also exported directly as sec1ToPkcs8(sec1Bytes, namedCurve?).

Encrypted private keys are not supported. Password-protected PEMs — PKCS#8 ENCRYPTED PRIVATE KEY (PBES2) or legacy Proc-Type: 4,ENCRYPTED blocks — require key-derivation cryptography, not byte re-packaging, and importKey rejects them with a clear error. Decrypt once outside the library instead:

openssl pkcs8 -topk8 -nocrypt -in encrypted-key.pem -out key.pem

Covered components

Components are strings or {id, params} objects:

  • Derived (from the message itself): @method, @target-uri, @authority, @scheme, @request-target, @path, @query, @query-param ({id: "@query-param", params: {name: "Pet"}}), @status
  • Fields: any header name (lowercased), with optional parameters:
    • key: select one dictionary member ({id: "example-dict", params: {key: "b"}})
    • sf: strict structured-field re-serialization (requires the field's type via options.fieldTypes)
    • bs: wrap raw value(s) as byte sequences
    • req: take the component from the related request when signing a response

Lower-level API

import {
  createSignatureBase, // build the §2.5 signature base string
  parseSignatureHeaders, // parse Signature-Input / Signature dictionaries
  signBase, verifyBase, // raw crypto over a base string
  importKey, ALGORITHMS,
} from "http-fields-signatures";

const { base } = createSignatureBase(request, ["@method", "content-type"], {
  created: 1618884473,
  keyid: "my-key",
});

Messages

Messages are plain objects, so any client/server framework can adapt to them:

// Request:  { method, url, headers }
// Response: { status, headers, request? }

headers may be a plain object (values string or string[]), an array of [name, value] entries, or a Fetch-style Headers instance.

Testing

npm test

The suite includes the full RFC 9421 Appendix B vectors (B.2.1–B.2.6) plus round-trip, tampering, and expiry tests.

Alternatives

See COMPARISON.md for a side-by-side comparison with http-message-signatures, including how to do the same signing/verifying tasks in each.

License

MIT