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

@rwandatrust/trust-verifier

v2.0.0

Published

Standalone offline verification of RwandaTrust trust artifacts: signed attestations and ISO 27560 consent receipts (JAdES-B-B)

Downloads

74

Readme

@rwandatrust/trust-verifier

Standalone offline verification of RwandaTrust trust artifacts — signed attestations and ISO/IEC TS 27560:2023 consent receipts (JAdES-B-B compact JWS).

Lightweight, zero external dependencies beyond canonicalize. Works in any Node.js 18+ environment.

What this package verifies

| Artifact | Entry point | What it proves | |---|---|---| | Signed attestation | verifyAttestation() | A verification event happened and was signed by RwandaTrust | | ISO 27560 consent receipt | verifyConsentReceipt() | A citizen granted consent for a specific purpose |

Both paths share the same JAdES-B-B + JWKS verification core, so alg=none rejection, algorithm-confusion defense (kty/crv cross-check against the header alg), and trusted-issuer enforcement apply uniformly.

Install

npm install @rwandatrust/trust-verifier

Quick Start

import { verifyAttestation } from '@rwandatrust/trust-verifier';

const result = await verifyAttestation({
  jws: attestation.jws,
  jwksUrl: 'https://rwandatrust.com/.well-known/rwandatrust-signing-keys.json',
});

if (result.valid) {
  console.log('Verified:', result.attestation.type, result.attestation.organizationId);
} else {
  console.error('Verification failed:', result.reason);
}

API

verifyAttestation(input): Promise<VerifyResult>

Main entry point. Parses a JWS, looks up the signing key in a JWKS document, verifies the cryptographic signature, and validates attestation claims.

interface VerifyInput {
  jws: string;                        // Compact JWS: header.payload.signature
  jwks?: JwksDocument;                // Pre-fetched JWKS (offline mode)
  jwksUrl?: string;                   // URL to fetch JWKS (online mode)
  expectedSnapshotDigest?: string;    // Pin JWKS to a specific digest
  trustedIssuers?: string[];          // Issuer allowlist (e.g. ['https://rwandatrust.com'])
  skipClaimValidation?: boolean;      // Crypto-only mode (default: false)
  allowMissingKeyStatus?: boolean;    // Accept keys without status field (default: false)
}

interface VerifyResult {
  valid: boolean;
  reason?: string;                    // Human-readable failure reason
  attestation?: ParsedAttestation;    // Parsed payload (if valid)
  jwksDigest?: string;                // Computed JWKS snapshot digest
}

Verification steps:

  1. Parse compact JWS into header / payload / signature
  2. Extract kid from header, look up public key in JWKS
  3. Reject keys that are not ACTIVE or ROTATING
  4. Optionally verify JWKS snapshot digest
  5. Optionally verify JWKS issuer against allowlist
  6. Convert JOSE signature to DER (for ECDSA) and verify with Node.js crypto
  7. Validate required claims: type, organizationId, subjectIdHash, issuedAt
  8. Validate type-specific claims: verificationRequestId or consentRecordId
  9. Reject future iat (5-minute clock skew tolerance)

pinJwks(jwks): string

Compute JWKS snapshot digest for key set pinning.

import { pinJwks, fetchJwks } from '@rwandatrust/trust-verifier';

const jwks = await fetchJwks('https://rwandatrust.com/.well-known/rwandatrust-signing-keys.json');
const digest = pinJwks(jwks);
// "sha256:a1b2c3..."

// Later, verify the JWKS hasn't changed:
const result = await verifyAttestation({
  jws: attestation.jws,
  jwks,
  expectedSnapshotDigest: digest,
});

toProvenanceGraph(attestation): ProvJsonDocument

Convert a verified attestation into a W3C PROV-JSON document for Data Hub integration.

import { verifyAttestation, toProvenanceGraph } from '@rwandatrust/trust-verifier';

const result = await verifyAttestation({ jws, jwks });
if (result.valid) {
  const prov = toProvenanceGraph(result.attestation);
  // prov.entity, prov.activity, prov.agent, prov.wasGeneratedBy, ...
}

PROV-DM mapping:

  • prov:Entity = the signed attestation
  • prov:Activity = the verification or consent ceremony
  • prov:Agent = the signing key (RwandaTrust platform)

parseCompactJws(jws): ParsedJws

Parse a JWS without verifying the signature.

import { parseCompactJws } from '@rwandatrust/trust-verifier';

const { header, payload, signatureB64 } = parseCompactJws(jws);
console.log(header.kid, header.alg);
console.log(payload.type, payload.organizationId);

fetchJwks(url): Promise<JwksDocument>

Fetch and validate a JWKS document. Handles both raw JWKS and RwandaTrust API envelope format.

computeKeyThumbprint(jwk): string

SHA-256 hex digest of JCS-canonicalized required JWK members. Note: produces hex output (not base64url like vanilla RFC 7638) to match the RwandaTrust JWKS snapshot scheme.

findActiveKey(jwks, kid, options?): JwkKey | null

Look up a key by kid, accepting only ACTIVE or ROTATING status. Keys without a status field are rejected by default; pass { allowMissingStatus: true } for generic JWKS interop.

Offline Verification

For environments without network access, pre-fetch the JWKS and pin it:

// Setup (online, once)
const jwks = await fetchJwks('https://rwandatrust.com/.well-known/rwandatrust-signing-keys.json');
const pinnedDigest = pinJwks(jwks);
// Store jwks and pinnedDigest

// Verification (offline, anytime)
const result = await verifyAttestation({
  jws: attestation.jws,
  jwks: storedJwks,
  expectedSnapshotDigest: pinnedDigest,
  trustedIssuers: ['https://rwandatrust.com'],
});

Supported Attestation Types

| Type | Required Claims | |------|----------------| | VERIFICATION_CERTIFICATE | organizationId, subjectIdHash, verificationRequestId, issuedAt | | CONSENT_CERTIFICATE | organizationId, subjectIdHash, consentRecordId, issuedAt |

Supported Algorithms

| Algorithm | Curve/Key | Status | |-----------|-----------|--------| | ES256 | ECDSA P-256 + SHA-256 | Primary | | ES384 | ECDSA P-384 + SHA-384 | Supported | | ES512 | ECDSA P-521 + SHA-512 | Supported | | RS256 | RSA + SHA-256 | Fallback |

Key Lifecycle

The verifier respects RwandaTrust key lifecycle states:

| Status | Accepted? | |--------|-----------| | ACTIVE | Yes | | ROTATING | Yes | | RETIRED | No | | COMPROMISED | No |

Dependencies

  • canonicalize -- RFC 8785 JSON Canonicalization Scheme
  • Node.js built-in crypto module

License

MIT