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

@theqrl/qrl-cryptography

v0.3.0

Published

All the cryptographic primitives used in QRL

Readme

@theqrl/qrl-cryptography

All pure-js cryptographic primitives normally used when developing Javascript / TypeScript applications and tools for QRL.

The cryptographic primitives included are:

Usage

Use NPM / Yarn in node.js / browser:

# NPM
npm install @theqrl/qrl-cryptography

# Yarn
yarn add @theqrl/qrl-cryptography

See browser usage for information on using the package with major Javascript bundlers. It is tested with Webpack, Rollup and Parcel.

This package has no single entry-point, but submodule for each cryptographic primitive. Read each primitive's section of this document to learn how to use them.

The reason for this is that importing everything from a single file will lead to huge bundles when using this package for the web. This could be avoided through tree-shaking, but the possibility of it not working properly on one of the supported bundlers is too high.

// Hashes
const { keccak256 } = require("@theqrl/qrl-cryptography/keccak");

// KDFs
const { argon2idSync } = require("@theqrl/qrl-cryptography/argon2id");

// Random
const { getRandomBytesSync } = require("@theqrl/qrl-cryptography/random");

// AES encryption
const { encrypt } = require("@theqrl/qrl-cryptography/aes");

// ML-DSA-87
const { ml_dsa87 } = require("@theqrl/qrl-cryptography/ml_dsa87");

// utilities
const { hexToBytes, toHex, utf8ToBytes } = require("@theqrl/qrl-cryptography/utils");

A note for CommonJS (require) consumers

@noble/hashes is ESM-only, so the CommonJS build (dist/cjs) vendors a frozen copy of it to keep require() working. That copy does not appear in your node_modules tree, so your own npm audit cannot see it and an upstream @noble/hashes advisory reaches you only through a new @theqrl/qrl-cryptography release. ESM (import) consumers are unaffected — they resolve @noble/hashes through their own dependency tree as usual. See SECURITY.md for the version-pin and patch-playbook details.

Hashes: Keccak and SHAKE256

function keccak256(msg: Uint8Array): Uint8Array;
function shake256(msg: Uint8Array, opts?: { dkLen?: number }): Uint8Array;

Exposes following cryptographic hash functions:

  • keccak-256 variant of SHA3 (also keccak224, keccak384, and keccak512)
  • SHAKE256 extendable-output function (32-byte output by default)
const { keccak256, keccak224, keccak384, keccak512, shake256 } = require("@theqrl/qrl-cryptography/keccak");

keccak256(Uint8Array.from([1, 2, 3]))

// Can be used with strings
const { utf8ToBytes } = require("@theqrl/qrl-cryptography/utils");
keccak256(utf8ToBytes("abc"))

// If you need hex
const { bytesToHex: toHex } = require("@theqrl/qrl-cryptography/utils");
toHex(keccak256(utf8ToBytes("abc")))

// Select the SHAKE256 output length in bytes
shake256(utf8ToBytes("abc"), { dkLen: 64 })

keccak256 also exposes an incremental (streaming) interface via keccak256.create(), for hashing data that arrives in chunks:

const hash = keccak256.create();
hash.update(utf8ToBytes("ab"));
hash.update(utf8ToBytes("c"));
hash.digest(); // === keccak256(utf8ToBytes("abc"))

KDFs: Argon2id

function argon2id(password: Uint8Array, salt: Uint8Array, t: number, m: number, p: number, dkLen: number, onProgress?: (progress: number) => void): Promise<Uint8Array>;
function argon2idSync(password: Uint8Array, salt: Uint8Array, t: number, m: number, p: number, dkLen: number, onProgress?: (progress: number) => void): Uint8Array;

The argon2id submodule has two functions implementing the Argon2id key derivation algorithm in synchronous and asynchronous ways. This algorithm is very slow, and using the synchronous version in the browser is not recommended, as it will block its main thread and hang your UI.

The salt must be at least 8 bytes (use ≥16 bytes, fresh and random per password — see SECURITY.md). Tune t/m/p for your environment; weak parameters produce weak hashes.

const { argon2id } = require("@theqrl/qrl-cryptography/argon2id");
const { getRandomBytesSync } = require("@theqrl/qrl-cryptography/random");
const { utf8ToBytes } = require("@theqrl/qrl-cryptography/utils");

const salt = getRandomBytesSync(16); // fresh, random, ≥16 bytes — store it alongside the hash
console.log(await argon2id(utf8ToBytes("password"), salt, 8, 262144, 1, 32));

CSPRNG (Cryptographically strong pseudorandom number generator)

function getRandomBytesSync(bytes: number): Uint8Array;

The random submodule generates cryptographically strong pseudo-random data, backed by the platform WebCrypto crypto.getRandomValues in both browsers and node.js. If no WebCrypto implementation is available the function throws — there is no insecure fallback.

Requests are chunked at the 64 KiB per-call WebCrypto quota, so any size up to 2³² − 1 bytes works. As an additional tripwire, requests of 16 bytes or more throw if the platform RNG returns all zeros (probability 2⁻¹²⁸ from a healthy RNG) — a canary for catastrophically broken platform RNGs, not an entropy meter.

const { getRandomBytesSync } = require("@theqrl/qrl-cryptography/random");
console.log(getRandomBytesSync(32));

