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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bicycle-codes/crypto-util

v0.2.7

Published

Cryptography utility functions for the browser or node

Downloads

656

Readme

crypto util

tests types module semantic versioning install size license

Utility functions for working with crypto keys in the browser or node.

This is some helpful functions that make it easier to work with cryptography. Note this does not deal with storing keys. Look at using @bicycle-codes/webauthn-keys (biometric authentication) or indexedDB for help with that.

This includes both sodium based keys and also webcrypto functions.

The Webcrypto keys are preferable because we create them as non-extractable keys, and are able to persist them in indexedDB, despite not being able to read the private key.

[!TIP] Request "persistent" storage with the .persist() method in the browser.


[!NOTE] The install size is kind of large (9.77 MB) because this includes a minified bundle of the sodium library.


Plus, See the docs generated from typescript


Contents


install


npm i -S @bicycle-codes/crypto-util

example


Create a new keypair

Use ECC keys with the web crypto API.

Also, you can use RSA keys.

import { create, KeyUse } from '@bicycle-codes/crypto-util'

// create a new keypair
const encryptKeypair = await create(KeyUse.Encrypt)
const signKeys = await create(KeyUse.Sign)

Use 2 ECC keypairs to create a new AES key

This requires a keypair + another keypair to derive a shared AES key.

import { getSharedKey } from '@bicycle-codes/crypto-util/webcrypto/ecc'
import { KeyUse } from '@bicycle-codes/crypto-util/types'

const alicesKeys = await createEcc(KeyUse.Encrypt)
const bobsKeys = await createEcc(KeyUse.Encrypt)

// pass in our private key, their public key
const sharedKey = await getSharedKey(alicesKeys.privateKey, bobsKeys.publicKey)

Bob can derive the same key by using their private key + Alice's public key.

const bobsSharedKey = await getSharedKey(bobsKeys.privateKey, alicesKeys.publicKey)

Encrypt with AES keys

Encrypt a given message with a given key.

import { create, encrypt } from '@bicycle-codes/crypto-util/webcrypto/aes'

const aesKey = await create()
const aesEncryptedText = await encrypt('hello AES', aesKey)

Decrypt with AES keys

import { decrypt } from '@bicycle-codes/crypto-util/webcrypto/aes'

const decrypted = await decrypt(aesEncryptedText, aesKey)

encrypt with ECC keys

This is a message from Alice to Bob. We use Alice's private key & Bob's public key.

import { KeyUse } from '@bicycle-codes/crypto-util'
import {
    create,
    encrypt,
    decrypt
} from '@bicycle-codes/crypto-util/webcrypto'

const alicesKeys = await create(KeyUse.Encrypt)
const bobsKeys = await create(KeyUse.Encrypt)
const eccEncryptedText = await encrypt(
    'hello ecc',
    alicesKeys.privateKey,
    bobsKeys.publicKey
)

Decrypt with ECC keys

Bob can decrypt the message encrypted by Alice, because we used bob's public key when encrypting it.

// note keys are reversed here --
// alice's public key and bob's private key
const decrypted = await decrypt(
    eccEncryptedText,
    bobsKeys.privateKey,
    alicesKeys.publicKey
)

// => 'hello ecc'

Create a signing keypair

Create another keypair that is used for signatures.

import { KeyUse } from '@bicycle-codes/crypto-util'
import { create } from '@bicycle-codes/crypto-util/webcrypto'

const eccSignKeys = await create(KeyUse.Sign)

Create signatures

import { sign } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const sig = await sign('hello dids', eccSignKeys.privateKey)

Create a DID

A DID is a decentralized identifier, a string the encodes a user's public key.

If you are transmiting your public key along with a message, for example, this is the preferred format.

import { publicKeyToDid } from '@bicycle-codes/crypto-util'

const did = await publicKeyToDid(eccSignKeys.publicKey)

Verify a signature

Use a DID to verify a signature string.

