Generate random salt for volume headers and check volumes for such header
This commit is contained in:
+24
-11
@@ -5,30 +5,43 @@ var crypto = require('crypto');
|
||||
// This code is taken from https://github.com/fabdrol/node-aes-helper
|
||||
module.exports = {
|
||||
algorithm: 'AES-256-CBC',
|
||||
salt: 'RgA{[A0+#I@]CK|U)}>yv9Y58CkJp3}UOV#xK,Lz@8@VE>?9fR;K]isV*+qX10o4',
|
||||
|
||||
key: function (password) {
|
||||
var key = this.salt + password;
|
||||
key: function (password, salt) {
|
||||
var key = salt.toString('utf8') + password;
|
||||
var hash = crypto.createHash('sha1');
|
||||
|
||||
hash.update(key, 'utf8');
|
||||
return hash.digest('hex');
|
||||
},
|
||||
|
||||
encrypt: function (plain, password) {
|
||||
var key = this.key(password);
|
||||
encrypt: function (plain, password, salt) {
|
||||
var key = this.key(password, salt);
|
||||
var cipher = crypto.createCipher(this.algorithm, key);
|
||||
var crypted = cipher.update(plain, 'utf8', 'hex');
|
||||
crypted += cipher.final('hex');
|
||||
var crypted;
|
||||
|
||||
try {
|
||||
crypted = cipher.update(plain, 'utf8', 'hex');
|
||||
crypted += cipher.final('hex');
|
||||
} catch (e) {
|
||||
console.error('Encryption error:', e);
|
||||
crypted = '';
|
||||
}
|
||||
|
||||
return crypted;
|
||||
},
|
||||
|
||||
decrypt: function (crypted, password) {
|
||||
var key = this.key(password);
|
||||
decrypt: function (crypted, password, salt) {
|
||||
var key = this.key(password, salt);
|
||||
var decipher = crypto.createDecipher(this.algorithm, key);
|
||||
var decoded = decipher.update(crypted, 'hex', 'utf8');
|
||||
decoded += decipher.final('utf8');
|
||||
var decoded;
|
||||
|
||||
try {
|
||||
decoded = decipher.update(crypted, 'hex', 'utf8');
|
||||
decoded += decipher.final('utf8');
|
||||
} catch (e) {
|
||||
console.error('Decryption error:', e);
|
||||
decoded = '';
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ Server.prototype._requirePassword = function (req, res, next) {
|
||||
return next(new HttpError(401, 'Wrong password entered'));
|
||||
}
|
||||
|
||||
// add password to the request's user object for further use
|
||||
req.user.password = req.body.password;
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
+29
-12
@@ -24,6 +24,8 @@ exports = module.exports = {
|
||||
};
|
||||
|
||||
var REPO_SUBFOLDER = 'repo';
|
||||
var VOLUME_META_FILENAME = '.meta';
|
||||
var CRYPTO_SALT_SIZE = 64; // 512-bit salt
|
||||
|
||||
// http://dustinsenos.com/articles/customErrorsInNode
|
||||
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
|
||||
@@ -77,7 +79,7 @@ Volume.prototype._resolveVolumeMountPoint = function() {
|
||||
};
|
||||
|
||||
Volume.prototype._initMetaDatabase = function () {
|
||||
this.meta = new db.Table(this.dataPath + '/.meta', {
|
||||
this.meta = new db.Table(path.join(this.dataPath, VOLUME_META_FILENAME), {
|
||||
username: { type: 'String', hashKey: true },
|
||||
passwordCypher: { type: 'String', priv: true }
|
||||
});
|
||||
@@ -122,7 +124,8 @@ Volume.prototype.open = function (username, password, callback) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
var volPassword = aes.decrypt(record.passwordCypher, password);
|
||||
var saltBuffer = new Buffer(record.salt, 'hex');
|
||||
var volPassword = aes.decrypt(record.passwordCypher, password, saltBuffer);
|
||||
|
||||
that.encfs.mount(volPassword, function (error, result) {
|
||||
if (error) {
|
||||
@@ -263,24 +266,32 @@ Volume.prototype.listFiles = function (directory, callback) {
|
||||
};
|
||||
|
||||
Volume.prototype.addUser = function (user, password, callback) {
|
||||
var that = this;
|
||||
|
||||
if (!this.meta) {
|
||||
debug('Invalid volume "' + this.name + '". Misses the meta database.');
|
||||
return callback(new VolumeError(null, VolumeError.META_MISSING));
|
||||
}
|
||||
|
||||
// TODO salt is currently fixed in aes-helper module - Johannes
|
||||
var record = {
|
||||
username: user.username,
|
||||
passwordCypher: aes.encrypt(password, user.password)
|
||||
};
|
||||
|
||||
this.meta.put(record, function (error) {
|
||||
crypto.randomBytes(CRYPTO_SALT_SIZE, function (error, salt) {
|
||||
if (error) {
|
||||
debug('Unable to add user to meta db. ' + safe.JSON.stringify(error));
|
||||
return callback(error);
|
||||
return callback(new VolumeError('Failed to generate random bytes', VolumeError.INTERNAL_ERROR));
|
||||
}
|
||||
|
||||
return callback(null, record);
|
||||
var record = {
|
||||
username: user.username,
|
||||
salt: salt.toString('hex'),
|
||||
passwordCypher: aes.encrypt(password, user.password, salt),
|
||||
};
|
||||
|
||||
that.meta.put(record, function (error) {
|
||||
if (error) {
|
||||
debug('Unable to add user to meta db. ' + safe.JSON.stringify(error));
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
return callback(null, record);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -407,6 +418,12 @@ function getVolume(name, username, config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if that volume has a meta information file, if not it is not created properly or broken
|
||||
if (!safe.fs.existsSync(path.join(vol.dataPath, VOLUME_META_FILENAME))) {
|
||||
debug('Volume "' + name + '" for user "' + username + '" does not have meta information, it is possibly broken.');
|
||||
return null;
|
||||
}
|
||||
|
||||
vol.repo = new Repo(path.join(vol.mountPoint, REPO_SUBFOLDER), vol.tmpPath);
|
||||
|
||||
return vol;
|
||||
|
||||
Reference in New Issue
Block a user