Giter Site home page Giter Site logo

cipher.js's Introduction

Cipher.js for Bun, Node, and Browsers

Because you don't have to be an expert to use cryptography!

  • Keys can be 128-, 192-, or 256-bit (16, 24, or 32 bytes)
  • Plain input can be raw Uint8Arrays or (UTF-8) Strings
  • Encrypted output can be Base64UrlSafe Strings, or raw Uint8Arrays

Table of Contents

  1. Example
  2. Generate a Key
  3. Initialize the Cipher (Codec)
  4. Encrypt (Cipher) Data
  5. Decrypt (Decipher) Data
  6. Convert between Bytes, Hex, Base64, and URL-Safe Base64
  7. API
  8. Implementation Details

Example

Encrypt and Decrypt with AES-GCM.

let Cipher = require("@root/cipher");

let cipher = Cipher.create(sharedSecret);

let plainBytes = [0xde, 0xad, 0xbe, 0xef];
let encBase64UrlSafe = cipher.encrypt(plainBytes);

let originalBytes = Cipher.decrypt(encBase64UrlSafe);

Usage

Copy-and-Paste Snippets for the Masses

1. Generate a Key

// Generate a 128-bit, 192-bit, or 256-bit AES secret:
let secretBytes = new Uint8Array(16); // or 24, or 32
crypto.getRandomValues(secretBytes);

let secretHex = Cipher.utils.bytesTohex(secretBytes);

2. Initialize the Cipher (Codec)

let secretHex = process.env.APP_SECRET_KEY;

let secretBytes = Cipher.utils.hexToBytes(secretHex);
let cipher = Cipher.create(sharedSecret);

3. Encrypt (Cipher) Data

Plain Bytes => Encrypted Base64UrlSafe

let plainBytes = [0xde, 0xad, 0xbe, 0xef];
let encBase64UrlSafe = cipher.encrypt(plainBytes);
console.info("Encrypted (URL-Safe Base64)", encBase64UrlSafe);

Plain String => Encrypted Base64UrlSafe

let plainText = "123-45-6789";
let encBase64UrlSafe = cipher.encryptString(plainText);
console.info("Encrypted (URL-Safe Base64)", encBase64UrlSafe);

Plain Bytes => Encrypted Bytes

let plainBytes = [0xde, 0xad, 0xbe, 0xef];
let encBytes = cipher.encryptAsBytes(plainBytes);
console.info("Encrypted (Bytes)", encBytes);

Plain String => Encrypted Bytes

let plainText = "123-45-6789";
let encBytes = cipher.encryptStringAsBytes(plainText);
console.info("Encrypted (Bytes)", encBytes);

4. Decrypt (Decipher) Data

Encrypted String => Plain Bytes

let bytes = cipher.decrypt(encBase64UrlSafe);
console.info("Plain (Bytes)", bytes);

Encrypted Bytes => Plain Bytes

let bytes = cipher.decryptBytes(encBytes);
console.info("Plain (Bytes)", bytes);

Encrypted String => Plain String

let text = cipher.decryptToString(encBase64UrlSafe);
console.info("Plain (Text)", text);

Encrypted Bytes => Plain String

let text = cipher.decryptBytesToString(encBytes);
console.info("Plain (Text)", text);

5. Convert between Bytes, Hex, Base64, and URL-Safe Base64

Doing what Uint8Array should do, but doesn't.

Bytes <=> Hex

let hex = Cipher.utils.bytesToHex(bytes);
let bytes = Cipher.utils.hexToBytes(hex);

Bytes <=> Base64

let base64 = Cipher.utils.bytesToBase64(bytes);
let bytes = Cipher.utils.base64ToBytes(base64);

Bytes <=> URL-Safe Base64

let base64urlsafe = Cipher.utils.bytesToUrlSafe(bytes);
let bytes = Cipher.utils.urlSafeToBytes(base64urlsafe);

API

Cipher.create(keyBytes)                => cipher instance

cipher.encrypt(bytes)                  => Promise<Base64UrlSafe>
cipher.encryptString(string)           => Promise<Base64UrlSafe>

cipher.encryptAsBytes(bytes)           => Promise<Uint8Array>
cipher.encryptStringAsBytes(string)    => Promise<Uint8Array>

cipher.decrypt(encrypted)              => Promise<Uint8Array>
cipher.decryptToString(encrypted)      => Promise<Base64UrlSafe>

cipher.decryptBytes(encBytes)          => Promise<Uint8Array>
cipher.decryptBytesToString(encBytes)  => Promise<Base64UrlSafe>
Cipher.utils.bytesToHex(bytes)         => hex string
Cipher.utils.hexToBytes(hex)           => bytes

Cipher.utils.bytesToBase64(bytes)      => base64
Cipher.utils.base64ToBytes(base64)     => bytes

Cipher.utils.bytesToUrlSafe(bytes)     => url-safe base64 string
Cipher.utils.urlSafeToBytes(url64)     => bytes

Implementation Details

The Initialization Vector (IV) is a salt that prevents known-plaintext attacks - meaning that if you encrypt the same message with the same key twice, you get a different encrypted output.

The first 12-bytes (96-bits) are for the IV. The following bytes are the data and the Tag.

If the data is somehow corrupted or truncated, but the first bytes are intact, it may be possible to use the IV to restore some of the partial data (though Tag verification will likely fail).

LICENSE

Copyright 2021-Present Root, Inc

This Source Code Form is subject to the terms of the Mozilla
Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at
https://mozilla.org/MPL/2.0/.

cipher.js's People

Contributors

coolaj86 avatar

Stargazers

 avatar

Watchers

 avatar

cipher.js's Issues

Ergonomic update

This just cleans up the API:

  • makes it possible to have multiple instances
  • removes the 'bespoke' prefix (no standards for encoding exist, AFAIK, so it's all 'bespoke')
  • places IV before and after the data (the backup is small, and without the IV, the data is unrecoverable)
"use strict";

let Crypto = require("crypto");

let Cipher = module.exports;

const ALG = "aes-128-cbc";
const IV_SIZE = 16;

/**
 * @param {Buffer} key128
 */
Cipher.create = function (key128) {
  //let sharedSecret = Buffer.from(key128, "base64");

  let cipher = {};

  /**
   * @param {String} plaintext
   */
  cipher.encrypt = function (plaintext) {
    let initializationVector = Crypto.randomBytes(IV_SIZE); // IV is always 16-bytes
    let encrypted = "";

    let _cipher = Crypto.createCipheriv(ALG, key128, initializationVector);
    encrypted += _cipher.update(plaintext, "utf8", "base64");
    encrypted += _cipher.final("base64");

    let iv64 = toWeb64(initializationVector.toString("base64"));
    let enc64 = toWeb64(encrypted);
    // store iv64 twice, as a backup
    return `${iv64}:${enc64}:${iv64}`;
  };

  /**
   * @param {String} parts
   */
  cipher.decrypt = function (parts) {
    let [initializationVector, encrypted, initializationVectorBak] =
      parts.split(":");
    let plaintext = "";
    if (initializationVector !== initializationVectorBak) {
      console.error("corrupt (but possibly recoverable) initialization vector");
    }

    let iv = Buffer.from(initializationVector, "base64");
    let _cipher = Crypto.createDecipheriv(ALG, key128, iv);
    plaintext += _cipher.update(encrypted, "base64", "utf8");
    plaintext += _cipher.final("utf8");

    return plaintext;
  };

  return cipher;
};

/**
 * @param {String} x
 */
function toWeb64(x) {
  return x.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

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.