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

webcrypto-crypt

v0.2.5

Published

Symmetric cryptography with a passphrase for node and the browser

Downloads

32

Readme

npm Travis license npm node GitHub last commit

Introduction

webcrypto-crypt encrypts and decrypts data at rest in node or the browser using secret key cryptography in conjunction with a passphrase.

The package will use either the Window.crypto or node-webcrypto-ossl library, depending on where it's running.

Encryption and decryption depend on the key derivation function referred to as PBKDF2 and the symmetric key algorithm called AES-GCM.

For node, the package provides streaming methods and command-line utility wcrypt to deal with files and pipes.

Install

λ npm install -g webcrypto-crypt

Quickstart

Command-line

From prompt

λ wcrypt | wcrypt -d
Data to encrypt: no honour among consultants.
Passphrase?
Confirm passphrase:
Passphrase?
no honour among consultants.λ

From and to file

λ wcrypt -i LICENSE -o LICENSE.wcrypt
Passphrase?
Confirm passphrase:
λ wcrypt -d -i LICENSE.wcrypt
Passphrase?
MIT License

Copyright (c) 2017 C2FO...

From arg

λ wcrypt -a 'edge of a dynastic rebellion' > mydata.wcrypt
Passphrase?
Confirm passphrase:
λ cat mydata.wcrypt | wcrypt -d  # or wcrypt -di mydata.wcrypt
Passphrase?
edge of a dynastic rebellionλ

From pipe

λ echo 'pretty despite its implicit threat' | wcrypt | wcrypt -d
Passphrase?
Confirm passphrase:
Passphrase?
pretty despite its implicit threat

Node.js

    const Wcrypt = require('webcrypto-crypt')
    const wcrypt = new Wcrypt.Cipher('justtesting')
    wcrypt.encrypt('no honour among consultants.')
    .then((buf) => {
      // do something with buf
    })

Browser

  <html>
    <head>
      <script src="dist/wcrypt.js"></script>
      <script>
        var wcrypt = new Wcrypt.Cipher('justtesting');
        wcrypt.encrypt('edge of a dynastic rebellion')
          .then((buf) => {
            console.log('Results %s', buf);
          });
      </script>
    </head>
    <body>
    </body>
  </html>

Examples

Node.js

#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
'use strict';
const readlineSync = require('readline-sync')
const Wcrypt = require('webcrypto-crypt')

function askForData () {
  return readlineSync.question('Data to encrypt? ')
}

function askForPassphrase () {
  return readlineSync
    .question('Passphrase to use? ',
      { hideEchoBack: true, mask: '' }
    )
}

var wcrypt = new Wcrypt.Cipher(askForPassphrase())

wcrypt.encrypt(askForData())
  .then((data) => {
    console.log(`
      Encrypted, hex-encoded: ${data.toString('hex')},
      Encrypted, base64-encoded: ${data.toString('base64')},
      Encrypted, web-safe base64-encoded: ${wcrypt.uriSafeBase64(data)},
      Decrypted using same passphrase:`
    )
    wcrypt.decrypt(data)
      .then((data) => {
        console.log('        ' + Buffer.from(data).toString('utf8'))
      })
  })
λ node examples/prompts.js
Passphrase to use?
Data to encrypt? ambiguous or even contradictory situations

Encrypted, hex-encoded: c0e82e52c7400925f43e2753249afd8b1527a193917267df53d73d63f287519717c73d3e6c1a3da1797ef19055a49b8402cbb0e10f0a5a294545
Encrypted, base64-encoded: wOguUsdACSX0PidTJJr9ixUnoZORcmffU9c9Y/KHUZcXxz0+bBo9oXl+8ZBVpJuEAsuw4Q8KWilFRQ==
Encrypted, web-safe base64-encoded: wOguUsdACSX0PidTJJr9ixUnoZORcmffU9c9Y_KHUZcXxz0.bBo9oXl.8ZBVpJuEAsuw4Q8KWilFRQ--

Decrypted using same passphrase:
ambiguous or even contradictory situations
λ

Browser

<html>
  <head>
    <script src="dist/wcrypt.js"></script>
    <script>
      var wcrypt = new Wcrypt.Cipher(prompt("Secret? "));
      wcrypt.encrypt(prompt("Data to encrypt? "))
        .then((data) => {
          console.log(`
            Encrypted, hex-encoded: ${ data.toString("hex") },
            Encrypted, base64-encoded: ${ data.toString("base64") },
            Encrypted, web-safe base64-encoded: ${ wcrypt.uriSafeBase64(data) },
            Decrypted using same passphrase:`
          );
          wcrypt.decrypt(data)
            .then((data) => {
              console.log('         ' + Buffer.from(data).toString("utf8"));
            });
        });
   </script>
 </head>
 <body>
   (Check the Developer console.)
 </body>
