123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- 'use strict'
-
- class Crypto {
- init(options) {
- this.crypto = require('crypto')
- this.algorithm = options.algorithm || 'aes-256-gcm'
- this.hashing = options.hashing || 'sha512'
- this.encodeas = options.encodeas || 'hex'
- this.iv_size = options.iv_size || 16
- this.at_size = options.at_size || 16
- this.key_size = options.key_size || 32
- this.secret = this._deriveKey(options.secret) || false
- }
-
- set(plaintext) {
- const iv = this.crypto.randomBytes(this.iv_size).toString(this.encodeas)
- const aad = this._digest(
- iv + this.secret,
- JSON.stringify(plaintext),
- this.hashing,
- this.encodeas
- )
- const ct = this._encrypt(
- this.secret,
- JSON.stringify(plaintext),
- this.algorithm,
- this.encodeas,
- iv,
- aad
- )
- const hmac = this._digest(this.secret, ct.ct, this.hashing, this.encodeas)
-
- const obj = JSON.stringify({
- hmac,
- ct: ct.ct,
- at: ct.at,
- aad,
- iv,
- })
-
- return obj
- }
-
- get(ciphertext) {
- let ct
-
- if (ciphertext) {
- try {
- ct = JSON.parse(ciphertext)
- } catch (err) {
- ct = ciphertext
- }
- }
-
- const hmac = this._digest(this.secret, ct.ct, this.hashing, this.encodeas)
-
- if (hmac !== ct.hmac) {
- throw new Error('Encrypted session was tampered with!')
- }
-
- if (ct.at) {
- ct.at = Buffer.from(ct.at)
- }
-
- const pt = this._decrypt(
- this.secret,
- ct.ct,
- this.algorithm,
- this.encodeas,
- ct.iv,
- ct.at,
- ct.aad
- )
-
- return pt
- }
-
- _digest(key, obj, hashing, encodeas) {
- const hmac = this.crypto.createHmac(this.hashing, key)
- hmac.setEncoding(encodeas)
- hmac.write(obj)
- hmac.end()
- return hmac.read().toString(encodeas)
- }
-
- _encrypt(key, pt, algo, encodeas, iv, aad) {
- const cipher = this.crypto.createCipheriv(algo, key, iv, {
- authTagLength: this.at_size,
- })
- let ct
- let at
-
- if (aad) {
- try {
- cipher.setAAD(Buffer.from(aad), {
- plaintextLength: Buffer.byteLength(pt),
- })
- } catch (err) {
- throw err
- }
- }
-
- ct = cipher.update(pt, 'utf8', encodeas)
- ct += cipher.final(encodeas)
-
- try {
- at = cipher.getAuthTag()
- } catch (err) {
- throw err
- }
-
- return at ? { ct, at } : { ct }
- }
-
- _decrypt(key, ct, algo, encodeas, iv, at, aad) {
- const cipher = this.crypto.createDecipheriv(algo, key, iv)
- let pt
-
- if (at) {
- try {
- cipher.setAuthTag(Buffer.from(at))
- } catch (err) {
- throw err
- }
- }
-
- if (aad) {
- try {
- cipher.setAAD(Buffer.from(aad), {
- plaintextLength: Buffer.byteLength(ct),
- })
- } catch (err) {
- throw err
- }
- }
-
- pt = cipher.update(ct, encodeas, 'utf8')
- pt += cipher.final('utf8')
-
- return pt
- }
-
- _deriveKey(secret) {
- const hash = this.crypto.createHash(this.hashing)
- hash.update(secret)
- const salt = hash.digest(this.encodeas).substr(0, 16)
-
- const key = this.crypto.pbkdf2Sync(secret, salt, 10000, 64, this.hashing)
-
- return key.toString(this.encodeas).substr(0, this.key_size)
- }
- }
-
- module.exports = new Crypto()
|