import { verifyWithDid, sign } from '@bicycle-codes/crypto-util/ecc'

const sig = await sign('hello dids', eccSignKeys.privateKey)
const isOk = await verifyWithDid('hello dids', sig, did)

API


See the API docs

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

ESM

import * as util from '@bicycle-codes/crypto-utils'

Common JS

const util = require('@bicycle-codes/crypto-utils')

pre-built JS

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

copy

cp ./node_modules/@bicycle-codes/crypto-util/dist/index.min.js ./public/crypto-util.js

HTML

<script type="module" src="./crypto-util.js"></script>

webcrypto vs sodium

To use the webcrypto API, import from the webcrypto sub-path.


webcrypto API


This depends on an environment with a webcrypto API.

import { aes, ecc, rsa } from '@bicycle-codes/crypto-util/webcrypto'

example

import { rsa, ecc, aes } from '@bicycle-codes/crypto-util/webcrypto'

// create some ECC keypairs
const eccKeypair = await ecc.create(KeyUse.Sign)
const eccSignKeys = await ecc.create(KeyUse.Encrypt)

// get the public key as a string
const publicKey = await ecc.exportPublicKey(eccSignKeys.publicKey)

// get the public key as a DID format string
const did = await ecc.publicKeyToDid(eccSignKeys.publicKey)

// transform a DID string to a public key instance
const publicKey = ecc.didToPublicKey(eccDid)

webcrypto AES API


aes.create

Create a new AES-GCM key.

function create (opts:{ alg, length } = {
    alg: DEFAULT_SYMM_ALGORITHM,
    length: DEFAULT_SYMM_LEN
}):Promise<CryptoKey>

aes.create example

import { create } from '@bicycle-codes/crypto-util/webcrypto/aes'

const aesKey = await createAes()

aes.encrypt

Encrypt a string.

async function encrypt (
    msg:Msg,
    key:SymmKey|string,
    opts?:Partial<SymmKeyOpts>
):Promise<string>
import { encrypt } from '@bicycle-codes/crypto-util/webcrypto/aes'

let aesEncryptedText:string
test('encrypt some text with AES', async t => {
    aesEncryptedText = await encrypt('hello AES', aesKey)
    // returns a string by default
})

aes.decrypt

async function decrypt (
    msg:Msg,
    key:SymmKey|string,
    opts?:Partial<SymmKeyOpts>,
    charSize:CharSize = DEFAULT_CHAR_SIZE
):Promise<string>
import { decrypt } from '@bicycle-codes/crypto-util/webcrypto/aes'

const decrypted = await decrypt(aesEncryptedText, aesKey)
// => 'hello AES'

webcrypto RSA API


We expose RSA because not all browser yet support ECC keys. See ./src/rsa/webcrypto.ts and ./test/index.ts.


webcrypto ECC API


ecc.create

async function create (
    use:KeyUse,
    curve:EccCurve = EccCurve.P_256,
):Promise<CryptoKeyPair>

create example

import { create } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const alicesEncryptionKeys = await createEcc(KeyUse.Encrypt)
const alicesSigningKeys = await createEcc(KeyUse.Sign)

sign

async function sign (
    msg:Msg,  // <-- string or Uint8Array
    privateKey:PrivateKey,
    { format }:{ format: 'base64'|'raw' } = { format: 'base64' },
    charSize:CharSize = DEFAULT_CHAR_SIZE,
    hashAlg:HashAlg = DEFAULT_HASH_ALGORITHM,
):Promise<ArrayBuffer|string>

example

import { sign } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const sig = await sign('hello webcrypto', eccSignKeys.privateKey)

verifyWithDid

Verify a signature with a DID format string.

async function verifyWithDid (
    msg:string,
    sig:string,
    did:DID
):Promise<boolean>
import { verifyWithDid } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const isOk = await verifyWithDid('hello dids', sig, did)

getSharedKey

