Add file bucket encryption using fileKey (#6765)
* add fileKey encryption to GridFSBucketStorageAdapter * remove fileAdapter options from test spec * ensure promise doesn't fall through in getFileData * switch secretKey to fileKey
This commit is contained in:
@@ -10,16 +10,30 @@
|
||||
import { MongoClient, GridFSBucket, Db } from 'mongodb';
|
||||
import { FilesAdapter, validateFilename } from './FilesAdapter';
|
||||
import defaults from '../../defaults';
|
||||
const crypto = require('crypto');
|
||||
|
||||
export class GridFSBucketAdapter extends FilesAdapter {
|
||||
_databaseURI: string;
|
||||
_connectionPromise: Promise<Db>;
|
||||
_mongoOptions: Object;
|
||||
_algorithm: string;
|
||||
|
||||
constructor(mongoDatabaseURI = defaults.DefaultMongoURI, mongoOptions = {}) {
|
||||
constructor(
|
||||
mongoDatabaseURI = defaults.DefaultMongoURI,
|
||||
mongoOptions = {},
|
||||
fileKey = undefined
|
||||
) {
|
||||
super();
|
||||
this._databaseURI = mongoDatabaseURI;
|
||||
|
||||
this._algorithm = 'aes-256-gcm';
|
||||
this._fileKey =
|
||||
fileKey !== undefined
|
||||
? crypto
|
||||
.createHash('sha256')
|
||||
.update(String(fileKey))
|
||||
.digest('base64')
|
||||
.substr(0, 32)
|
||||
: null;
|
||||
const defaultMongoOptions = {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
@@ -51,7 +65,19 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
const stream = await bucket.openUploadStream(filename, {
|
||||
metadata: options.metadata,
|
||||
});
|
||||
await stream.write(data);
|
||||
if (this._fileKey !== null) {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv(this._algorithm, this._fileKey, iv);
|
||||
const encryptedResult = Buffer.concat([
|
||||
cipher.update(data),
|
||||
cipher.final(),
|
||||
iv,
|
||||
cipher.getAuthTag(),
|
||||
]);
|
||||
await stream.write(encryptedResult);
|
||||
} else {
|
||||
await stream.write(data);
|
||||
}
|
||||
stream.end();
|
||||
return new Promise((resolve, reject) => {
|
||||
stream.on('finish', resolve);
|
||||
@@ -82,7 +108,24 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
chunks.push(data);
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
const data = Buffer.concat(chunks);
|
||||
if (this._fileKey !== null) {
|
||||
const authTagLocation = data.length - 16;
|
||||
const ivLocation = data.length - 32;
|
||||
const authTag = data.slice(authTagLocation);
|
||||
const iv = data.slice(ivLocation, authTagLocation);
|
||||
const encrypted = data.slice(0, ivLocation);
|
||||
const decipher = crypto.createDecipheriv(
|
||||
this._algorithm,
|
||||
this._fileKey,
|
||||
iv
|
||||
);
|
||||
decipher.setAuthTag(authTag);
|
||||
return resolve(
|
||||
Buffer.concat([decipher.update(encrypted), decipher.final()])
|
||||
);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
stream.on('error', (err) => {
|
||||
reject(err);
|
||||
|
||||
Reference in New Issue
Block a user