AES Encryption

// Misuse-resistant: generates a fresh IV internally (recommended).
function seal(msg: Uint8Array, key: Uint8Array): Promise<Uint8Array>;
function open(sealed: Uint8Array, key: Uint8Array): Promise<Uint8Array>;

// Raw AEAD: you supply and manage the IV.
function encrypt(msg: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;
function decrypt(cypherText: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;

The aes submodule provides authenticated encryption with AES-256-GCM, delegated to the platform's WebCrypto implementation. The 32-byte key is required; no other modes or key sizes are supported.

Prefer seal/open unless you have a specific reason to manage the IV yourself. seal draws a fresh 12-byte IV from the CSPRNG on every call and prepends it to the ciphertext (returning iv || ciphertext); open reads that IV back off the front and decrypts. This removes the catastrophic IV-reuse footgun of the raw encrypt/decrypt pair, where a repeated (key, iv) destroys confidentiality and authenticity (see below).

const { seal, open } = require("@theqrl/qrl-cryptography/aes");
const { getRandomBytesSync } = require("@theqrl/qrl-cryptography/random");
const { utf8ToBytes, bytesToUtf8 } = require("@theqrl/qrl-cryptography/utils");

const key = getRandomBytesSync(32); // 32 bytes for AES-256
const sealed = await seal(utf8ToBytes("message"), key); // iv is generated for you
const plaintext = await open(sealed, key);
console.log(bytesToUtf8(plaintext)); // "message"

The raw encrypt/decrypt pair below additionally require a caller-supplied 12-byte iv.

Encrypting with passwords

AES is not supposed to be used directly with a password. Doing that will compromise your users' security.

The key parameter is meant to be a strong cryptographic key. If you want to derive one from a password, use a key derivation function like argon2id.

How to use the IV parameter

The iv parameter must be unique per (key, plaintext) pair. Reusing an IV with the same key destroys both confidentiality and authenticity in AES-GCM.

Generate a fresh 12-byte IV for every encryption with the random module. Store the IV alongside the ciphertext; you must supply the same IV to decrypt.

How to handle errors with this module

Sensitive information can be leaked via error messages when using this module. Catch all errors thrown by seal/open (and the raw encrypt/decrypt) and re-raise them as a single generic "encryption failure" / "decryption failure" error in your application.

Example usage

const { encrypt, decrypt } = require("@theqrl/qrl-cryptography/aes");
const { getRandomBytesSync } = require("@theqrl/qrl-cryptography/random");
const { utf8ToBytes, bytesToUtf8 } = require("@theqrl/qrl-cryptography/utils");

const key = getRandomBytesSync(32); // 32 bytes for AES-256
const iv = getRandomBytesSync(12);  // 12 bytes for GCM

const ciphertext = await encrypt(utf8ToBytes("message"), key, iv);
const plaintext = await decrypt(ciphertext, key, iv);
console.log(bytesToUtf8(plaintext)); // "message"

ML-DSA-87

The ml_dsa87 submodule exports a single object whose methods are:

const ml_dsa87: {
  keygen(seed?: Uint8Array): { publicKey: Uint8Array; secretKey: Uint8Array };
  sign(secretKey: Uint8Array, message: Uint8Array, ctx: Uint8Array, randomizedSigning?: boolean): Uint8Array;
  verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array, ctx: Uint8Array): boolean;
};

Unlike the other submodules, the ML-DSA-87 functions are namespaced under the ml_dsa87 object — call ml_dsa87.keygen(), not a bare keygen().

It provides the ML-DSA-87 (FIPS 204) post-quantum digital signature scheme, powered by @theqrl/mldsa87.

keygen draws a fresh seed from the platform CSPRNG when none is supplied, and wipes that internal seed (best-effort) after key generation. Pass an explicit seed only when you need deterministic key derivation — a seed can regenerate the keypair, so treat it exactly like the secret key; when you supply one, you own its lifecycle (wipe it when done).

sign is hedged by default (FIPS 204 §3.4): fresh CSPRNG randomness is mixed into each signature's nonce, so the same inputs produce different — all valid — signature bytes on every call. This frustrates fault-injection attacks against deterministic signing. Pass randomizedSigning: false only when byte-reproducible signatures are themselves the requirement (test vectors, deterministic fixtures).

const { ml_dsa87 } = require("@theqrl/qrl-cryptography/ml_dsa87");
const { utf8ToBytes } = require("@theqrl/qrl-cryptography/utils");

// Generate a key pair (random seed)
const { publicKey, secretKey } = ml_dsa87.keygen();

// Sign a message
const ctx = utf8ToBytes("context");
const msg = utf8ToBytes("hello");
const signature = ml_dsa87.sign(secretKey, msg, ctx);

// Verify a signature
const isValid = ml_dsa87.verify(publicKey, msg, signature, ctx);

Browser usage

Rollup setup

Using this library with Rollup requires the following plugins:

These can be used by setting your plugins array like this:

  plugins: [
    commonjs(),
    resolve({
      browser: true,
      preferBuiltins: false,
    }),
  ]

License

@theqrl/qrl-cryptography is released under the MIT License.

Copyright (c) 2021 Patricio Palladino, Paul Miller, ethereum-cryptography contributors Copyright (c) 2024 The QRL Contributors

See the LICENSE file for the full text.