Skip to content

Commit b1c9402

Browse files
committed
feat(bcrypt): genSalt and genSaltSync
1 parent 35da5ad commit b1c9402

File tree

9 files changed

+127
-55
lines changed

9 files changed

+127
-55
lines changed

package.json

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22
"name": "node-rs",
33
"version": "0.0.0",
44
"description": "Node & Rust bindings monorepo",
5-
"main": "index.js",
65
"author": "LongYinan <[email protected]>",
76
"license": "MIT",
87
"private": true,
9-
"workspaces": [
10-
"packages/*"
11-
],
8+
"workspaces": ["packages/*"],
129
"scripts": {
1310
"artifacts": "lerna run artifacts",
1411
"bench": "lerna run bench --concurrency 1 --stream --no-prefix",
@@ -47,33 +44,18 @@
4744
"typescript": "^4.2.3"
4845
},
4946
"ava": {
50-
"extensions": [
51-
"ts"
52-
],
53-
"require": [
54-
"@swc-node/register"
55-
],
56-
"files": [
57-
"packages/**/*.spec.ts"
58-
],
47+
"extensions": ["ts"],
48+
"require": ["@swc-node/register"],
49+
"files": ["packages/**/*.spec.ts"],
5950
"environmentVariables": {
6051
"TS_NODE_PROJECT": "./tsconfig.test.json"
6152
}
6253
},
6354
"lint-staged": {
64-
"*.@(js|ts|tsx)": [
65-
"prettier --write",
66-
"eslint -c .eslintrc.yml --fix"
67-
],
68-
"*.@(yml|yaml)": [
69-
"prettier --parser yaml --write"
70-
],
71-
"*.json": [
72-
"prettier --parser json --write"
73-
],
74-
"*.md": [
75-
"prettier --parser markdown --write"
76-
]
55+
"*.@(js|ts|tsx)": ["prettier --write", "eslint -c .eslintrc.yml --fix"],
56+
"*.@(yml|yaml)": ["prettier --parser yaml --write"],
57+
"*.json": ["prettier --parser json --write"],
58+
"*.md": ["prettier --parser markdown --write"]
7759
},
7860
"prettier": {
7961
"printWidth": 120,
@@ -83,4 +65,4 @@
8365
"arrowParens": "always",
8466
"parser": "typescript"
8567
}
86-
}
68+
}

packages/bcrypt/benchmark/bcrypt.js

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
const { cpus } = require('os')
22

3-
const { hashSync, hash, compare } = require('bcrypt')
4-
const { hashSync: hashSyncJs, hash: hashJs, compare: compareJs } = require('bcryptjs')
3+
const { hashSync, hash, compare, genSaltSync } = require('bcrypt')
4+
const { hashSync: hashSyncJs, hash: hashJs, compare: compareJs, genSaltSync: genSaltSyncJs } = require('bcryptjs')
55
const { Suite } = require('benchmark')
66
const chalk = require('chalk')
77
const { range } = require('lodash')
88

9-
const { hash: napiHash, hashSync: napiHashSync, verify } = require('../index')
9+
const { hash: napiHash, hashSync: napiHashSync, verify, genSaltSync: napiGenSaltSync } = require('../index')
1010

11-
const hashRounds = [10, 12, 14]
1211
const parallel = cpus().length
1312

1413
const password = 'node-rust-password'
1514