</html>
// In the dev console, after calls to window.prompt() are satisfied, e.g.:
1GTLSRGmdkiAn6pwBB1XWYllS2VgF87h8575Ok2NlIzdH42YgoUFRpFiqbYqj2BKLJEQqoQ-
index.html:11 in a world that allowed such mistakes

Test

Tested environments

| OS | Environment | Version | | :-------- | :------- | :------- | | Mac El Capitan | Chrome | 79.0.3945.130 | | Mac El Capitan | Firefox | 72.0.2 | | Mac El Capitan | Node | v13.9.0 | | Mac El Capitan | Safari | 11.1.2 |

Node.js

λ npm run test

Browsers

A file that should run the module tests in the browser is test/browser/wcrypt-test.html. webcrypto-crypt currently injects browserify's Buffer as a global object.

API

Wcrypt.getSignature()

Return the current file signature in use by this library as a Buffer.

Wcrypt.parseHeader(Buffer data)

Provided data is a valid webcrypto-wcrypt header, parse it and return an object with the following structure:

    {
      "material": {
        "iv": "<iv>",
        "salt": "<salt>",
      },
      "config": {
        "crypto": {
          "tagLength": "<tagLength>"
        },
        "derive": {
          "iterations": "<iterations>"
        }
      }
    }

Wcrypt.setHeader(Object config)

Applies this configuration object to the current file header definition.

Wcrypt.DEBUG = true | false

If set to true, send debugging statements to stderr. Default false.

new Wcrypt.Cipher(String passphrase || Object options)

