Skip to content

Commit

Permalink
Merge pull request #811 from techhead/use_buffers
Browse files Browse the repository at this point in the history
Allow optional use of Node Buffers.
  • Loading branch information
recrsn committed Jan 1, 2021
2 parents 63c8403 + f158e6e commit 2821c03
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 33 deletions.
26 changes: 13 additions & 13 deletions bcrypt.js
Expand Up @@ -83,16 +83,16 @@ module.exports.genSalt = function genSalt(rounds, minor, cb) {
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String|Buffer} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @return {String} hash
module.exports.hashSync = function hashSync(data, salt) {
if (data == null || salt == null) {
throw new Error('data and salt arguments required');
}

if (typeof data !== 'string' || (typeof salt !== 'string' && typeof salt !== 'number')) {
throw new Error('data must be a string and salt must either be a salt string or a number of rounds');
if (!(typeof data === 'string' || data instanceof Buffer) || (typeof salt !== 'string' && typeof salt !== 'number')) {
throw new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
}

if (typeof salt === 'number') {
Expand All @@ -103,21 +103,21 @@ module.exports.hashSync = function hashSync(data, salt) {
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String|Buffer} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @param {Function} cb callback(err, hash)
module.exports.hash = function hash(data, salt, cb) {
var error;

if (typeof data === 'function') {
error = new Error('data must be a string and salt must either be a salt string or a number of rounds');
error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
return process.nextTick(function() {
data(error);
});
}

if (typeof salt === 'function') {
error = new Error('data must be a string and salt must either be a salt string or a number of rounds');
error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
return process.nextTick(function() {
salt(error);
});
Expand All @@ -140,8 +140,8 @@ module.exports.hash = function hash(data, salt, cb) {
});
}

if (typeof data !== 'string' || (typeof salt !== 'string' && typeof salt !== 'number')) {
error = new Error('data must be a string and salt must either be a salt string or a number of rounds');
if (!(typeof data === 'string' || data instanceof Buffer) || (typeof salt !== 'string' && typeof salt !== 'number')) {
error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
return process.nextTick(function() {
cb(error);
});
Expand All @@ -158,23 +158,23 @@ module.exports.hash = function hash(data, salt, cb) {
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String|Buffer} data the data to hash and compare
/// @param {String} hash expected hash
/// @return {bool} true if hashed data matches hash
module.exports.compareSync = function compareSync(data, hash) {
if (data == null || hash == null) {
throw new Error('data and hash arguments required');
}

if (typeof data !== 'string' || typeof hash !== 'string') {
throw new Error('data and hash must be strings');
if (!(typeof data === 'string' || data instanceof Buffer) || typeof hash !== 'string') {
throw new Error('data must be a string or Buffer and hash must be a string');
}

return bindings.compare_sync(data, hash);
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String|Buffer} data the data to hash and compare
/// @param {String} hash expected hash
/// @param {Function} cb callback(err, matched) - matched is true if hashed data matches hash
module.exports.compare = function compare(data, hash, cb) {
Expand Down Expand Up @@ -211,7 +211,7 @@ module.exports.compare = function compare(data, hash, cb) {
});
}

if (typeof data !== 'string' || typeof hash !== 'string') {
if (!(typeof data === 'string' || data instanceof Buffer) || typeof hash !== 'string') {
error = new Error('data and hash must be strings');
return process.nextTick(function() {
cb(error);
Expand Down
45 changes: 26 additions & 19 deletions src/bcrypt_node.cc
Expand Up @@ -59,25 +59,22 @@ namespace {
return true;
}

char ToCharVersion(Napi::String str) {
std::string our_str = str.Utf8Value();
return our_str.c_str()[0];
inline char ToCharVersion(const std::string& str) {
return str[0];
}

/* SALT GENERATION */

class SaltAsyncWorker : public Napi::AsyncWorker {
public:
SaltAsyncWorker(Napi::Function& callback, std::string seed, ssize_t rounds, char minor_ver)
SaltAsyncWorker(const Napi::Function& callback, const std::string& seed, ssize_t rounds, char minor_ver)
: Napi::AsyncWorker(callback, "bcrypt:SaltAsyncWorker"), seed(seed), rounds(rounds), minor_ver(minor_ver) {
}

~SaltAsyncWorker() {}

void Execute() {
char salt[_SALT_LEN];
bcrypt_gensalt(minor_ver, rounds, (u_int8_t *)&seed[0], salt);
this->salt = std::string(salt);
}

void OnOK() {
Expand All @@ -87,9 +84,9 @@ namespace {

private:
std::string seed;
std::string salt;
ssize_t rounds;
char minor_ver;
char salt[_SALT_LEN];
};

Napi::Value GenerateSalt(const Napi::CallbackInfo& info) {
Expand Down Expand Up @@ -133,11 +130,15 @@ namespace {
return Napi::String::New(env, salt, strlen(salt));
}

inline std::string BufferToString(const Napi::Buffer<char> &buf) {
return std::string(buf.Data(), buf.Length());
}

/* ENCRYPT DATA - USED TO BE HASHPW */

class EncryptAsyncWorker : public Napi::AsyncWorker {
public:
EncryptAsyncWorker(Napi::Function& callback, std::string input, std::string salt)
EncryptAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& salt)
: Napi::AsyncWorker(callback, "bcrypt:EncryptAsyncWorker"), input(input), salt(salt) {
}

Expand All @@ -147,27 +148,27 @@ namespace {
if (!(ValidateSalt(salt.c_str()))) {
SetError("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
}
char bcrypted[_PASSWORD_LEN];
bcrypt(input.c_str(), input.length(), salt.c_str(), bcrypted);
output = std::string(bcrypted);
}

void OnOK() {
Napi::HandleScope scope(Env());
Callback().Call({Env().Undefined(),Napi::String::New(Env(), output)});
Callback().Call({Env().Undefined(),Napi::String::New(Env(), bcrypted)});
}
private:
std::string input;
std::string salt;
std::string output;
char bcrypted[_PASSWORD_LEN];
};

Napi::Value Encrypt(const Napi::CallbackInfo& info) {
if (info.Length() < 3) {
throw Napi::TypeError::New(info.Env(), "3 arguments expected");
}
std::string data = info[0].As<Napi::String>();;
std::string salt = info[1].As<Napi::String>();;
std::string data = info[0].IsBuffer()
? BufferToString(info[0].As<Napi::Buffer<char>>())
: info[0].As<Napi::String>();
std::string salt = info[1].As<Napi::String>();
Napi::Function callback = info[2].As<Napi::Function>();
EncryptAsyncWorker* encryptWorker = new EncryptAsyncWorker(callback, data, salt);
encryptWorker->Queue();
Expand All @@ -179,8 +180,10 @@ namespace {
if (info.Length() < 2) {
throw Napi::TypeError::New(info.Env(), "2 arguments expected");
}
std::string data = info[0].As<Napi::String>();;
std::string salt = info[1].As<Napi::String>();;
std::string data = info[0].IsBuffer()
? BufferToString(info[0].As<Napi::Buffer<char>>())
: info[0].As<Napi::String>();
std::string salt = info[1].As<Napi::String>();
if (!(ValidateSalt(salt.c_str()))) {
throw Napi::Error::New(env, "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
}
Expand All @@ -196,7 +199,7 @@ namespace {

class CompareAsyncWorker : public Napi::AsyncWorker {
public:
CompareAsyncWorker(Napi::Function& callback, std::string input, std::string encrypted)
CompareAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& encrypted)
: Napi::AsyncWorker(callback, "bcrypt:CompareAsyncWorker"), input(input), encrypted(encrypted) {
result = false;
}
Expand Down Expand Up @@ -226,7 +229,9 @@ namespace {
if (info.Length() < 3) {
throw Napi::TypeError::New(info.Env(), "3 arguments expected");
}
std::string input = info[0].As<Napi::String>();
std::string input = info[0].IsBuffer()
? BufferToString(info[0].As<Napi::Buffer<char>>())
: info[0].As<Napi::String>();
std::string encrypted = info[1].As<Napi::String>();
Napi::Function callback = info[2].As<Napi::Function>();
CompareAsyncWorker* compareWorker = new CompareAsyncWorker(callback, input, encrypted);
Expand All @@ -239,7 +244,9 @@ namespace {
if (info.Length() < 2) {
throw Napi::TypeError::New(info.Env(), "2 arguments expected");
}
std::string pw = info[0].As<Napi::String>();
std::string pw = info[0].IsBuffer()
? BufferToString(info[0].As<Napi::Buffer<char>>())
: info[0].As<Napi::String>();
std::string hash = info[1].As<Napi::String>();
char bcrypted[_PASSWORD_LEN];
if (ValidateSalt(hash.c_str())) {
Expand Down
7 changes: 6 additions & 1 deletion test/implementation.js
Expand Up @@ -29,6 +29,7 @@ module.exports = {
test_embedded_nulls: function(assert) {
assert.strictEqual(bcrypt.hashSync("Passw\0rd123", "$2b$05$CCCCCCCCCCCCCCCCCCCCC."), "$2b$05$CCCCCCCCCCCCCCCCCCCCC.VHy/kzL4sCcX3Ib3wN5rNGiRt.TpfxS");
assert.strictEqual(bcrypt.hashSync("Passw\0 you can literally write anything after the NUL character", "$2b$05$CCCCCCCCCCCCCCCCCCCCC."), "$2b$05$CCCCCCCCCCCCCCCCCCCCC.4vJLJQ6nZ/70INTjjSZWQ0iyUek92tu");
assert.strictEqual(bcrypt.hashSync(Buffer.from("Passw\0 you can literally write anything after the NUL character"), "$2b$05$CCCCCCCCCCCCCCCCCCCCC."), "$2b$05$CCCCCCCCCCCCCCCCCCCCC.4vJLJQ6nZ/70INTjjSZWQ0iyUek92tu");
assert.done();
},
test_shorten_salt_to_128_bits: function(assert) {
Expand All @@ -43,6 +44,10 @@ module.exports = {
assert.strictEqual(bcrypt.hashSync("p@5sw0rd", "$2b$12$zQ4CooEXdGqcwi0PHsgc8e"), "$2b$12$zQ4CooEXdGqcwi0PHsgc8eAf0DLXE/XHoBE8kCSGQ97rXwuClaPam");
assert.strictEqual(bcrypt.hashSync("C'est bon, la vie!", "$2b$12$cbo7LZ.wxgW4yxAA5Vqlv."), "$2b$12$cbo7LZ.wxgW4yxAA5Vqlv.KR6QFPt4qCdc9RYJNXxa/rbUOp.1sw.");
assert.strictEqual(bcrypt.hashSync("ἓν οἶδα ὅτι οὐδὲν οἶδα", "$2b$12$LeHKWR2bmrazi/6P22Jpau"), "$2b$12$LeHKWR2bmrazi/6P22JpauX5my/eKwwKpWqL7L5iEByBnxNc76FRW");
assert.done();
assert.strictEqual(bcrypt.hashSync(Buffer.from("ἓν οἶδα ὅτι οὐδὲν οἶδα"), "$2b$12$LeHKWR2bmrazi/6P22Jpau"), "$2b$12$LeHKWR2bmrazi/6P22JpauX5my/eKwwKpWqL7L5iEByBnxNc76FRW");
bcrypt.hash(Buffer.from("ἓν οἶδα ὅτι οὐδὲν οἶδα"), "$2b$12$LeHKWR2bmrazi/6P22Jpau", function(err, hash) {
assert.strictEqual(hash, "$2b$12$LeHKWR2bmrazi/6P22JpauX5my/eKwwKpWqL7L5iEByBnxNc76FRW");
assert.done();
});
}
}

0 comments on commit 2821c03

Please sign in to comment.