16-
function runAsync(round) {
15+
function runAsync(round = 12) {
1716
const asyncHashSuite = new Suite(`Async hash round ${round}`)
1817
return new Promise((resolve) => {
1918
asyncHashSuite
@@ -53,11 +52,7 @@ function runAsync(round) {
5352
})
5453
}
5554

56-
hashRounds
57-
.reduce(async (acc, cur) => {
58-
await acc
59-
return runAsync(cur)
60-
}, Promise.resolve())
55+
runAsync()
6156
.then(
6257
() =>
6358
new Promise((resolve) => {
@@ -103,24 +98,47 @@ hashRounds
10398
}),
10499
)
105100
.then(() => {
106-
for (const round of hashRounds) {
107-
const syncHashSuite = new Suite(`Hash round ${round}`)
101+
return new Promise((resolve) => {
102+
const syncHashSuite = new Suite(`Hash round 12`)
108103
syncHashSuite
109104
.add('@node-rs/bcrypt', () => {
110-
napiHashSync(password, round)
105+
napiHashSync(password, 12)
111106
})
112107
.add('node bcrypt', () => {
113-
hashSync(password, round)
108+
hashSync(password, 12)
114109
})
115110
.add('bcryptjs', () => {
116-
hashSyncJs(password, round)
111+
hashSyncJs(password, 12)
117112
})
118113
.on('cycle', function (event) {
119114
console.info(String(event.target))
120115
})
121116
.on('complete', function () {
122117
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
118+
resolve()
123119
})
124120
.run()
125-
}
121+
})
122+
})
123+
.then(() => {
124+
return new Promise((resolve) => {
125+
new Suite('genSaltSync')
126+
.add('@node-rs/bcrypt', () => {
127+
napiGenSaltSync(10, '2b')
128+
})
129+
.add('node bcrypt', () => {
130+
genSaltSync(10, 'b')
131+
})
132+
.add('bcryptjs', () => {
133+
genSaltSyncJs(10)
134+
})
135+
.on('cycle', function (event) {
136+
console.info(String(event.target))
137+
})
138+
.on('complete', function () {
139+
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
140+
resolve()
141+
})
142+
.run()
143+
})
126144
})

packages/bcrypt/index.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,23 @@ export function hashSync(password: string | Buffer, round?: number): string
44
export function hash(password: string | Buffer, round?: number): Promise<string>
55
export function verifySync(password: string | Buffer, hash: string | Buffer): boolean
66
export function verify(password: string | Buffer, hash: string | Buffer): Promise<boolean>
7+
/**
8+
* The same with `verifySync`
9+
*/
10+
export function compareSync(password: string | Buffer, hash: string | Buffer): boolean
11+
/**
12+
* The same with `verify`
13+
*/
14+
export function compare(password: string | Buffer, hash: string | Buffer): Promise<boolean>
15+
16+
export type Version = '2a' | '2x' | '2y' | '2b'
17+
/**
18+
* @param round default 10
19+
* @param version default '2b'
20+
*/
21+
export function genSaltSync(round?: number, version?: Version): string
22+
/**
23+
* @param round default 10
24+
* @param version default '2b'
25+
*/
26+
export function genSalt(round?: number, version?: Version): Promise<string>

packages/bcrypt/index.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,25 @@ const binding = loadBinding(__dirname, 'bcrypt', '@node-rs/bcrypt')
44

55
const DEFAULT_COST = 12
66

7+
function verify(password, hash) {
8+
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
9+
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
10+
return binding.verify(password, hash)
11+
}
12+
13+
function verifySync(password, hash) {
14+
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
15+
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
16+
return binding.verifySync(password, hash)
17+
}
18+
719
module.exports = {
820
DEFAULT_COST: DEFAULT_COST,
921

22+
genSaltSync: function genSaltSync(round = 10, version = '2b') {
23+
return binding.genSaltSync(round, version)
24+
},
25+
1026
genSalt: function genSalt(round = 10, version = '2b') {
1127
return binding.genSalt(round, version)
1228
},
@@ -21,15 +37,8 @@ module.exports = {
2137
return binding.hash(input, round)
2238
},
2339

24-
verifySync: function verifySync(password, hash) {
25-
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
26-
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
27-
return binding.verifySync(password, hash)
28-
},
29-
30-
verify: function verify(password, hash) {
31-
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
32-
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
33-
return binding.verify(password, hash)
34-
},
40+
verifySync,
41+
verify,
42+
compareSync: verifySync,
43+
compare: verify,
3544
}

packages/bcrypt/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@node-rs/helper": "^1.1.0"
5050
},
5151
"devDependencies": {
52+
"@types/bcrypt": "^3.0.0",
5253
"bcrypt": "^5.0.1",
5354
"bcryptjs": "^2.4.3"
5455
},

packages/bcrypt/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod bcrypt;
1717
mod errors;
1818
mod hash_task;
1919
mod lib_bcrypt;
20+
mod salt_task;
2021
mod verify_task;
2122

2223
#[cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))]
@@ -29,9 +30,10 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
2930

3031
#[module_exports]
3132
fn init(mut exports: JsObject) -> Result<()> {
33+
exports.create_named_method("genSaltSync", js_salt)?;
34+
exports.create_named_method("genSalt", js_async_salt)?;
3235
exports.create_named_method("hash", js_async_hash)?;
3336
exports.create_named_method("hashSync", js_hash)?;
34-
exports.create_named_method("genSalt", js_salt)?;
3537
exports.create_named_method("verifySync", js_verify)?;
3638
exports.create_named_method("verify", js_async_verify)?;
3739

@@ -51,6 +53,18 @@ fn js_salt(ctx: CallContext) -> Result<JsString> {
5153
ctx.env.create_string(&salt_string)
5254
}
5355

56+
#[js_function(2)]
57+
fn js_async_salt(ctx: CallContext) -> Result<JsObject> {
58+
let round = ctx.get::<JsNumber>(0)?;
59+
let version = ctx.get::<JsString>(1)?.into_utf8()?;
60+
let task = salt_task::SaltTask {
61+
round: round.try_into()?,
62+
version: Version::from_str(version.as_str()?)
63+
.map_err(|_| Error::from_status(Status::InvalidArg))?,
64+
};
65+
ctx.env.spawn(task).map(|t| t.promise_object())
66+
}
67+
5468
#[js_function(2)]
5569
fn js_hash(ctx: CallContext) -> Result<JsString> {
5670
let password = ctx.get::<JsBuffer>(0)?.into_value()?;

packages/bcrypt/src/lib_bcrypt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub struct HashParts {
2424

2525
/// BCrypt hash version
2626
/// https://en.wikipedia.org/wiki/Bcrypt#Versioning_history
27+
#[derive(Debug, Clone, Copy)]
2728
pub enum Version {
2829
TwoA,
2930
TwoX,

packages/bcrypt/src/salt_task.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use napi::{Env, JsString, Result, Task};
2+
3+
use crate::{format_salt, gen_salt, Version};
4+
5+
pub struct SaltTask {
6+
pub(crate) round: u32,
7+
pub(crate) version: Version,
8+
}
9+
10+
impl Task for SaltTask {
11+
type Output = String;
12+
type JsValue = JsString;
13+
14+
fn compute(&mut self) -> Result<Self::Output> {
15+
let random = gen_salt();
16+
Ok(format_salt(self.round, self.version, &random))
17+
}
18+
19+
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
20+
env.create_string(output.as_str())
21+
}
22+
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,11 @@
12901290
resolved "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
12911291
integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
12921292

1293+
"@types/bcrypt@^3.0.0":
1294+
version "3.0.0"
1295+
resolved "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-3.0.0.tgz#851489a9065a067cb7f3c9cbe4ce9bed8bba0876"
1296+
integrity sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ==
1297+
12931298
"@types/crc@^3.4.0":
12941299
version "3.4.0"
12951300
resolved "https://registry.npmjs.org/@types/crc/-/crc-3.4.0.tgz#2366beb4399cd734b33e42c7ac809576e617d48a"

0 commit comments

Comments
 (0)