1password
Version:
Work With 1Password Keychains
363 lines (307 loc) • 8.57 kB
JavaScript
'use strict';
/**
* A collection of cryptographic functions.
* The important ones (AES, SHA, HMAC ...) are all wrappers for the openssl
* library built into Node.
*/
var nodeCrypto = require('crypto');
var BLOCKSIZE = 16;
var Crypto = {
/**
* crypto.encrypt
*
* Encrypt data using AES256 in CBC mode.
*
* - plaintext {Buffer} : The data to encrypt.
* - key {String|Buffer} : The key to encrypt with.
* - iv {String|Buffer} : The initialization vector.
* - [encoding] {string} : The format to return the encrypted data in.
* > buffer
*/
encrypt: function(plaintext, key, iv, encoding) {
iv = this.toBuffer(iv);
key = this.toBuffer(key);
var cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv);
cipher.setAutoPadding(false);
var buffer = this.concat([cipher.update(plaintext), cipher.final()]);
if (encoding !== undefined) {
return buffer.toString(encoding);
}
return buffer;
},
/**
* crypto.decrypt
*
* Decrypt encrypted data using AES256 in CBC mode
*
* - ciphertext {String|Buffer} : The data to decipher. Length must be a
* multiple of the blocksize.
* - key {String|Buffer} : The key to decipher with.
* - iv {String|Buffer} : The initialization vector.
* - [encoding] {String} : The format to return the decrypted contents in.
* > buffer
*/
decrypt: function(ciphertext, key, iv, encoding) {
iv = this.toBuffer(iv);
key = this.toBuffer(key);
ciphertext = this.toBuffer(ciphertext);
var cipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv);
cipher.setAutoPadding(false);
var buffer = this.concat([cipher.update(ciphertext), cipher.final()]);
if (encoding !== undefined) {
return buffer.toString(encoding);
}
return buffer;
},
/**
* crypto.pbkdf2
*
* Generate keys from password using PKDF2-HMAC-SHA512.
*
* - password {String|Buffer} : The password.
* - salt {String|Buffer} : The salt.
* - [iterations=10000] {Number} : The numbers of iterations.
* - [keysize=512] {Number} : The SHA algorithm to use.
* > string
*/
pbkdf2: require('./crypto_pbkdf2'),
/**
* crypto.hmac
*
* Cryptographically hash data using HMAC.
*
* - data {String|Buffer} : The data to be hashed.
* - key {String|Buffer} : The key to use with HMAC.
* - keysize {Number} : The type of hash to use, e.g. 256 or 512.
* - [encoding] {String} : Data encoding to return as. If left unspecified,
* it will return as a buffer.
* > buffer
*/
hmac: function(data, key, keysize, encoding) {
data = this.toBuffer(data);
key = this.toBuffer(key);
var mode = 'sha' + keysize;
var hmac = nodeCrypto.createHmac(mode, key);
hmac.update(data);
if (encoding !== undefined) {
return hmac.digest(encoding);
}
return hmac.digest();
},
/**
* crypto.hash
*
* Create a hash digest of data.
*
* - data {string|buffer} : The data to hash.
* - keysize {number} : The type of hash to use, e.g. 256 or 512.
* - [encoding] {string} : Data encoding to return as. If left unspecified,
* it will return as a buffer.
* > buffer
*/
hash: function(data, keysize, encoding) {
data = this.toBuffer(data);
var mode = 'sha' + keysize;
var hash = nodeCrypto.createHash(mode);
hash.update(data);
if (encoding !== undefined) {
return hash.digest(encoding);
}
return hash.digest();
},
/**
* crypto.pad
*
* Prepend padding to data to make it fill the blocksize.
*
* - data {Buffer} : The data to pad.
* > buffer
*/
pad: function(data) {
var paddingLength = BLOCKSIZE - (data.length % BLOCKSIZE);
var padding = this.randomBytes(paddingLength);
return Buffer.concat([padding, data]);
},
/**
* crypto.unpad
*
* Remove padding from text.
*
* - plaintextLength {number} : The length of the plaintext in bytes.
* - data {buffer} : The data to remove the padding from. Can be a hex string
* or a buffer.
* > buffer
*/
unpad: function(plaintextLength, data) {
return data.slice(-plaintextLength);
},
/**
* crypto.randomBytes
*
* Generates cryptographically strong pseudo-random data.
*
* - length {Number} : How many bytes of data you need.
* > buffer
*/
randomBytes: function(length) {
return nodeCrypto.randomBytes(length);
},
/**
* crypto.randomValue
*
* Generate a cryptographically strong pseudo-random number.
*
* Very similar to Math.random() except it's more random.
* > float - between 0 and 1
*/
randomValue: function() {
var bytes = this.randomBytes(4).toString('hex');
var decimal = parseInt(bytes, 16);
return decimal * Math.pow(2, -32);
},
/**
* crypto.toBuffer
*
* Convert data to a Buffer
*
* - data {String|Buffer} : The string to be converted.
* - [encoding=hex] {String} : The format of the data to convert from.
* > buffer
*/
toBuffer: function(data, encoding) {
if (encoding === undefined) { encoding = 'hex'; }
if (data instanceof Buffer) { return data; }
return new Buffer(data, encoding);
},
/**
* crypto.toHex
*
* Convert data to hex.
*
* - data {String|Buffer} : The data to be converted.
* > string
*/
toHex: function(data) {
if (data instanceof Buffer) {
return data.toString('hex');
}
return data;
},
/**
* crypto.fromBase64
*
* Convert base64 to Buffer.
*
* - data {String} : A base64 encoded string.
* > buffer
*/
fromBase64: function(data) {
return new Buffer(data, 'base64');
},
/**
* crypto.concat
*
* Join an array of buffers together.
*
* - buffers {Array} : An array of buffers.
* > buffer
*/
concat: function(buffers) {
return Buffer.concat(buffers);
},
/**
* crypto.parseLittleEndian
*
* Parse a litte endian number. Original JS version by Jim Rogers.
* http://www.jimandkatrin.com/CodeBlog/post/Parse-a-little-endian.aspx
*
* - hex {buffer or string} : The little endian number.
* > number - the little endian converted to a number.
*/
parseLittleEndian: function(hex) {
hex = this.toHex(hex);
var result = 0;
var pow = 0;
var i = 0;
var len = hex.length - 1;
while (i < len) {
result += parseInt(hex.slice(i++, +(i++) + 1 || 9e9), 16) * Math.pow(2, pow);
pow += 8;
}
return result;
},
/**
* crypto.litteEndian
*
* Convert an integer into a little endian.
*
* - number {Number} number The integer you want to convert.
* - [pad=true] {Boolean} : Pad the little endian with zeroes.
* > string - encoded as hex
*/
littleEndian: function(number, pad) {
if (pad === undefined) pad = true;
var power = Math.floor((Math.log(number) / Math.LN2) / 8) * 8;
var multiplier = Math.pow(2, power);
var value = Math.floor(number / multiplier);
var remainder = number % multiplier;
var endian = "";
if (remainder > 255) {
endian += this.stringifyLittleEndian(remainder, false);
} else if (power !== 0) {
endian += this.dec2hex(remainder);
}
endian += this.dec2hex(value);
if (pad) {
var padding = 16 - endian.length;
for (var i = 0; i < padding; i += 1) endian += "0";
}
return endian;
},
/**
* crypto.dec2hex
*
* Turn a decimal into a hexadecimal.
*
* - dec {Number} : The decimal.
* > string
*/
dec2hex: function(dec) {
var hex;
hex = dec.toString(16);
if (hex.length < 2) {
hex = '0' + hex;
}
return hex;
},
/**
* crypto.bin2hex
*
* Convert a binary string into a hex string.
*
* - binary {String} : The binary encoded string.
* > string
*/
bin2hex: function(binary) {
var hex = "";
for (var i = 0, len = binary.length; i < len; i += 1) {
var char = binary[i];
hex += char.charCodeAt(0).toString(16).replace(/^([\dA-F])$/i, '0$1');
}
return hex;
},
/**
* crypto.generateUuid
*
* Generate a uuid.
*
* - param {Number} [length=32] The length of the UUID.
* > string
*/
generateUuid: function(length) {
if (! length) { length = 32; }
length /= 2;
return this.randomBytes(length).toString('hex').toUpperCase();
}
};
module.exports = Crypto;