node-aescrypt
Version:
A node implementation of the AES Crypt <https://www.aescrypt.com/> file encryption format.
149 lines • 12.1 kB
JavaScript
import { createCipheriv, randomBytes } from 'crypto';
import { Transform } from 'stream';
import { AESCRYPT_FILE_FORMAT_VERSION, getHMAC, getKey, NAME, toStream, VERSION, withStream, } from './util';
/**
* Encrypt a Buffer using the AES Crypt file format.
*
* Create a stream transformer that takes any [Readable stream](https://nodejs.org/api/stream.html)
* and passes on a Readable stream of the encrypted Buffer in the
* [AES Crypt file format](https://www.aescrypt.com/aes_file_format.html).
*/
export class Encrypt extends Transform {
constructor(password, options) {
super(options);
this.password = password;
this.cipher = null;
this.hmac = null;
this.contentLength = 0;
// Delay initialization.
}
// Create a small helper static method if you just want to encrypt a whole
// Buffer all at once.
// Note: There is a bit of duplication with the Decrypt version of this method.
static buffer(password, buffer) {
return new Promise((resolve, reject) => {
toStream(buffer)
.pipe(new Encrypt(password))
.pipe(withStream(contents => {
resolve(contents);
}))
.on('error', reject);
});
}
_transform(chunk, _, callback) {
// Okay, we have data. Let's initialize.
this._init();
// This is unnecessary, but makes tslint keep quiet.
if (this.cipher == null) {
return;
}
if (this.hmac == null) {
return;
}
// Track the file contents size.
this.contentLength += chunk.length;
// Encrypt this chunk and push it.
const encChunk = this.cipher.update(chunk);
this.push(encChunk);
// And add the encrypted cipher block to the signature.
this.hmac.update(encChunk);
callback();
}
_flush(callback) {
// Make sure we have initialized (even if it is an empty file).
this._init();
// This is unnecessary, but makes tslint keep quiet.
if (this.cipher == null) {
return;
}
if (this.hmac == null) {
return;
}
// Store the size of the last block and determin the padding.
const lenMod16 = this.contentLength % 16;
const padding = 16 - lenMod16;
// Encrypt and sign the padding.
const encChunk = this.cipher.update(Buffer.alloc(padding, padding));
this.push(encChunk);
this.hmac.update(encChunk);
// Push down the final encryption, size of the last content block and the signature.
this.push(this.cipher.final()); // This one should be unnecessary, as we are disabling the padding, but just in case.
this.push(Buffer.from([lenMod16]));
this.push(this.hmac.digest());
callback();
}
_init() {
if (this.cipher == null) {
this._pushFileHeader();
this._pushExtensions();
const credentials = this._getCredentials(this.password);
this._pushCredentials(credentials);
this.cipher = this._getCipher(credentials.encKey, credentials.encIV);
this.hmac = getHMAC(credentials.encKey);
delete this.password; // Don't need this anymore.
return true;
}
return false;
}
_pushFileHeader() {
const buff = Buffer.alloc(3 + 1 + 1);
buff.write('AES', 0);
buff.writeUInt8(AESCRYPT_FILE_FORMAT_VERSION, 3);
this.push(buff);
}
_pushExtensions() {
const extensions = {
CREATED_BY: NAME + ' ' + VERSION,
};
// Calculate the final length of the extensions.
const capacity = Object.keys(extensions).reduce((acc, k) => acc + 2 + k.length + 1 + extensions[k].length, 0) + // Extensions
(2 + 128) + // extension container
2; // end extensions
// Allocate a single buffer for all the extensions.
const buff = Buffer.alloc(capacity);
let len = 0;
Object.keys(extensions).forEach(k => {
len = buff.writeUInt16BE(k.length + 1 + extensions[k].length, len);
len += buff.write(k, len);
len += 1; // Delimiter
len += buff.write(extensions[k], len);
});
len = buff.writeUInt16BE(128, len);
// We don't need to actually "create" the extension container, as it is just
// 0x00s, and that is the default fill from Buffer.alloc().
this.push(buff);
}
_getCredentials(password) {
const credIV = randomBytes(16);
return {
credIV,
credKey: getKey(credIV, password),
encIV: randomBytes(16),
encKey: randomBytes(32),
};
}
_pushCredentials(credentials) {
const { credIV, credKey, encIV, encKey } = credentials;
// Encrypt our credentials.
const credCipher = this._getCipher(credKey, credIV);
const credBlock = Buffer.concat([
credCipher.update(encIV),
credCipher.update(encKey),
credCipher.final(),
]);
// Sign them.
const credHMAC = getHMAC(credKey)
.update(credBlock)
.digest();
// Than push them downstream.
this.push(credIV);
this.push(credBlock);
this.push(credHMAC);
}
_getCipher(key, iv) {
const encCipher = createCipheriv('aes-256-cbc', key, iv);
encCipher.setAutoPadding(false);
return encCipher;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5jcnlwdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvZW5jcnlwdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQVUsY0FBYyxFQUFRLFdBQVcsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNuRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ25DLE9BQU8sRUFDTCw0QkFBNEIsRUFDNUIsT0FBTyxFQUNQLE1BQU0sRUFDTixJQUFJLEVBQ0osUUFBUSxFQUVSLE9BQU8sRUFDUCxVQUFVLEdBQ1gsTUFBTSxRQUFRLENBQUM7QUFTaEI7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLE9BQVEsU0FBUSxTQUFTO0lBc0JwQyxZQUFZLFFBQWdCLEVBQUUsT0FBYTtRQUN6QyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztRQUN6QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQztRQUN2Qix3QkFBd0I7SUFDMUIsQ0FBQztJQTVCRCwwRUFBMEU7SUFDMUUsc0JBQXNCO0lBQ3RCLCtFQUErRTtJQUN4RSxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQWdCLEVBQUUsTUFBYztRQUNuRCxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3JDLFFBQVEsQ0FBQyxNQUFNLENBQUM7aUJBQ2IsSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO2lCQUMzQixJQUFJLENBQ0gsVUFBVSxDQUFDLFFBQVEsQ0FBQyxFQUFFO2dCQUNwQixPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDcEIsQ0FBQyxDQUFDLENBQ0g7aUJBQ0EsRUFBRSxDQUFD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