Get a shared key given two existing keypairs.

async function getSharedKey (
    privateKey:PrivateKey,
    publicKey:PublicKey,
    opts?:Partial<{
        alg:'AES-GCM'|'AES-CBC'|'AES-CTR'
        length:SymmKeyLength
        iv:ArrayBuffer
    }>
):Promise<SymmKey>
let BobsKeys:CryptoKeyPair
let sharedKey:CryptoKey

const BobsKeys = await createEcc(KeyUse.Encrypt)
const sharedKey = await getSharedKey(eccKeypair.privateKey, BobsKeys.publicKey)
t.ok(sharedKey instanceof CryptoKey, 'should return a `CryptoKey`')

encrypt

Encrypt something with your private key and the recipient's public key.

async function encrypt (
    msg:Msg,  // <-- string or Uint8Array
    privateKey:PrivateKey,
    publicKey:string|PublicKey,  // <-- base64 or key
    { format }:{ format: 'base64'|'raw' } = { format: 'base64' },
    charSize:CharSize = DEFAULT_CHAR_SIZE,
    curve:EccCurve = DEFAULT_ECC_CURVE,
    opts?:Partial<{
        alg:SymmAlg
        length:SymmKeyLength
        iv:ArrayBuffer
    }>
):Promise<Uint8Array|string>

example

const eccEncryptedText = await ecc.encrypt(
    'hello ecc',
    alicesKeys.privateKey,
    BobsKeys.publicKey
)

decrypt

Decrypt some text that was encrypted with ecc.ecncrypt.

async function decrypt (
    msg:Msg,  // <-- string or Uint8Array
    privateKey:PrivateKey,
    publicKey:string|PublicKey,
    curve:EccCurve = DEFAULT_ECC_CURVE,
    opts?:Partial<{
        alg:'AES-GCM'|'AES-CBC'|'AES-CTR'
        length:SymmKeyLength
        iv:ArrayBuffer
    }>
):Promise<string>

example

Note the keys a swapped here -- the public and private keys can come from either keypair and it still works.

const decrypted = await ecc.decrypt(
    eccEncryptedText,
    BobsKeys.privateKey,
    alicesKeys.publicKey
)

sodium API


These should work anywhere that JS can run.

import { aes, ecc, rsa } from '@bicycle-codes/crypto-util/sodium'

Or import individual modules

import * as aes from '@bicycle-codes/crypto-util/sodium/aes'
import * as ecc from '@bicycle-codes/crypto-util/sodium/ecc'
import * as webcryptoAes from '@bicycle-codes/crypto-util/webcrypto/aes'

Sodium AES API


Encrypt with AEGIS-256 (symmetric crypto).

Sodium + AES example

import {
    create,
    encrypt,
    decrypt
} from '@bicycle-codes/crypto-util/sodium/aes'

// create a new key
const key = await create()

// or create a key, return a Uint8Array
const keyAsBuffer = await createAes({ format: 'raw' })

// encrypt something
const encryptedString = await encrypt('hello sodium + AES', key)

aes.create

Create a new AES key. Pass { format: 'raw' } to return a Uint8Array.

async function create (opts:{
    format: 'string'|'raw'
} = { format: 'string' }):Promise<Uint8Array|string>

example

import { create } from '@bicycle-codes/crypto-util/sodium/aes'

const aesKey = await create()

aes.encrypt

Encrypt the given string or buffer. Pass { format: 'raw' } to return a Uint8Array.

async function encrypt (
    msg:Uint8Array|string,
    key:Uint8Array|string,
    opts:Partial<{
        iv?:Uint8Array
        format?:'string'|'raw'
    }> = { format: 'string' },
):Promise<Uint8Array|string>

example

import { encrypt } from '@bicycle-codes/crypto-util/sodium/aes'

const encryptedString = await encryptAes('hello sodium + AES', aesKey)

aes.decrypt