Instantiate a webcrypto-crypt object using just a passphrase or more options beyond the passphrase. When passing in an object, the minimum specification looks like {material: { passphrase: <your passphrase> } }. Possible options are described below:

  var wcrypt = new Wcrypt.Cipher({
    config: {
      crypto: {
        usages: [myU1, myU2...],  // default ['encrypt', 'decrypt']
        tagLength: myTagLength,   // default 128
      },
      delimiter: myDelimiter,     // default '<WcRyP+>'
      derive: {
        hash: myHashFunction,     // default 'SHA-512'
        iterations: myIterations, // default 2000
        length: myLength,         // default 128
      },
      paranoid: true              // check each encrypted block
                                  // for incidental presence of
                                  // the delimiter, re-encrypt data
                                  // if detected (very slow).
                                  // default false
    },
    material: {
      iv: myInitializationVector, // default getRandomValues(new Uint8Array(12))
      passphrase: 'justtesting',  // REQUIRED, passphrase as String
      salt: mySalt                // default getRandomValues(new Uint8Array(16))
    }
  })
  .then(...

Callers passing in their own value for initialization vector (material.iv) must ensure that it is never reused under the same key. Likewise, callers passing in their own value for key derivation salt (material.salt) should ensure it contains data that explicitly distinguishes between different operations.

wcrypt.createHeader()

Return a Buffer filled with the appropriate seed data for encryption. See these lines.

wcrypt.decrypt(Buffer data)

Decrypt data by first parsing its header section to extract config and material settings. The innvoking wcrypt object will assume any extracted config and material settings for all subsequent operations until overwritten n by reading a different header. Returns a promise with its resolved value set to a Buffer of the decrypted data.

wcrypt.delimiter

The current delimiter in use by this library, e.g., <WcRyP+>.

wcrypt.encrypt(Buffer data)

Encrypt the given data and include a header. Returns a promise with its resolved value set to a Buffer of the encrypted data.

wcrypt.encryptDelimited(Buffer data)

Encrypt the given data and include a delimiter. Returns a promise with its resolved value set to a Buffer of the encrypted data.

wcrypt.getDelimiter()

Return the delimiter webcrypto-crypt is currently configured to use as a Buffer.

wcrypt.name

The current name of this library, e.g., webcrypto-crypt.

wcrypt.rawDecrypt(Buffer data, Object options)

Decrypt data. Assumes no header present unless assumeHeader: true is passed in via an options object. Returns a promise with its resolved value set to a Buffer of the decrypted data.

wcrypt.rawEncrypt(Buffer data, Object options)

Returns a promise with its resolved value set to a Buffer of the encrypted data. The first argument is the data to encrypt. The second argument options is optional and currently supports two attributes:

wcrypt.rawEncrypt('Some text to encrypt', {appendDelimiter: true});

Will append the wcrypt.delimiter to the end of the encrypted data before returning it. And:

wcrypt.rawEncrypt('Some text to encrypt', {includeHeader: true});

Will append a cleartext header to the returned data including: initialization vector, iterations, salt, and tagLength used in the encryption. See these lines.

wcrypt.subtle

The SubtleCrypto object currently in use.

wcrypt.uriSafeBase64(Buffer data)

Convenience method for browser contexts to encode the passed-in data using web safe base 64 alphabet and return the result.

wcrypt.version

The current version of this library, e.g., 0.1.2.

Processing node streams

In a node.js environment, webcrypto-crypt/lib/node-streams provides convenience methods for encrypting and decrypting Readable Streams. Please assume the following lines precede these examples:

        const fs = require('fs')
        const Wcrypt = require('webcrypto-crypt')
        const WcryptStream = require('webcrypto-crypt/lib/node-streams')
        const cleartext = 'LICENSE'
        const ciphertext = cleartext + '.wcrypt'

Encrypt a readable stream

When encrypting you need to pass in your own wcrypt object:

        const wcrypt = new Wcrypt.Cipher('justtesting')
        const clear = fs.createReadStream(cleartext)
        const encrypted = fs.createWriteStream(ciphertext)
        const read = WcryptStream.encrypt(wcrypt, clear)

        read.on('data', (chunk) => {
          encrypted.write(chunk)
        })
        read.on('end', () => {
          console.error('wrote ' + ciphertext)
        })

Decrypt a readable stream

When decrypting you need to pass in the passphrase:

        const decrypt = fs.createReadStream(ciphertext)
        const read = WcryptStream.decrypt('justtesting', decrypt)
        read.on('data', (chunk) => {
          console.log(chunk.toString('utf8'))
        })

Command-line

Installing webcrypto-crypt also installs a command-line utilty, wcrypt:

Help

TTY

λ wcrypt -h
Usage: wcrypt [options]

Options:
  --debug, -D    write debug info to stderr         [boolean] [default: "false"]
  --help, -h     Show help                                             [boolean]
  --outfile, -o  write data to this file                                [string]
  --infile, -i   read data from this file                               [string]
  --arg, -a      read data from command line                            [string]
  --decrypt, -d                                       [boolean] [default: false]

Examples:
  wcrypt -i msg.txt -o msg.wcrypt      file to encrypt
  wcrypt -di msg.wcrypt -o msg.txt     file to decrypt
  wcrypt -a "nonessential appliances"  string to encrypt

Piped

λ echo '' | wcrypt -h
Usage: data | wcrypt [options]

Options:
  --debug, -D    write debug info to stderr         [boolean] [default: "false"]
  --help, -h     Show help                                             [boolean]
  --outfile, -o  write data to this file                                [string]
  --decrypt, -d                                       [boolean] [default: false]

Examples:
  cat msg.txt | wcrypt -o msg.wcrypt       encrypt,store in file
  cat msg.wcrypt | wcrypt -d               decrypt
  curl http:..:4196 | wcrypt > aud.wcrypt  stream to encrypt

Examples

Note that some of these examples may take a few minutes to download or stream.

Note also that you may set the environment variable WCRYPT_PASSFILE to have wcrypt skip its passphrase prompts.

Text file

λ export TXTFILE_URL="http://web.archive.org/web/20160514230001/http://www.mit.edu/~yandros/doc/command.txt"
λ curl -Ss "$TXTFILE_URL" | wcrypt > command.wcrypt
Passphrase?
Confirm passphrase:
λ wcrypt -di command.wcrypt -o command.txt
Passphrase?
λ less command.txt

Audio file

λ export AUDIO_URL="https://ia802509.us.archive.org/18/items/interview-with-neal-stephenson.JUcDkK.popuparchive.org/focus990527b.mp3"
λ curl -NSs "$AUDIO_URL" | wcrypt > ns-interview.wcrypt # may take a few minutes...
Passphrase?
Confirm passphrase:
λ wcrypt -di ns-interview.wcrypt -o ns-interview.mp3
Passphrase?
λ open ns-interview.mp3

Audio stream

λ export STREAM_URL="http://vprbbc.streamguys.net/vprbbc24-mobile.mp3"
λ curl -NSsm 10 "$STREAM_URL" | wcrypt | wcrypt -d | mpg123 -q -
Passphrase?
Confirm passphrase:
Passphrase?

.wcryptpass

If no passphrase is specified, wcrypt will consult the environment variable WCRYPT_PASSFILE and try to use its contents as the path to a file containing the passphrase. If WCRYPT_PASSFILE is not defined, wcrypt will check for a .wcryptpass file in the user's home directory for the same purpose. In either case, on Unix systems the permissions of this file need to disallow access to world or group, achievable with, e.g., chmod 0600 ~/.wcryptpass. If the permissions are less strict, the file will be ignored.

Header structure

See these lines.

Security

Security Mail: [email protected] PGP key fingerprint: E838 B51C C63F 7ED6 0980 9535 4D46 5218 A674 6F81 Keyserver: pgp.mit.edu

See also

To do

  • Promisify mocha tests
  • Improve stream handling
  • Add key usage permissions to file header