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/keys

v0.0.11

Published

Create keys with the webcrypto API

Downloads

862

Readme

keys

tests types module semantic versioning Common Changelog install size license

Create and store keypairs in the browser with the web crypto API.

Use indexedDB to store non-extractable keypairs in the browser. "Non-extractable" means that the browser prevents you from ever reading the private key, but the keys can be persisted and re-used indefinitely.

[!TIP] Use the persist method to tell the browser not to delete from indexedDB.

See also, the API docs generated from typescript.

install

npm i -S @bicycle-codes/keys

get started

indexedDB

Create a new keypair, then save it in indexedDB.

import { Keys } from '@bicycle-codes/keys'

const keys = await Keys.create()

// save the keys to indexedDB
await keys.persist()

// ... sometime in the future ...
// get our keys from indexedDB
const keysAgain = await Keys.load()

console.assrt(keys.DID === keysAgain.DID)  // true

sign and verify something

.verify takes the content, the signature, and the DID for the public key used to sign. The DID is exposed as the property .DID on a Keys instance.

[!NOTE]
verify is exposed as a separate function, so you don't have to include all of Keys just to verify a signature.

import { verify } from '@bicycle-codes/keys'

// sign something
const sig = await keys.signAsString('hello string')

// verify the signature
const isOk = await verify('hello string', sig, keys.DID)

encrypt something

Takes the public key we are encrypting to, return an object of { content, key }, where content is the encrypted content as a string, and key is the AES key that was used to encrypt the content, encrypted to the given public key. (AES key is encrypted to the public key.)

import { encryptTo } from '@bicycle-codes/keys'

// need to know the public key we are encrypting for
const publicKey = await keys.getPublicEncryptKey()

const encrypted = await encryptTo.asString({
  content: 'hello public key',
  publicKey
})

// => { content, key }

decrypt something

A Keys instance has a method decrypt. The encryptedMessage argument is an object of { content, key } as returned from encryptTo, above.

import { Keys } from '@bicycle-codes/keys'

const keys = await Keys.create()
// ...
const decrypted = await keys.decrypt(encryptedMsg)

API

exports

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

ESM

import '@bicycle-codes/keys'

Common JS

require('@bicycle-codes/keys')

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/@bicycle-codes/keys/dist/index.min.js ./public/keys.min.js

HTML

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

examples

Create a new Keys instance

Use the factory function Keys.create because async. The optional parameters, encryptionKeyName and signingKeyName, are added as properties to the keys instance -- ENCRYPTION_KEY_NAME and SIGNING_KEY_NAME. These are used as indexes for saving the keys in indexedDB.

class Keys {
  ENCRYPTION_KEY_NAME:string = 'encryption-key'
  SIGNING_KEY_NAME:string = 'signing-key'

  static async create (opts?:{
      encryptionKeyName:string,
      signingKeyName:string
  }):Promise<Keys>
}

.create() example

import { Keys } from '@bicycle-codes/keys'

const keys = await Keys.create()

Get a hash of the DID

Get a 32-character, DNS-friendly string of the hash of the given DID. Available as static or instance method.

static method

class Keys {
  static async deviceName (did:DID):Promise<string>
}

instance method

class Keys {
  async getDeviceName ():Promise<string>
}

Persist the keys

Save the keys to indexedDB. This depends on the values of class properties ENCRYPTION_KEY_NAME and SIGNING_KEY_NAME. Set them if you want to change the indexes under which the keys are saved to indexedDB.

.persist

class Keys {
  async persist ():Promise<void>
}

Restore from indexedDB

Create a Keys instance from data saved to indexedDB. Pass in different indexedDB key names for the keys if you need to.

static .load

class Keys {
    static async load (opts:{
      encryptionKeyName,
      signingKeyName
    } = {
      encryptionKeyName: DEFAULT_ENC_NAME,
      signingKeyName: DEFAULT_SIG_NAME
    }):Promise<Keys>
}

example

import { Keys } from '@bicycle-codes/keys'

const newKeys = await Keys.load()

Sign something

Create a new signature for the given input.

class Keys {
  async sign (
    msg:ArrayBuffer|string|Uint8Array,
    charsize?:CharSize,
  ):Promise<Uint8Array>
}

