@emartech/easy-crypto
Advanced tools
Comparing version 1.2.1 to 2.0.0
'use strict'; | ||
let crypto = require('crypto'); | ||
const crypto = require('crypto'); | ||
@@ -26,63 +26,57 @@ const DEFAULT_PASSWORD_SALT_SIZE_IN_BYTES = 12; | ||
*encryptAsync(password, plaintext) { | ||
let parameters = yield this._buildEncryptParameters(password, plaintext); | ||
encrypt(password, plaintext) { | ||
return this._buildEncryptParameters(password, plaintext) | ||
.then(parameters => this._encryptWithParameters(parameters)); | ||
} | ||
let encrypted = this._encryptRaw(parameters); | ||
return Buffer.concat([parameters.passwordSalt, encrypted]).toString(CIPHERTEXT_ENCODING); | ||
decrypt(password, ciphertext) { | ||
return this._buildDecryptParameters(password, ciphertext) | ||
.then(parameters => this._decryptWithParameters(parameters)); | ||
} | ||
*decryptAsync(password, ciphertext) { | ||
let parameters = yield this._buildDencryptParameters(password, ciphertext); | ||
let decrypted = this._decryptRaw(parameters); | ||
return decrypted.toString(PLAINTEXT_ENCODING); | ||
_buildEncryptParameters(password, plaintext) { | ||
return this._getRandomParameters() | ||
.then(randomParameters => this._generateKeyFromPassword(password, randomParameters.passwordSalt).then(key => { | ||
const encryptObject = this._createEncryptKeyObject(plaintext, randomParameters, key); | ||
return Promise.resolve(encryptObject); | ||
})); | ||
} | ||
_validateNumber(parameter, name) { | ||
if (typeof parameter !== 'number') { | ||
throw new TypeError(name + ' must be a number.'); | ||
_encryptWithParameters(parameters) { | ||
try { | ||
const encrypted = this._encryptRaw(parameters); | ||
const encodedCiphertext = Buffer.concat([parameters.passwordSalt, encrypted]).toString(CIPHERTEXT_ENCODING); | ||
return Promise.resolve(encodedCiphertext); | ||
} catch(ex) { | ||
return Promise.reject(ex); | ||
} | ||
} | ||
*_buildEncryptParameters(password, plaintext) { | ||
let randomParameters = yield this._getRandomParameters(); | ||
let key = yield this._generateKeyFromPasswordAsync(password, randomParameters.passwordSalt); | ||
let rawPlaintext = new Buffer(plaintext, PLAINTEXT_ENCODING); | ||
return { | ||
passwordSalt: randomParameters.passwordSalt, | ||
iv: randomParameters.iv, | ||
key: key, | ||
rawPlaintext: rawPlaintext | ||
}; | ||
_encryptRaw(encryptionParameters) { | ||
const cipher = crypto.createCipheriv(ENCRYPTION_MODE, encryptionParameters.key, encryptionParameters.iv); | ||
const encrypted = cipher.update(encryptionParameters.rawPlaintext); | ||
return Buffer.concat([encryptionParameters.iv, encrypted, cipher.final(), cipher.getAuthTag()]); | ||
} | ||
*_buildDencryptParameters(password, ciphertext) { | ||
let rawCiphertext = new Buffer(ciphertext, CIPHERTEXT_ENCODING); | ||
let slicedRawData = this._sliceCiphertext(rawCiphertext); | ||
let key = yield this._generateKeyFromPasswordAsync(password, slicedRawData.passwordSalt); | ||
return { | ||
iv: slicedRawData.iv, | ||
encrypted: slicedRawData.encrypted, | ||
tag: slicedRawData.tag, | ||
key: key | ||
}; | ||
_buildDecryptParameters(password, ciphertext) { | ||
const rawCiphertext = new Buffer(ciphertext, CIPHERTEXT_ENCODING); | ||
const slicedRawData = this._sliceCiphertext(rawCiphertext); | ||
return this._generateKeyFromPassword(password, slicedRawData.passwordSalt) | ||
.then(key => this._createDecryptKeyObject(slicedRawData, key)); | ||
} | ||
_encryptRaw(encryptionParameters) { | ||
let cipher = crypto.createCipheriv(ENCRYPTION_MODE, encryptionParameters.key, encryptionParameters.iv); | ||
let encrypted = cipher.update(encryptionParameters.rawPlaintext); | ||
return Buffer.concat([encryptionParameters.iv, encrypted, cipher.final(), cipher.getAuthTag()]); | ||
_decryptWithParameters(parameters){ | ||
try { | ||
const decrypted = this._decryptRaw(parameters); | ||
return Promise.resolve(decrypted.toString(PLAINTEXT_ENCODING)); | ||
} catch(ex) { | ||
return Promise.reject(ex); | ||
} | ||
} | ||
_decryptRaw(decryptionParameters) { | ||
let decipher = crypto.createDecipheriv(ENCRYPTION_MODE, decryptionParameters.key, decryptionParameters.iv); | ||
const decipher = crypto.createDecipheriv(ENCRYPTION_MODE, decryptionParameters.key, decryptionParameters.iv); | ||
decipher.setAuthTag(decryptionParameters.tag); | ||
let decrypted = decipher.update(decryptionParameters.encrypted); | ||
const decrypted = decipher.update(decryptionParameters.encrypted); | ||
@@ -92,18 +86,57 @@ return Buffer.concat([decrypted, decipher.final()]); | ||
*_getRandomParameters() { | ||
let randomBytes = yield this._randomBytesAsync(IV_SIZE_IN_BYTES + this._passwordSaltSize); | ||
let passwordSalt = randomBytes.slice(0, this._passwordSaltSize); | ||
let iv = randomBytes.slice(this._passwordSaltSize, this._passwordSaltSize + IV_SIZE_IN_BYTES); | ||
_getRandomParameters() { | ||
return this._getRandomBytes(IV_SIZE_IN_BYTES + this._passwordSaltSize) | ||
.then(randomBytes => this._splitRandomBytes(randomBytes)); | ||
} | ||
return { passwordSalt: passwordSalt, iv: iv }; | ||
_getRandomBytes(length) { | ||
return new Promise((resolve, reject) => { | ||
crypto.randomBytes(length, (err, bytes) => { | ||
if (err) return reject(err); | ||
return resolve(bytes); | ||
}); | ||
}); | ||
} | ||
_splitRandomBytes(randomBytes) { | ||
const passwordSalt = randomBytes.slice(0, this._passwordSaltSize); | ||
const iv = randomBytes.slice(this._passwordSaltSize, this._passwordSaltSize + IV_SIZE_IN_BYTES); | ||
return Promise.resolve({passwordSalt, iv}); | ||
} | ||
_generateKeyFromPassword(password, passwordSalt) { | ||
const keyFromPassword = (resolve, reject) => crypto.pbkdf2( | ||
password, | ||
passwordSalt, | ||
this._iterationCount, | ||
KEY_SIZE_IN_BYTES, | ||
HMAC_MODE, | ||
(err, key) => { | ||
if (err) return reject(err); | ||
return resolve(key); | ||
} | ||
); | ||
return new Promise(keyFromPassword); | ||
} | ||
_createEncryptKeyObject(plaintext, randomParameters, key) { | ||
const iv = randomParameters.iv; | ||
const passwordSalt = randomParameters.passwordSalt; | ||
const rawPlaintext = new Buffer(plaintext, PLAINTEXT_ENCODING); | ||
return { iv, passwordSalt, rawPlaintext, key }; | ||
} | ||
_createDecryptKeyObject(slicedRawData, key){ | ||
const iv = slicedRawData.iv; | ||
const encrypted = slicedRawData.encrypted; | ||
const tag = slicedRawData.tag; | ||
const keyObject = { iv, encrypted, tag, key }; | ||
return Promise.resolve(keyObject) | ||
} | ||
_sliceCiphertext(ciphertext) { | ||
let minimumCiphertextSize = this._passwordSaltSize + IV_SIZE_IN_BYTES + AUTH_TAG_LENGTH_IN_BYTES + 1; | ||
if (minimumCiphertextSize > ciphertext.length) { | ||
throw new Error('Ciphertext must be at least ' + minimumCiphertextSize + ' bytes long.'); | ||
} | ||
this._validateCiphertextMinLength(ciphertext); | ||
let ivEndIndex = this._passwordSaltSize + IV_SIZE_IN_BYTES; | ||
let authTagStartIndex = ciphertext.length - AUTH_TAG_LENGTH_IN_BYTES; | ||
const ivEndIndex = this._passwordSaltSize + IV_SIZE_IN_BYTES; | ||
const authTagStartIndex = ciphertext.length - AUTH_TAG_LENGTH_IN_BYTES; | ||
@@ -118,20 +151,13 @@ return { | ||
_randomBytesAsync(length) { | ||
return new Promise((resolve, reject) => { | ||
crypto.randomBytes(length, (err, bytes) => { | ||
if (err) return reject(err); | ||
return resolve(bytes); | ||
}); | ||
}); | ||
_validateCiphertextMinLength(ciphertext) { | ||
const minimumCiphertextSize = this._passwordSaltSize + IV_SIZE_IN_BYTES + AUTH_TAG_LENGTH_IN_BYTES + 1; | ||
if (minimumCiphertextSize > ciphertext.length) { | ||
throw new Error('Ciphertext must be at least ' + minimumCiphertextSize + ' bytes long.'); | ||
} | ||
} | ||
_generateKeyFromPasswordAsync(password, passwordSalt) { | ||
return new Promise((resolve, reject) => { | ||
crypto.pbkdf2(password, passwordSalt, this._iterationCount, KEY_SIZE_IN_BYTES, HMAC_MODE, (err, key) => { | ||
if (err) return reject(err); | ||
return resolve(key); | ||
}); | ||
}); | ||
_validateNumber(parameter, name) { | ||
if (typeof parameter !== 'number') { | ||
throw new TypeError(name + ' must be a number.'); | ||
} | ||
} | ||
@@ -138,0 +164,0 @@ |
@@ -17,5 +17,5 @@ 'use strict'; | ||
let encrypted = yield defaultInstance.encryptAsync(password, randomData); | ||
let encrypted = yield defaultInstance.encrypt(password, randomData); | ||
let decrypted = yield defaultInstance.decryptAsync(password, encrypted); | ||
let decrypted = yield defaultInstance.decrypt(password, encrypted); | ||
@@ -64,3 +64,3 @@ expect(randomData).to.eql(decrypted); | ||
describe('#encryptAsync', function() { | ||
describe('#encrypt', function() { | ||
@@ -79,3 +79,3 @@ beforeEach(function() { | ||
let encrypted = new Buffer(yield defaultInstance.encryptAsync('pw', 'data'), 'base64'); | ||
let encrypted = new Buffer(yield defaultInstance.encrypt('pw', 'data'), 'base64'); | ||
@@ -96,3 +96,3 @@ expect(encrypted.slice(0, 12).toString('utf-8')).to.eql('AAAAAAAAAAAA'); | ||
describe('#decryptAsync', function() { | ||
describe('#decrypt', function() { | ||
@@ -108,3 +108,3 @@ [ | ||
let randomData = crypto.randomBytes(1024).toString('hex'); | ||
let encrypted = yield defaultInstance.encryptAsync(password, randomData); | ||
let encrypted = yield defaultInstance.encrypt(password, randomData); | ||
let tamperedEncrypted = tamperWithCiphertext(encrypted, testCase.index); | ||
@@ -114,3 +114,3 @@ let expectedError; | ||
try { | ||
yield defaultInstance.decryptAsync(password, tamperedEncrypted); | ||
yield defaultInstance.decrypt(password, tamperedEncrypted); | ||
} catch (ex) { | ||
@@ -130,3 +130,3 @@ expectedError = ex; | ||
try { | ||
yield defaultInstance.decryptAsync('does not matter', shortData); | ||
yield defaultInstance.decrypt('does not matter', shortData); | ||
} catch (ex) { | ||
@@ -143,7 +143,7 @@ expectedError = ex; | ||
let randomData = crypto.randomBytes(1024).toString('hex'); | ||
let encrypted = yield defaultInstance.encryptAsync(password, randomData); | ||
let encrypted = yield defaultInstance.encrypt(password, randomData); | ||
let expectedError; | ||
try { | ||
yield defaultInstance.decryptAsync(password + 'X', encrypted); | ||
yield defaultInstance.decrypt(password + 'X', encrypted); | ||
} catch (ex) { | ||
@@ -150,0 +150,0 @@ expectedError = ex; |
@@ -40,3 +40,3 @@ { | ||
}, | ||
"version": "1.2.1" | ||
"version": "2.0.0" | ||
} |
17039
260
6