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

@amsemnat/verifier

v0.1.2

Published

Verifier for Romanian eID artifacts produced by the am-semnat SDKs — eMRTD passive auth and PAdES B-B signer verification. Runs in Node 20+, modern browsers, and edge runtimes.

Readme

@amsemnat/verifier

Verifier for Romanian eID artifacts produced by the am-semnat SDKs. Pure JavaScript, runs unchanged in Node 20+, modern browsers, Cloudflare Workers, Deno, and Bun.

Two operations:

  • verifyPassive — eMRTD passive authentication. Takes the raw EF.SOD bytes plus the data groups read from the chip and verifies the SOD CMS signature, the DSC chain to a caller-supplied CSCA Romania anchor, and the per-DG hashes. Use this server-side when you receive RomanianIdentity.rawSod / RomanianIdentity.rawDg* from one of the mobile SDKs.

  • verifyPadesSignatures — verifies every PAdES B-B signature in an assembled signed PDF. Returns one result per signature in document order, with coversWholeDocument for incremental-update detection.

This package ships zero MAI trust material. Consumers fetch the current CSCA Romania (DGP) and RO CEI MAI Root/Sub-CA (DGEP) certs themselves from the official MAI publication points.

  • DGP — CSCA Romania, published at https://pasapoarte.mai.gov.ro/csca.html. Self-signed ICAO CSCA that issues the Document Signer embedded in the eMRTD SOD. This is the trust anchor for verifyPassive(...).
  • DGEP — RO CEI MAI Root-CA / Sub-CA, published at https://hub.mai.gov.ro/cei/info/descarca-cert. Issues the per-citizen signing certificates stored in the CEI applet and used by AmSemnat.sign(...); those are the anchors for verifying the PAdES signatures the SDK produces.

Your app owns freshness and revocation — re-fetch on a cadence appropriate for your trust window.

Install

npm install @amsemnat/verifier

ESM-only. Server-side: Node 20 LTS or newer (for native globalThis.crypto.subtle). Client-side: any evergreen browser.

Quick start — passive auth

import { readFileSync } from "node:fs";
import { verifyPassive } from "@amsemnat/verifier";

const csca = readFileSync("./csca-romania.cer"); // DER, fetched from DGP

const result = await verifyPassive({
  rawSod: req.body.rawSod,             // RomanianIdentity.rawSod from the mobile SDK
  dataGroups: {
    1: req.body.rawDg1,
    2: req.body.rawDg2,
    14: req.body.rawDg14,
  },
  trustAnchors: [csca],
});

if (!result.valid) {
  console.error("Passive auth failed:", result.errors);
}

Quick start — PAdES signature (Node)

import { readFileSync } from "node:fs";
import { verifyPadesSignatures } from "@amsemnat/verifier";

const root = readFileSync("./ro-cei-mai-root-ca.cer");
const sub = readFileSync("./ro-cei-mai-sub-ca.cer");

const results = await verifyPadesSignatures({
  pdf: readFileSync("./signed.pdf"),
  trustAnchors: [root, sub],
});

for (const sig of results) {
  console.log(
    `#${sig.signatureIndex} [${sig.fieldName ?? "?"}] valid=${sig.valid} ` +
      `signer="${sig.signerCommonName ?? "?"}" ` +
      `coversWholeDocument=${sig.coversWholeDocument}`,
  );
}

Quick start — PAdES signature (browser)

import { verifyPadesSignatures } from "@amsemnat/verifier";

// Trust anchors fetched as static assets (DER), or bundled. Both work.
const [root, sub] = await Promise.all([
  fetch("/anchors/ro-cei-mai-root-ca.cer").then((r) => r.arrayBuffer()),
  fetch("/anchors/ro-cei-mai-sub-ca.cer").then((r) => r.arrayBuffer()),
]);

async function onFile(file: File) {
  const pdf = new Uint8Array(await file.arrayBuffer());
  const results = await verifyPadesSignatures({
    pdf,
    trustAnchors: [new Uint8Array(root), new Uint8Array(sub)],
  });
  // render `results` in the UI
}

The verifier returns [] for unsigned PDFs. Each PadesVerificationResult includes:

  • valid — overall outcome (CMS signature + chain + signingCertificateV2 binding).
  • errors — human-readable failure strings; empty when valid: true.
  • signerCommonName, signedAt — best-effort metadata.
  • signatureIndex, fieldName — multi-sig disambiguation.
  • byteRange, coversWholeDocument — caller decides whether valid && coversWholeDocument is the right policy for "trust this PDF end-to-end". A coversWholeDocument: false on a non-final signature is normal in incremental-update workflows.

Trust anchors

trustAnchors is a flat list of DER-encoded X.509 certificates. The verifier auto-classifies self-signed entries as roots and the rest as intermediates — same convention as the mobile SDKs' verifyPassiveOffline. Pass everything in one array.

PEM input is not accepted in 0.1.0. Decode to DER on the caller side (pem.replace(/-----.*-----|\s/g, '') then base64-decode).

What we do not check yet

Documented out of scope for 0.1.0:

  • Timestamp tokens (PAdES B-T). signedAt comes from the signingTime signed attribute only, not from a TST.
  • CRL / OCSP / LTV. Freshness and revocation are the consumer's responsibility — fetch a current CSCA / DGEP masterlist on a reasonable cadence yourself.
  • MRZ / DG1 parsing. This package verifies; it doesn't parse identity. Use the mobile SDK's RomanianIdentity object for that.
  • PEM trust anchors. DER only.

Public API parity

The top-level result fields (valid, errors, signerCommonName, signedAt) match the mobile SDKs' verifyPassiveOffline shape. Cross- platform code reading either offline or server-side results sees the same surface for the load-bearing checks.

License

Apache-2.0. No vendored third-party source. Runtime deps (pkijs, asn1js, pvtsutils) are MIT-licensed.