example

const sig = await keys.sign('hello signatures')

Get a signature as a string

class Keys {
  async signAsString (
    msg:ArrayBuffer|string|Uint8Array,
    charsize?:CharSize
  ):Promise<string>
}
const sig = await keys.signAsString('hello string')
// => ubW9PIjb360v...

Verify a signature

Check if a given signature is valid. This is exposed as a stateless function so that it can be used independently from any keypairs. You need to pass in the data that was signed, the signature, and the DID string of the public key used to create the signature.

async function verify (
    msg:string|Uint8Array,
    sig:string|Uint8Array,
    signingDid:DID
):Promise<boolean>
import { verify } from '@bicycle-codes/keys'

const isOk = await verify('hello string', sig, keys.DID)

Encrypt a key

This method uses async (RSA) encryption, so it should be used to encrypt AES keys only, not arbitrary data. You must pass in either a DID or a public key as the encryption target.

async function encryptKeyTo ({ key, publicKey, did }:{
    key:string|Uint8Array|CryptoKey;
    publicKey?:CryptoKey|Uint8Array|string;
    did?:DID
}):Promise<Uint8Array>

example

const encrypted = await encryptKeyTo({
    content: myAesKey,
    publicKey: keys.publicEncryptKey
})

const encryptedTwo = await encryptKeyTo({
  content: aesKey,
  did: keys.DID
})

Encrypt some arbitrary data

Take some arbitrary content and encrypt it. Will use either the given AES key, or will generate a new one if it is not passed in. The return value is the encrypted key and the given data. You must pass in either a DID or a public key to encrypt to.

async function encryptTo (opts:{
    content:string|Uint8Array;
    publicKey?:CryptoKey|string;
    did?:DID;
}, aesKey?:SymmKey|Uint8Array|string):Promise<{
    content:Uint8Array;
    key:Uint8Array;
}>

example

import { encryptTo } from '@bicycle-codes/keys'

const encrypted = await encryptTo({
    key: 'hello encryption',
    publicKey: keys.publicEncryptKey
    // or pass in a DID
    // did: keys.DID
})

// => {
//   content:Uint8Array
//   key: Uint8Array  <-- the encrypted AES key
// }

Decrypt a message

class Keys {
  async decrypt (msg:{
      content:string|Uint8Array;
      key:string|Uint8Array;
  }):Promise<Uint8Array>
}
const decrypted = await keys.decrypt(encrypted)
// => Uint8Array

decryptToString

Decrypt a message, and stringify the result.

class Keys {
  async decryptToString (msg:EncryptedMessage):Promise<string>
}
const decrypted = await keys.decryptToString(encryptedMsg)
// => 'hello encryption'

AES

Expose several AES functions with nice defaults.

  • algorithm: AES-GCM
  • key size: 256
  • iv size: 12 bytes (96 bits)
import { AES } from '@bicycle-codes/keys'

const key = await AES.create(/* ... */)

create

Create a new AES key. By default uses 256 bits & GCM algorithm.

function create (opts:{ alg:string, length:number } = {
    alg: DEFAULT_SYMM_ALGORITHM,  // AES-GCM
    length: DEFAULT_SYMM_LENGTH  // 256
}):Promise<CryptoKey>
import { AES } from '@bicycle-codes/keys'
const aesKey = await AES.create()

export

Get the AES key as a Uint8Array.

{
  async export (key:CryptoKey):Promise<Uint8Array>
}
const exported = await AES.export(aesKey)

exportAsString

Get the key as a string, base64 encoded.

async function exportAsString (key:CryptoKey):Promise<string>
const exported = await AES.exportAsString(aesKey)

encrypt

async function encrypt (
  data:Uint8Array,
  cryptoKey:CryptoKey|Uint8Array,
  iv?:Uint8Array
):Promise<Uint8Array>
const encryptedText = await AES.encrypt(fromString('hello AES'), aesKey)

decrypt

async function decrypt (
  encryptedData:Uint8Array|string,
  cryptoKey:CryptoKey|Uint8Array|ArrayBuffer,
  iv?:Uint8Array
):Promise<Uint8Array>
const decryptedText = await AES.decrypt(encryptedText, aesKey)