GithubHelp home page GithubHelp logo

secure-chest's Introduction

Secure Chest

Build Status Test Coverage Greenkeeper Badge Dependencies NPM Downloads Semantic-Release Gardener Gitter

Web-safe Encryption and Signing of Data

Use Case

Intended for storing data with untrusted party. Useful when storing data on server is expensive, forbidden, inconvenient or impossible.

Data is first signed and then, together with a timestamp, encrypted into a "chest" using a secret. Data can be extracted again and checked for consistency and freshness using the same secret.

Encoded Data is Url Safe and satisfies the regular expression ^[A-Za-z0-9\-_]+$. Internally Gzip is used when this shortens the tokens.

Getting Started

$ npm i --save secure-chest

Below is an example flow that allows users to be signed in without persisting any information on the server.

const { Chester, DecryptionExpiredError } = require("secure-chest");

const chester = Chester("SECRET-ENCRYPTION-KEY", {
  name: "facebook-auth",
  maxAgeInSec: 60 * 60 // require re-auth every hour
});

// ... facebook oauth flow ...

const token = getFacebookSessionToken();
const chest = chester.lockObj({ token });

// ... store chest with client ...

// ... time passes ...

// .. client makes request and provides chest ...

try {
  if (isValidFacebookUser(chester.unlockObj(chest))) {
    // welcome back
  }
} catch (e) {
  if (e instanceof DecryptionExpiredError) {
    // ... re-authenticate with facebook ...
  }
}

Or to create an unsubscribe link without storing information on the server one could use it as follows.

const { Chester, DecryptionExpiredError } = require("secure-chest");

const chester = Chester("SECRET-ENCRYPTION-KEY", {
  name: "email-unsubscribe",
  maxAgeInSec: 60 * 60 * 24 * 90 // link is valid for 90 days
});

// could also include hashed password-salt if unsubscribe links should be invalidated when password changes
const unsubscribeLink = `https://domain.com/unsubscribe?token=${chester.lockObj({ userId, userEmail })}`;

// ... generate and send email to user ...

// ... user clicks unsubscribe link ...

const token = getQueryParam("token");

try {
  const info = chester.unlockObj(token);
  const user = findUser(info.userId, info.userEmail);
  if (user) {
    unsubscribe(user);
  } else {
    // user does not exist or email address was changed
  }
} catch (e) {
  if (e instanceof DecryptionExpiredError) {
    // unsubscribe link has expired
  }
}

Chester

Exposes main functionality.

Parameters

secret

Type: string or Buffer

Secret used to encrypt data. If string is provided it is converted into Buffer internally using provided encoding.

name

Type: string
Default: default

Name of this Chester. A Chester can not open chests if Chester with different name but same secret locked them. Ease-of-life, so one can use same secret for different Chester.

Internally name is merged with provided secret and passed into underlying Crypter.

encoding

Type: constants.ENCODING
Default: constants.ENCODING.utf8

Encoding used to convert between strings and Buffers. For most cases utf8 is suitable.

zeroTime

Type: number
Default: 1514764800

Used to delay year 2038 problem. Should never be changed.

Necessary since timestamp is stored as 4 bytes.

maxAgeInSec

Type: number
Default: 60

Maximum age in seconds before chest expires and DecryptionExpiredError is thrown when trying to unlock it.

When value is changed it is automatically changed for all previously created chests, since chests only store a timestamp.

gzip

See Crypter below

encryption

See Cryper below

ivLength

See Cryper below

Errors

EncryptionError

General Encryption Error that all Encryption Errors inherit from.

EncryptionJsonError

Thrown from lockObj when JSON.stringify fails.

DecryptionError

General Decryption Error that all Decryption Errors inherit from.

DecryptionIntegrityError

Provided data can not be decrypted. Can be indication for invalid data or incorrect secret or name.

DecryptionSignatureError

Data was decrypted successfully, but signature did not match. Can also indicate invalid data or an incorrect secret or name.

Also thrown when context was changed.

DecryptionTimeTravelError

The chest is not valid yet. This usually only happens when the zeroTime is changed.

DecryptionExpiredError

The chest has expired.

DecryptionJsonError

Thrown from unlockObj when JSON.parse fails.

Functions

lock

lock(treasure, ...contexts)

Create and "lock" new chest. Takes data to encrypt as first argument and contexts as additional arguments.

When unlocking chest where contexts have been provided to lock it, unlocking requires the contexts to be identical.

lockObj

Wraps lock, and JSON.stringify is applied to first argument. On failure EncryptionJsonError is thrown.

unlock

unlock(chest, ...contexts)

Unlock a chest and returns data. Takes data to decrypt as first argument and contexts as additional arguments.

This method can throw various errors (see section).

unlockObj

Wraps unlock, and JSON.parse is applied to return value. On failure DecryptionJsonError is thrown.

_crypter

Exposes the underlying Crypter. Useful for debugging.

Example

const Chester = require("secure-chest").Chester;

const chester = Chester("SECRET-ENCRYPTION-KEY");
const data = "Some Text";
const chest = chester.lock(data);
chester.unlock(chest);
// => "Some Text"

Crypter

Used to encrypt and decrypt data using aes-256-cbc with 16 bit random IV by default (see notes below).

Deals only with Buffers and produced web-safe base64 and hence is encoding independent.

Internally this uses GZip when this shortens the output.

Important: Errors are not explicitly handled.

Functions

encrypt

encrypt(Buffer)

Takes a Buffer and encrypts it as a web-safe base64 encoded string.

decrypt

decrypt(Base64)

Takes a web-safe base64 encoded string and decrypts it into a Buffer.

Parameters

secret

Type: Buffer

Secret used to encrypt data. Internally this gets hashed.

gzip

Type: constant.GZIP_MODE
Default: constant.GZIP_MODE.AUTO

Overwrite gzip mode. By default gzip mode is only used when output is shortened. Useful when gzip is computationally too expensive.

Will not change how first bit is set in IV and hence ok to change for existing tokens.

encryption

Type: string
Default: aes-256-cbc

Defines encryption algorithm. IV length must be compatible.

ivLength

Type: number
Default: 16

Defines length of IV. Must be compatible with encryption.

Example

const crypto = require('crypto');
const Crypter = require("secure-chest").Crypter;

const crypter = Crypter(crypto.randomBytes(64));

const data = crypto.randomBytes(64);
const encrypted = crypter.encrypt(data); // non-deterministic due to IV
const decrypted = crypter.decrypt(encrypted);

Buffer.compare(data, decrypted);
// => 0

Constants

GZIP_MODE

Values AUTO, NEVER, FORCE

Defines gzip mode used internally.

ENCODING

Values utf8, ascii, latin1, binary

Defines encoding for string to buffer conversions.

Utility Functions

The functions toUrlSafeBase64 and fromUrlSafeBase64 are exposed.

toUrlSafeBase64(Buffer)

fromUrlSafeBase64(Base64)

Implementation Notes

This project is considered complete and won't see any major features or changes.

Input values are heavily checked and TypeError is raised if invalid.

Security Observations

GZip is only used when this shortens the output. One bit in IV indicates this and hence only len - 1 bits are truly random. Acceptable when len(IV) >= 16 bytes.

secure-chest's People

Contributors

greenkeeper[bot] avatar simlu avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.