UNPKG

otpus

Version:

General purpose cipher for Node & browser

435 lines (324 loc) 12.8 kB
# OTPUS Cipher and tools for Node and Browser. ( 범용 암호 구현 및 환경) ## Features - Ready to development using Web Crypto API. - Isomorphic Web Crypto Name Reference(Node and Browser) - You can use isomorphic reference name of 'webCrypto' from otpus. - Node.js and Browser use different namespace. - WebCrypto API based general purpose async style encryption functions - algorithm: AES-GCM, PBKDF2. - encrypt() : support any type of data and key. - `random data size` - secret buffer pack ( meta buffer pack ) - decrypt() : return `origin data type`.( thanks to the special feature of MBP) - pure otpus implements - xotp() - cipher function. based XOR and Pseudo OTP. - encryptMessage() - simple but strong enough text message encryption.( `Use stong passphrase`) - recersive hash sum with salt.(like PBKDF2) - `random data size`.(hide real message size.) - HMAC support. (detect corrupted message.) - output: base64 string.(simple handy universal data format) - decryptMessage() - receive base64 encoded message. - return plain text. - return undefined. ( for corrupted message) - include essential tools: - otpus.sha256 : [fast-sha256](https://github.com/dchest/fast-sha256-js) : sync type hash, hmac. - otpus.base64js : [base64js](https://github.com/beatgammit/base64-js) : transcoder( buffer <-> UTF8 string ) - otpus.MBP : [`meta buffer pack`]( https://github.com/make-robot/meta-buffer-pack) : buffer packer - otpus.Buffer : [buffer](https://github.com/feross/buffer) : Node.js Buffer for browser. ## Table of Contents - [otpus](#otpus) - [Features](#features) - [Table of Contents](#table-of-contents) - [Support](#support) - [Usage](#usage) - [Installing](#installing) - [Loading module](#loading-module) - [Sync functions](#sync-functions) - [encryptMessage()](#encryptmessage) - [decryptMessage()](#decryptmessage) - [Encoded data](#encoded-data) - [decryptMessage()](#decryptmessage-1) - [corruption check](#corruption-check) - [xotp()](#xotp) - [Async functions using WebCrypto API](#async-functions-using-webcrypto-api) - [encrypt()](#encrypt) - [decrypt()](#decrypt) - [Error catch](#error-catch) - [Handling buffer](#handling-buffer) - [Examples](#examples) - [Online demo](#online-demo) - [License](#license) ## Support You can use modern ESM style or Legacy CJS, IIFE style both. - NodeJS: - ESM: src/index.js , dist/otpus.mjs (bundled version.) - CommonJS: dist/otpus.cjs - Browser: - ESM: dist/otpus.esm.js - IIFE: dist/otpus.min.js ## Usage ### Installing ``` npm i otpus ``` ### Browser - IIFE: Use script tag in html - the otpus.min.js file is inside dist folder. ```html <script src="../dist/otpus.min.js"></script> ``` - You can use global CDN url. [ jsDeliver ](https://www.jsdelivr.com/package/npm/otpus) ```html <script src="https://cdn.jsdelivr.net/npm/otpus@1/dist/otpus.min.js"></script> ``` - ES Module. ```js // don't forget the fullpath and file extension. import {encryptMessage, decryptMessage, xotp } from "../path/otpus.esm.js" ``` ### Node.js ```js // Node ESM import {encryptMessage, decryptMessage, xotp } from "otpus" // Node CJS // tip. if your pacakage.json using "module" type, you should use *.cjs file extension. const {encryptMessage, decryptMessage, xotp } = require("otpus") ``` ## Sync functions ### encryptMessage() ```js encryptMessage( data: String , key: String , nPower: Number = 15 ) : String ``` General purpose text message encryption. - data: Any UTF8 string. - key: Any UTF8 string. - `please use long passPhrase.` - nPower: - factor for recursive hash sum counter - counter = 2 ** nPower - counter is the result of exponentiation with number two as the base and integer nPower as the exponent. - default value is 15. ( 2 ** 15 => 32768 times) - `You can use high number for strong encryption`( but it takes more cpu time. ) - example( on my device) - mobile: 15 => spent about one second. - desktop: 17 => spent about one second. - output: encrytped and encoded base64 string. ### decryptMessage() ```js decryptMessage( data: String, key: String ) : String ``` - data: base64 ecoded string data - key: same with encryptMessage() - output: plain text ### Encoded data - encryptMessage() return base64 string data. - If you need buffer data. use Buffer.from( base64Msg, 'base64' ) method. - or reverse command: buffer.toString('base64') ```js import { encryptMessage, decryptMessage , Buffer } from 'otpus' const plainText = 'this is sercret message' const keyStr = 'this is secret key' let encPack = encryptMessage( plainText, keyStr ) // now encPack is encrypted & base64 encoded string. /* random size. sX7SStdL/pF7umBBEJ7EKXuv7QYLflCe3vy9F+XEayQVfAVa3PoZ1UasXl ... SxbIm5Qb3dlciIsIjgiLDE0OF1dAE8= */ // if you need buffer data. const encBuffer = Buffer.from( encPack , 'base64' ) // or reverse transformation ( from buffer to base64) also avaiable. const base64Pack = encBuffer.toString('base64') let decMsg = decryptMessage(encPack ,keyStr ) if( decMsg ){ // success // decMsg === 'this is sercret message' }else{ // decMsg === undefined when fail. } ``` ### decryptMessage() ### corruption check - When encryption process is fail for any reason. it will return undefined. - The encryption data store HMAC ( hash of message and key) info inside. - If calculated hmac is dismatched with stored hmac then return undefined. - Try this example. See the result when modify one byte of encryption data. ```js import { encryptMessage, decryptMessage } from 'otpus' const plainText = 'this is sercret message' const keyStr = 'this is secret key' let encPack = encryptMessage( plainText, keyStr ) encPack = messageModification( encPack) //try make corrupted message. let decMsg = decryptMessage(encPack ,keyStr ) if( decMsg ){ // success console.log( 'decMsg:', decMsg ) }else{ // return undefined when fail. console.log('wrong data') } function messageModification( dataOrg ){ const data = Buffer.from(dataOrg, 'base64') data[10] ^= 0x01; // modify one byte return data.toString('base64') } ``` ### xotp() This function is base cipher algorithm for otpus.( using XOR and Pseudo OTP.) You can make other encryption function( like encyptMessage) using this function. ```js xotp( data: Uint8Array, otpKey32Bytes: Uint8Array, otpSartIndex: Number = 0, shareDataBuffer: boolean = false ) : Uint8Array ``` - Using encryption and decryption both. - data: Uint8Array only. - key: 32bytes Uint8Array only. - otpStartIndex: Number( 0 ~ 2**32-1.) default. 0 - shareDataBuff: `important` - false: return new buffer. default. - recommended for small size data. - true: modify input data buffer. - recommended for the large data. ```js import { MBP, sha256, xotp } from 'otpus' const msgStr = 'aaaaaaaaaaaa' const pwStr = 'passphrase' let otpKey = sha256.hash( pwStr) // return 32bytes Uint8Array from string data. let data = MBP.U8( msgStr ) // MBP.U8(): parse any type of data into Uint8Array // use case 1. shareDataBuffer is false. let enc = xotp( data ,otpKey, 0 ) // false default. let dec = xotp( enc , otpKey, 0 ) // use case 2. shareDataBuffer is true. let encShare = xotp( data ,otpKey, 0 , true) // encShare & data is reference of same arrayBuffer. // when shareOption is true. // no return value needed. ( same below.) // data before encryption. xotp( data , otpKey, 0, true) // now input data is changed. xotp( data , otpKey, 0 , true) // now data is decrypted. ``` ## Async functions using WebCrypto API **Secure context**: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers. [MDN SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) **Node.js**: The Web Cryptography API implementation has landed as an experimental feature in Node.js 15.0.0.( current status: Stability: 1 - Experimental.) [Implementing the Web Cryptography API for Node.js Core](https://www.nearform.com/blog/implementing-the-web-cryptography-api-for-node-js-core/) ### encrypt() otpus's general purpose encryption implement using Web Crypto API. features: - any type of data. - any type of key( passPhrase). `string passphrase or buffer` - result of decryption data will be same data type of origin data. - randomize data size. (to hide real message size) - Using WebCrypto API - encryption algorithm: AES-GCM of Webcrypto API. - support message authentication - key generation: PBKDF2 of WebCrypto API. - salt, iv: from getRandomValues - data packaging: MBP(meta-buffer-pack) ```js encrypt( data: any , passPhrase: any ,iterations : Number = 32768 ) bufferPack: Promise ``` - async version of otpus.encrytMessage() - available: promise chaining, async await. - input - data {Stinrg | Uint8Array | Number | Object } - passPhrase {Stinrg | Uint8Array | Number | Object } - iterations {Number} iterations for PBKDF2 ( default 32768.) - returns - Promise ( will return bufferPack when fulfilled ) - bufferPack is Node.js Buffer( subclass of Uint8Array) ### decrypt() ```js decrypt(data:Uint8Array , passPhrase: any ) decodeData: Promise ``` - input - bufferPack(MBP pack) {Uint8Array} - passPhrase {String | Uint8Array | any } - returns - Promise ( will return decodedData when fulfilled ) ```js import { encrypt, decrypt ,Buffer } from "otpus" // async await style const plainText = 'this is a secret message.' const encPack = await encrypt(plainText, 'passPhrase', 10000) const decodeMessage = await decrypt(encPack, 'passPhrase') console.log('decrypted:', decodeMessage) const plainData = Buffer.alloc(100 * 2 ** 20) const encData = await encrypt(plainData, 'passPhrase', 10000) const decData = await decrypt(encData, 'passPhrase') const some = decData.slice(0, 16) console.log('decrypted: some:', some) console.log('decrypted: byteLength:', decData.byteLength) const key = 'key' const strData = 'hello world' // promise chaining // string data will return string data. encrypt(strData, key) .then(secretPack => { console.log('secretPack', secretPack.byteLength) return decrypt(secretPack, key) }) .then(data => { console.log( 'typeof data:', typeof data) // string console.log('decoded string message: ', data) }) // Uint8Array data will return Uint8Array data. const binaryData = Uint8Array.from([1, 2, 3, 4]) encrypt(binaryData, key) .then(secretPack => { console.log('secretPack', secretPack.byteLength) return decrypt(secretPack, key) }) .then(data => { console.log('instanceof Uint8Array:', data instanceof Uint8Array ) //true console.log('decoded binary data: ', data) }) ``` ### Error catch For many reason encryption process could be fail. You should use try, catch statements. ```js // await style try{ const secretPack = await encrypt('plain text','key') const decoded = await decrypt( secretPack,'key') console.log('decoded:', decoded) }catch(error){ console.log(error) } // Promise style encrypt('plain text','key') .then( secretPack => decrypt(secretPack, 'key' ) ) .then( decoded => console.log( decoded )) .catch( error=>{ console.log(error) }) ``` ### Handling buffer encrypt() function generate a buffer( meta-buffer-pack). You can transform data format. ```js import{ encrypt, decrypt, MBP } from 'otpus' const secretPack = await encrypt('data','key') // secretPack is Buffer ( subclass of Uint8Array) console.log( secretPack instanceof Uint8Array ) // true // case. If you need base64 string data. const base64Pack = secretPack.toString('base64') console.log( 'base64 data:', base64Pack ) /* base64 data: F59Pyfq8EOSVSprdvbI2U+46JGtpcQ0sa3W2YtGRBxdJTdT41GY6a+FfBIvT4byChSu1KTGP1PJXewTidfeN1dgQh2IAe+eB2f/Df64kBXdr7HcvMFbawu2tOuUzUsonluKK25J5ilUtpuF2qszf9DEwMDAwW1siZW5jRGF0YSIsIkIiLDAsODRdLFsiaXYiLCJCIiw4NCwxMl0sWyJzYWx0IiwiQiIsOTYsMTZdLFsiaXRlcmF0aW9ucyIsIk4iLDExMiw1XV0AUw== */ // secretPack is meta-buffer-pack of MBP. // You can use MBP.unpack() to check public information like iv , salt. cosnt secretObject = MBP.unpack( secretPack ) console.log( 'salt:', sercretObject.salt ) // 16 bytes. random values. // salt: <Buffer 96 e2 8a db 92 79 8a 55 2d a6 e1 76 aa cc df f4> ``` ### Examples - NodeJs: please check test, testing directory. - Browser: example directory. ### Online demo - [https://remocons.github.io/otpus/example/]( https://remocons.github.io/otpus/example/) ### license [MIT](LICENSE)