Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ jobs:
run: yarn lint
if: matrix.os == 'ubuntu-latest'

- name: typecheck
- name: TypeCheck
run: yarn typecheck
if: matrix.os == 'ubuntu-latest'

- name: Cargo test
uses: actions-rs/cargo@v1
timeout-minutes: 5
with:
command: test
args: -p nodr-rs-bcrypt --lib -- --nocapture

- name: Run build
run: |
cargo build --release
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"./packages/bcrypt",
"./packages/crc32",
"./packages/jieba"
]
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ Make rust crates binding to NodeJS use [napi-rs](https://github.com/Brooooooklyn

# Packages

| Package | Status | Description |
| ---------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- |
| [`@node-rs/crc32`](./packages/crc32/README.md) | ![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg) | Fastest `CRC32` implementation using `SIMD` |
| [`@node-rs/jieba`](./packages/jieba/README.md) | ![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg) | [`jieba-rs`](https://github.com/messense/jieba-rs) binding |
| Package | Status | Description |
| -------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- |
| [`@node-rs/crc32`](./packages/crc32) | ![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg) | Fastest `CRC32` implementation using `SIMD` |
| [`@node-rs/jieba`](./packages/jieba) | ![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg) | [`jieba-rs`](https://github.com/messense/jieba-rs) binding |
| [`@node-rs/bcrypt`](./packages/bcrypt) | ![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg) | Fastest bcrypt implementation |
26 changes: 26 additions & 0 deletions packages/bcrypt/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "nodr-rs-bcrypt"
version = "0.1.0"
authors = ["LongYinan <lynweklm@gmail.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
radix64 = "0.6"
blowfish = { version = "0.4", features = ["bcrypt"] }
byteorder = "1"
napi-rs = { version = "0.3" }
napi-rs-derive = { version = "0.2" }
rand = "0.7"
phf = { version = "0.8", features = ["macros"] }

[target.'cfg(unix)'.dependencies]
jemallocator = { version = "0.3", features = ["disable_initial_exec_tls"] }

[dev-dependencies]
quickcheck = "0.9"

[build-dependencies]
napi-build = { version = "0.1" }
63 changes: 63 additions & 0 deletions packages/bcrypt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# `@node-rs/bcrypt`

![](https://github.com/Brooooooklyn/node-rs/workflows/CI/badge.svg)

🚀 Fastest bcrypt in NodeJS

## Support matrix

| | node 10 | node12 | node13 | node14 |
| ----------------- | ------- | ------ | ------ | ------ |
| Windows 64 latest | ✓ | ✓ | ✓ | ✓ |
| macOS latest | ✓ | ✓ | ✓ | ✓ |
| Linux | ✓ | ✓ | ✓ | ✓ |

## Usage

```typescript
export const DEFAULT_ROUND = 12

function hashSync(password: string | Buffer, round?: number): string
function hash(password: string | Buffer, round?: number): Promise<string>
function verifySync(password: string | Buffer, hash: string | Buffer): boolean
function verify(password: string | Buffer, hash: string | Buffer): Promise<boolean>
```

## Bench

```
Model Name: MacBook Pro
Model Identifier: MacBookPro15,1
Processor Name: Intel Core i7
Processor Speed: 2.6 GHz
Number of Processors: 1
Total Number of Cores: 6
L2 Cache (per Core): 256 KB
L3 Cache: 12 MB
Hyper-Threading Technology: Enabled
Memory: 16 GB
```

<pre>
@node-rs/bcrypt x <span style="color: hotpink;">72.11</span> ops/sec ±1.43% (33 runs sampled)
node bcrypt x <span style="color: hotpink;">62.75</span> ops/sec ±2.95% (30 runs sampled)
Async hash round 10 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">18.49</span> ops/sec ±1.04% (12 runs sampled)
node bcrypt x <span style="color: hotpink;">16.67</span> ops/sec ±2.05% (11 runs sampled)
Async hash round 12 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">3.99</span> ops/sec ±3.17% (6 runs sampled)
node bcrypt x <span style="color: hotpink;">3.13</span> ops/sec ±1.92% (6 runs sampled)
Async hash round 14 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">14.32</span> ops/sec ±0.55% (10 runs sampled)
node bcrypt x <span style="color: hotpink;">13.55</span> ops/sec ±2.83% (10 runs sampled)
Async verify bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">15.98</span> ops/sec ±1.12% (44 runs sampled)
node bcrypt x <span style="color: hotpink;">14.55</span> ops/sec ±1.30% (40 runs sampled)
Hash round 10 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">4.65</span> ops/sec ±3.60% (16 runs sampled)
node bcrypt x <span style="color: hotpink;">4.26</span> ops/sec ±1.90% (15 runs sampled)
Hash round 12 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
@node-rs/bcrypt x <span style="color: hotpink;">1.16</span> ops/sec ±2.65% (7 runs sampled)
node bcrypt x <span style="color: hotpink;">1.04</span> ops/sec ±2.95% (7 runs sampled)
Hash round 14 bench suite: Fastest is <span style="color: rgb(80, 250, 123);">@node-rs/bcrypt</span>
</pre>
17 changes: 17 additions & 0 deletions packages/bcrypt/__tests__/bcrypt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import test from 'ava'
import { verifySync, hash } from '../index'

const { hashSync } = require('bcrypt')

const fx = Buffer.from('bcrypt-test-password')

const hashedPassword = hashSync(fx)

test('verifySync hashed password from bcrypt should be true', (t) => {
t.true(verifySync(fx, hashedPassword))
})

test('verifySync hashed password from @node-rs/bcrypt should be true', async (t) => {
const hashed = await hash(fx)
t.true(verifySync(fx, hashed))
})
104 changes: 104 additions & 0 deletions packages/bcrypt/benchmark/bcrypt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const { Suite } = require('benchmark')
const { hashSync, hash, compare } = require('bcrypt')
const { cpus } = require('os')
const chalk = require('chalk')
const { range } = require('lodash')

const { hash: napiHash, hashSync: napiHashSync, verify } = require('../index')

const hashRounds = [10, 12, 14]
const parallel = cpus().length

const password = 'node-rust-password'

function runAsync(round) {
const asyncHashSuite = new Suite(`Async hash round ${round}`)
return new Promise((resolve) => {
asyncHashSuite
.add('@node-rs/bcrypt', {
defer: true,
fn: (deferred) => {
Promise.all(range(parallel).map(() => napiHash(password, round))).then(() => {
deferred.resolve()
})
},
})
.add('node bcrypt', {
defer: true,
fn: (deferred) => {
Promise.all(range(parallel).map(() => hash(password, round))).then(() => {
deferred.resolve()
})
},
})
.on('cycle', function (event) {
event.target.hz = event.target.hz * parallel
console.info(String(event.target))
})
.on('complete', function () {
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
resolve()
})
.run({ async: true })
})
}

hashRounds
.reduce(async (acc, cur) => {
await acc
return runAsync(cur)
}, Promise.resolve())
.then(
() =>
new Promise((resolve) => {
const suite = new Suite('Async verify')
const hash = napiHashSync(password)
suite
.add({
name: '@node-rs/bcrypt',
defer: true,
fn: (deferred) => {
Promise.all(range(parallel).map(() => verify(password, hash))).then(() => {
deferred.resolve()
})
},
})
.add({
name: 'node bcrypt',
defer: true,
fn: (deferred) => {
Promise.all(range(parallel).map(() => compare(password, hash))).then(() => {
deferred.resolve()
})
},
})
.on('cycle', function (event) {
event.target.hz = event.target.hz * parallel
console.info(String(event.target))
})
.on('complete', function () {
resolve()
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
})
.run()
}),
)
.then(() => {
for (const round of hashRounds) {
const syncHashSuite = new Suite(`Hash round ${round}`)
syncHashSuite
.add('@node-rs/bcrypt', () => {
napiHashSync(password, round)
})
.add('node bcrypt', () => {
hashSync(password, round)
})
.on('cycle', function (event) {
console.info(String(event.target))
})
.on('complete', function () {
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
})
.run()
}
})
5 changes: 5 additions & 0 deletions packages/bcrypt/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern crate napi_build;

fn main() {
napi_build::setup();
}
6 changes: 6 additions & 0 deletions packages/bcrypt/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const DEFAULT_COST: 12

export function hashSync(password: string | Buffer, round?: number): string
export function hash(password: string | Buffer, round?: number): Promise<string>
export function verifySync(password: string | Buffer, hash: string | Buffer): boolean
export function verify(password: string | Buffer, hash: string | Buffer): Promise<boolean>
35 changes: 35 additions & 0 deletions packages/bcrypt/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { locateBinding } = require('@node-rs/helper')

const binding = require(locateBinding(__dirname, 'bcrypt'))

const DEFAULT_COST = 12

module.exports = {
DEFAULT_COST: DEFAULT_COST,

genSalt: function genSalt(round = 10, version = '2b') {
return binding.genSalt(round, version)
},

hashSync: function hashSync(password, round = DEFAULT_COST) {
const input = Buffer.isBuffer(password) ? password : Buffer.from(password)
return binding.hashSync(input, round)
},

hash: function hash(password, round = DEFAULT_COST) {
const input = Buffer.isBuffer(password) ? password : Buffer.from(password)
return binding.hash(input, round)
},

verifySync: function verifySync(password, hash) {
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
return binding.verifySync(password, hash)
},

verify: function verify(password, hash) {
password = Buffer.isBuffer(password) ? password : Buffer.from(password)
hash = Buffer.isBuffer(hash) ? hash : Buffer.from(hash)
return binding.verify(password, hash)
},
}
50 changes: 50 additions & 0 deletions packages/bcrypt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@node-rs/bcrypt",
"version": "0.0.0",
"description": "Rust bcrypt binding",
"keywords": [
"bcrypt",
"auth",
"password",
"authentication",
"encryption",
"crypto",
"N-API",
"napi-rs",
"node-rs"
],
"author": "LongYinan <lynweklm@gmail.com>",
"homepage": "https://github.com/Brooooooklyn/node-rs",
"license": "MIT",
"main": "index.js",
"typings": "index.d.ts",
"files": [
"index.js",
"index.d.ts",
"*.node",
"LICENSE",
"COPYING"
],
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Brooooooklyn/node-rs.git"
},
"scripts": {
"bench": "cross-env NODE_ENV=production node benchmark/bcrypt.js",
"build": "napi --release ./bcrypt",
"build:debug": "napi ./bcrypt.debug"
},
"bugs": {
"url": "https://github.com/Brooooooklyn/node-rs/issues"
},
"dependencies": {
"@node-rs/helper": "^0.1.3"
},
"devDependencies": {
"bcrypt": "^4.0.1"
}
}
Loading