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

@substrate-system/simple-hpke

v0.0.8

Published

Hybrid Public Key Encryption

Downloads

1,289

Readme

Simple HPKE

tests types module semantic versioning Common Changelog install size gzip size license

Hybrid Public Key Encryption (RFC 9180)

1 dependency -- uint8arrays.

Install

npm i -S @substrate-system/simple-hpke

Example

Wrap an AES key, or encrypt a message.

Key Wrapping

Encrypt an AES key to yourself, then recover it later.

import { seal, open } from '@substrate-system/simple-hpke'

// An X25519 keypair. The private key can be non-extractable.
// HPKE needs only `deriveBits`.
const keypair = await crypto.subtle.generateKey(
    { name: 'X25519' },
    false,  // extractable
    ['deriveBits']
)

// create a new AES key, and encrypt it to your public key.
const { wrapped, key } = await seal(keypair)

// Or wrap an existing AES key. The supplied key must be extractable.
const aesKey = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    true,  // extractable
    ['encrypt', 'decrypt']
)

// pass in a key. The return value is just the wrapped key
const { wrapped } = await seal(keypair, aesKey)

// Later, recover the same key with your private key.
const recoveredKey = await open(keypair, wrapped)

See docs/README.md for the full API and rationale.

Hybrid Encryption

Seal a key, then use it to encrypt a message with AES-GCM. The wrapped key is concattenated with the cipher text, along with the IV. The recipient uses their private key to open the AES key and decrypt the message.

import { seal, open } from '@substrate-system/simple-hpke'

const recipient = await crypto.subtle.generateKey(
    { name: 'X25519' },
    false,  // not extractable
    ['deriveBits']
)

// Create and seal a fresh AES-GCM key, and encrypt a message with it.
const { wrapped, key } = await seal(recipient)
const iv = crypto.getRandomValues(new Uint8Array(12))
const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    new TextEncoder().encode('attack at dawn')
)

// Send `wrapped`, `iv`, and `ciphertext` together. `wrapped` is a fixed
// 80 bytes for this suite, and the AES-GCM IV is 12 bytes, so the recipient
// can slice the payload back apart at known offsets.
const ciphertext = new Uint8Array(ciphertext)
const message = new Uint8Array(wrapped.length + iv.length + ciphertext.length)
message.set(wrapped, 0)
message.set(iv, wrapped.length)
message.set(ciphertext, wrapped.length + iv.length)

// On the other side, split the payload back into its parts.
const wrapped2 = message.subarray(0, 80)
const iv2 = message.subarray(80, 80 + 12)
const ciphertext2 = message.subarray(80 + 12)

// Recover the key, then decrypt the message.
const recovered = await open(recipient, wrapped2)
const plaintext = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv: iv2 },
    recovered,
    ciphertext2
)

new TextDecoder().decode(plaintext)  // => 'attack at dawn'

Encrypt / Decrypt

So that was a lot of code to encrypt and decrypt a message... This package exposes functions encrypt and decrypt which do the same thing.

encrypt seals an AES key to the recipient, encrypts the message under that key, and returns a single envelope: wrappedLen + wrapped + iv + ciphertext (a 2-byte length prefix, the wrapped key, the 12-byte AES-GCM IV, and the cipher text). decrypt reverses it, returning the plaintext bytes.

[!NOTE]
See decrypt.asString & decrypt.fromString below for a convenient way to decrypt from a string.

See encrypt.asString for encrypting and ecoding to a string.

import {
    encrypt,
    decrypt
} from '@substrate-system/simple-hpke'

// need a public key for the recipient
const recipient = await crypto.subtle.generateKey(
    { name: 'X25519' },
    false,
    ['deriveBits']
)

// create a new AES key, encrypt a message, and get back one envelope
const encryptedMessage = await encrypt(recipient, 'hello encryption')

// the recipient recovers the message with their private key
const text = await decrypt.asString(recipient, encryptedMessage)

// use `decrypt` to get a Uint8Array
const bytes = await decrypt(recipient, encryptedMessage)

//
// use an existing AES key
//
const existingKey = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    true,  // extractable
    ['encrypt', 'decrypt']
)

const anotherEncryptedMsg = await encrypt(
    recipient,
    'hello again',
    existingKey
)
encrypt

The recipient can be a crypto key, a Uint8Array, or a string public key.

type RecipientKey =
    | CryptoKey
    | CryptoKeyPair
    | Uint8Array
    | { publicKey:string; encoding?:Uint8ArrayEncodings }

async function encrypt (
    recipient:RecipientKey,
    message:Uint8Array|string,
    aesKey?:CryptoKey|Uint8Array|null,
    opts?:{
        keysize?:128|256
        info?:Uint8Array|string
    }
):Promise<Uint8Array>
Encrypt to a string

encrypt.asString is encrypt with the envelope encoded to a string, handy for transports that carry text (JSON, URLs, headers). opts.encoding sets the string encoding. Default encoding is base64url.

import { encrypt, decrypt } from '@substrate-system/ecies'
import { fromString } from 'uint8arrays'

// recipient is any RecipientKey; keypair holds the matching private key
const encryptedString = await encrypt.asString(
    recipient,
    'message for them',
    null,  // an AES key if you want
    { encoding: 'base64url' }
)

// Decode it back to bytes before decrypting.
const message = fromString(encryptedString, 'base64url')
const plaintext = await decrypt.asString(keypair, message)
// 'message for them'

The returned string encodes the same envelope encrypt returns, so the recipient decodes it with a matching decoder (here fromString) and passes the bytes to decrypt / decrypt.asString.

decrypt

Descrypt the given data, return a Uint8Array.

async function decrypt (
    keypair:CryptoKeyPair,
    message:Uint8Array,
    opts?:{ info?:Uint8Array|string }
):Promise<Uint8Array>
decrypt.asString

Take a Uint8Array, return a sttring.

decrypt.asString = async function decryptToString (
    keypair:CryptoKeyPair,
    message:Uint8Array,
    opts?:{ info?:Uint8Array|string }
):Promise<string>
decrypt.fromString

Take a string as input. Return either a string, or if opts.buffer is true, aUint8Array.

decrypt.fromString = async function decryptFromString (
    keypair:CryptoKeyPair,
    message:string,
    opts?:{ info?:Uint8Array|string, buffer?:boolean }
):Promise<string|Uint8Array>

Modules

This exposes ESM and common JS via package.json exports field.

ESM

import { seal, open, encrypt, decrypt } from '@substrate-system/simple-hpke'

Common JS

require('@substrate-system/simple-hpke')

pre-built JS

This package exposes minified JS files too. Copy them to a location that is accessible to your web server, then link to them in HTML.

copy

cp ./node_modules/@substrate-system/simple-hpke/dist/index.min.js ./public/hpke.min.js

HTML

<script type="module" src="./hpke.min.js"></script>