Decrypt the given string or buffer. Pass { format: 'raw' } to return a Uint8Array.

async function decrypt (
    cipherText:string|Uint8Array,
    key:string|Uint8Array,
    opts:{ format:'string'|'raw' } = { format: 'string' }
):Promise<Uint8Array|string>

example

import { decrypt } from '@bicycle-codes/sodium/aes'

const decrypted = await decrypt(encryptedAes, aesKey)
// => "hello sodium + AES"

Sodium ECC API


Sodium + ECC example

import * as ecc from '@bicycle-codes/crypto-util/sodium/ecc'

const keys = await ecc.create()

ecc.create

Create a new Edward keypair.

async function create (
    use:KeyUse,
    curve:EccCurve = EccCurve.P_256,
):Promise<CryptoKeyPair>

.create example

import { create } from '@bicycle-codes/crypto-util/sodium/ecc'

const keys = await create()

ecc.sign

Create a signature for the diven data. Pass { format: 'raw' } to get a Uint8Array instead of a string.

async function sign (
    data:string|Uint8Array,
    key:LockKey,
    opts:{
        format:'string'|'raw'
    } = { format: 'string' }
):Promise<string|Uint8Array>

ecc.sign example

import { sign } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const sig = await sign('hello webcrypto', alicesKeys, { format: 'raw' })

ecc.publicKeyToDid

Take a public key instance and return a DID format string.

async function publicKeyToDid (
    publicKey:Uint8Array|PublicKey
):Promise<DID>

ecc.publicKeyToDid example

import {
  exportPublicKey,
  publicKeyToDid
} from '@bicycle-codes/crypto-util/webcrypto/ecc'

const arr = await exportPublicKey(eccSignKeys.publicKey)
const did = await publicKeyToDid(arr)

ecc.verify

Verify the given signature + public key + message data.

async function verify (
    msg:Msg,  // <-- string or Uint8Array
    sig:string|Uint8Array|ArrayBuffer,
    publicKey:string|PublicKey,
    charSize:CharSize = DEFAULT_CHAR_SIZE,
    curve:EccCurve = DEFAULT_ECC_CURVE,
    hashAlg: HashAlg = DEFAULT_HASH_ALGORITHM
):Promise<boolean>

ecc.verify example

const key = await importDid(eccDid)
const isOk = await verify('hello webcrypto', sig, key)
t.ok(isOk, 'should verify a valid signature')

ecc.verifyWithDid

Verify the given signature + message + public key are ok together, using the given DID string as public key material.

async function verifyWithDid (
    msg:string,
    sig:string,
    did:DID
):Promise<boolean>

ecc.verifyWithDid example

import { verifyWithDid } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const isOk = await verifyWithDid('hello dids', sig, did)

ecc.encrypt

Use the given private and public keys to create a shared key, then encrypt the message with the key.

async function encrypt (
    msg:Msg,
    privateKey:PrivateKey,
    publicKey:string|PublicKey,  // <-- base64 or key
    { format }:{ format: 'base64'|'raw' } = { format: 'base64' },
    charSize:CharSize = DEFAULT_CHAR_SIZE,
    curve:EccCurve = DEFAULT_ECC_CURVE,
    opts?:Partial<{
        alg:SymmAlg
        length:SymmKeyLength
        iv:ArrayBuffer
    }>
):Promise<Uint8Array|string>

ecc.encrypt example

import { encrypt } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const eccEncryptedText = await encrypt(
    'hello ecc',
    AlicesKeys.privateKey,
    BobsKeys.publicKey
)

ecc.decrypt

Decrypt a message given a public and private key. Note in the example, the keypairs are reversed from the encrypt example. This creates a new shared key via Diffie Hellman.

import { decrypt } from '@bicycle-codes/crypto-util/webcrypto/ecc'

const decrypted = await decrypt(
    eccEncryptedText,
    BobsKeys.privateKey,
    AlicesKeys.publicKey
)

see also