diff --git a/.gitattributes b/.gitattributes index ab4a5226..cec55130 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,8 @@ *.debug text eol=lf merge=union # Generated codes +packages/argon2/index.js linguist-detectable=false +packages/argon2/index.d.ts linguist-detectable=false packages/bcrypt/index.js linguist-detectable=false packages/bcrypt/index.d.ts linguist-detectable=false packages/crc32/index.js linguist-detectable=false diff --git a/Cargo.toml b/Cargo.toml index d0eee53a..a08a0573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ cargo-features = ["strip"] [workspace] members = [ "./crates/alloc", + "./packages/argon2", "./packages/bcrypt", "./packages/crc32", "./packages/deno-lint", diff --git a/packages/argon2/.npmignore b/packages/argon2/.npmignore new file mode 100644 index 00000000..f96abe0b --- /dev/null +++ b/packages/argon2/.npmignore @@ -0,0 +1,10 @@ +target +Cargo.lock +.cargo +.github +npm +.eslintrc +.prettierignore +rustfmt.toml +yarn.lock +*.node diff --git a/packages/argon2/Cargo.toml b/packages/argon2/Cargo.toml new file mode 100644 index 00000000..4fb31940 --- /dev/null +++ b/packages/argon2/Cargo.toml @@ -0,0 +1,16 @@ +[package] +edition = "2021" +name = "node-rs_argon2" +version = "0.0.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +argon2 = {version = "0.3", features = ["parallel"]} +napi = {version = "2", default-features = false, features = ["napi3"]} +napi-derive = {version = "2", default-features = false, features = ["type-def"]} +rand = {version = "0.8", features = ["nightly", "simd_support"]} + +[build-dependencies] +napi-build = "1" diff --git a/packages/argon2/README.md b/packages/argon2/README.md new file mode 100644 index 00000000..8d952a1b --- /dev/null +++ b/packages/argon2/README.md @@ -0,0 +1,70 @@ +# `@node-rs/argon2` + +![](https://github.com/napi-rs/node-rs/workflows/CI/badge.svg) +![](https://img.shields.io/npm/dm/@node-rs/argon2.svg?sanitize=true) + +[argon2](https://crates.io/crates/argon2) binding for Node.js. + +## Support matrix + +| | node12 | node14 | node16 | node17 | +| ------------------- | ------ | ------ | ------ | ------ | +| Windows x64 | ✓ | ✓ | ✓ | ✓ | +| Windows x32 | ✓ | ✓ | ✓ | ✓ | +| Windows arm64 | ✓ | ✓ | ✓ | ✓ | +| macOS x64 | ✓ | ✓ | ✓ | ✓ | +| macOS arm64(m chip) | ✓ | ✓ | ✓ | ✓ | +| Linux x64 gnu | ✓ | ✓ | ✓ | ✓ | +| Linux x64 musl | ✓ | ✓ | ✓ | ✓ | +| Linux arm gnu | ✓ | ✓ | ✓ | ✓ | +| Linux arm64 gnu | ✓ | ✓ | ✓ | ✓ | +| Linux arm64 musl | ✓ | ✓ | ✓ | ✓ | +| Android arm64 | ✓ | ✓ | ✓ | ✓ | +| Android armv7 | ✓ | ✓ | ✓ | ✓ | +| FreeBSD x64 | ✓ | ✓ | ✓ | ✓ | + +## API + +```typescript +export const enum Algorithm { + Argon2d = 0, + Argon2i = 1, + Argon2id = 2, +} +export const enum Version { + V0x10 = 0, + V0x13 = 1, +} +export interface Options { + /** + * Memory size, expressed in kilobytes, between 1 and (2^32)-1. + * Value is an integer in decimal (1 to 10 digits). + */ + memoryCost?: number | undefined | null + /** + * Number of iterations, between 1 and (2^32)-1. + * Value is an integer in decimal (1 to 10 digits). + */ + timeCost?: number | undefined | null + /** + * Degree of parallelism, between 1 and 255. + * Value is an integer in decimal (1 to 3 digits). + */ + outputLen?: number | undefined | null + parallelism?: number | undefined | null + algorithm?: Algorithm | undefined | null + version?: Version | undefined | null + secret?: Buffer | undefined | null +} +export function hash( + password: string | Buffer, + options?: Options | undefined | null, + abortSignal?: AbortSignal | undefined | null, +): Promise +export function verify( + hashed: string | Buffer, + password: string | Buffer, + options?: Options | undefined | null, + abortSignal?: AbortSignal | undefined | null, +): Promise +``` diff --git a/packages/argon2/__test__/argon2.spec.ts b/packages/argon2/__test__/argon2.spec.ts new file mode 100644 index 00000000..91b5cb13 --- /dev/null +++ b/packages/argon2/__test__/argon2.spec.ts @@ -0,0 +1,49 @@ +import { randomBytes } from 'crypto' + +import test from 'ava' + +import { Algorithm, hash, verify, Version } from '../index.js' + +test('should be able to hash string', async (t) => { + await t.notThrowsAsync(() => hash('whatever')) + await t.notThrowsAsync(() => + hash('whatever', { + secret: randomBytes(32), + }), + ) +}) + +test('should be able to verify hashed string', async (t) => { + const PASSWORD = 'Argon2_is_the_best_algorithm_ever' + t.true(await verify(await hash(PASSWORD), PASSWORD)) + t.true( + await verify( + await hash(PASSWORD, { + algorithm: Algorithm.Argon2d, + }), + PASSWORD, + ), + ) + t.true( + await verify( + await hash(PASSWORD, { + algorithm: Algorithm.Argon2i, + }), + PASSWORD, + ), + ) + const secret = randomBytes(32) + t.true( + await verify( + await hash(PASSWORD, { + algorithm: Algorithm.Argon2d, + version: Version.V0x10, + secret, + }), + PASSWORD, + { + secret, + }, + ), + ) +}) diff --git a/packages/argon2/benchmark/argon2.js b/packages/argon2/benchmark/argon2.js new file mode 100644 index 00000000..7349e748 --- /dev/null +++ b/packages/argon2/benchmark/argon2.js @@ -0,0 +1,42 @@ +const { cpus } = require('os') + +const nodeArgon2 = require('argon2') +const { Suite } = require('benchmark') +const chalk = require('chalk') + +const { hash, verify, Algorithm } = require('../index') + +const PASSWORD = '$v=19$m=4096,t=3,p=1$fyLYvmzgpBjDTP6QSypj3g$pb1Q3Urv1amxuFft0rGwKfEuZPhURRDV7TJqcBnwlGo' +const CORES = cpus().length + +const suite = new Suite('Hash with all cores') + +suite + .add( + '@node-rs/argon', + async (deferred) => { + await hash(PASSWORD, { + algorithm: Algorithm.Argon2id, + parallelism: CORES, + }) + deferred.resolve() + }, + { defer: true }, + ) + .add( + 'node-argon', + async (deferred) => { + await nodeArgon2.hash(PASSWORD, { type: nodeArgon2.argon2id, parallelism: CORES }) + deferred.resolve() + }, + { + defer: true, + }, + ) + .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() diff --git a/packages/argon2/build.rs b/packages/argon2/build.rs new file mode 100644 index 00000000..1f866b6a --- /dev/null +++ b/packages/argon2/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/packages/argon2/index.d.ts b/packages/argon2/index.d.ts new file mode 100644 index 00000000..ae9df7f7 --- /dev/null +++ b/packages/argon2/index.d.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ + +/* auto-generated by NAPI-RS */ + +export class ExternalObject { + readonly '': { + readonly '': unique symbol + [K: symbol]: T + } +} +export const enum Algorithm { + Argon2d = 0, + Argon2i = 1, + Argon2id = 2, +} +export const enum Version { + V0x10 = 0, + V0x13 = 1, +} +export interface Options { + /** + * Memory size, expressed in kilobytes, between 1 and (2^32)-1. + * Value is an integer in decimal (1 to 10 digits). + */ + memoryCost?: number | undefined | null + /** + * Number of iterations, between 1 and (2^32)-1. + * Value is an integer in decimal (1 to 10 digits). + */ + timeCost?: number | undefined | null + /** + * Degree of parallelism, between 1 and 255. + * Value is an integer in decimal (1 to 3 digits). + */ + outputLen?: number | undefined | null + parallelism?: number | undefined | null + algorithm?: Algorithm | undefined | null + version?: Version | undefined | null + secret?: Buffer | undefined | null +} +export function hash( + password: string | Buffer, + options?: Options | undefined | null, + abortSignal?: AbortSignal | undefined | null, +): Promise +export function verify( + hashed: string | Buffer, + password: string | Buffer, + options?: Options | undefined | null, + abortSignal?: AbortSignal | undefined | null, +): Promise diff --git a/packages/argon2/index.js b/packages/argon2/index.js new file mode 100644 index 00000000..4f2987b0 --- /dev/null +++ b/packages/argon2/index.js @@ -0,0 +1,200 @@ +const { existsSync, readFileSync } = require('fs') +const { join } = require('path') + +const { platform, arch } = process + +let nativeBinding = null +let localFileExisted = false +let isMusl = false +let loadError = null + +switch (platform) { + case 'android': + if (arch !== 'arm64') { + throw new Error(`Unsupported architecture on Android ${arch}`) + } + localFileExisted = existsSync(join(__dirname, 'argon2.android-arm64.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.android-arm64.node') + } else { + nativeBinding = require('@node-rs/argon2-android-arm64') + } + } catch (e) { + loadError = e + } + break + case 'win32': + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'argon2.win32-x64-msvc.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.win32-x64-msvc.node') + } else { + nativeBinding = require('@node-rs/argon2-win32-x64-msvc') + } + } catch (e) { + loadError = e + } + break + case 'ia32': + localFileExisted = existsSync(join(__dirname, 'argon2.win32-ia32-msvc.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.win32-ia32-msvc.node') + } else { + nativeBinding = require('@node-rs/argon2-win32-ia32-msvc') + } + } catch (e) { + loadError = e + } + break + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'argon2.win32-arm64-msvc.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.win32-arm64-msvc.node') + } else { + nativeBinding = require('@node-rs/argon2-win32-arm64-msvc') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`) + } + break + case 'darwin': + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'argon2.darwin-x64.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.darwin-x64.node') + } else { + nativeBinding = require('@node-rs/argon2-darwin-x64') + } + } catch (e) { + loadError = e + } + break + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'argon2.darwin-arm64.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.darwin-arm64.node') + } else { + nativeBinding = require('@node-rs/argon2-darwin-arm64') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`) + } + break + case 'freebsd': + if (arch !== 'x64') { + throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) + } + localFileExisted = existsSync(join(__dirname, 'argon2.freebsd-x64.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.freebsd-x64.node') + } else { + nativeBinding = require('@node-rs/argon2-freebsd-x64') + } + } catch (e) { + loadError = e + } + break + case 'linux': + switch (arch) { + case 'x64': + isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl') + if (isMusl) { + localFileExisted = existsSync(join(__dirname, 'argon2.linux-x64-musl.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.linux-x64-musl.node') + } else { + nativeBinding = require('@node-rs/argon2-linux-x64-musl') + } + } catch (e) { + loadError = e + } + } else { + localFileExisted = existsSync(join(__dirname, 'argon2.linux-x64-gnu.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.linux-x64-gnu.node') + } else { + nativeBinding = require('@node-rs/argon2-linux-x64-gnu') + } + } catch (e) { + loadError = e + } + } + break + case 'arm64': + isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl') + if (isMusl) { + localFileExisted = existsSync(join(__dirname, 'argon2.linux-arm64-musl.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.linux-arm64-musl.node') + } else { + nativeBinding = require('@node-rs/argon2-linux-arm64-musl') + } + } catch (e) { + loadError = e + } + } else { + localFileExisted = existsSync(join(__dirname, 'argon2.linux-arm64-gnu.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.linux-arm64-gnu.node') + } else { + nativeBinding = require('@node-rs/argon2-linux-arm64-gnu') + } + } catch (e) { + loadError = e + } + } + break + case 'arm': + localFileExisted = existsSync(join(__dirname, 'argon2.linux-arm-gnueabihf.node')) + try { + if (localFileExisted) { + nativeBinding = require('./argon2.linux-arm-gnueabihf.node') + } else { + nativeBinding = require('@node-rs/argon2-linux-arm-gnueabihf') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`) + } + break + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) +} + +if (!nativeBinding) { + if (loadError) { + throw loadError + } + throw new Error(`Failed to load native binding`) +} + +const { Algorithm, Version, hash, verify } = nativeBinding + +module.exports.Algorithm = Algorithm +module.exports.Version = Version +module.exports.hash = hash +module.exports.verify = verify diff --git a/packages/argon2/npm/android-arm-eabi/README.md b/packages/argon2/npm/android-arm-eabi/README.md new file mode 100644 index 00000000..5c940657 --- /dev/null +++ b/packages/argon2/npm/android-arm-eabi/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-android-arm-eabi` + +This is the **armv7-linux-androideabi** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/android-arm-eabi/package.json b/packages/argon2/npm/android-arm-eabi/package.json new file mode 100644 index 00000000..1cea857c --- /dev/null +++ b/packages/argon2/npm/android-arm-eabi/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-android-arm-eabi", + "version": "0.0.0", + "os": [ + "android" + ], + "cpu": [ + "arm" + ], + "main": "argon2.android-arm-eabi.node", + "files": [ + "argon2.android-arm-eabi.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/android-arm64/README.md b/packages/argon2/npm/android-arm64/README.md new file mode 100644 index 00000000..8d1bccb9 --- /dev/null +++ b/packages/argon2/npm/android-arm64/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-android-arm64` + +This is the **aarch64-linux-android** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/android-arm64/package.json b/packages/argon2/npm/android-arm64/package.json new file mode 100644 index 00000000..5a6407b8 --- /dev/null +++ b/packages/argon2/npm/android-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-android-arm64", + "version": "0.0.0", + "os": [ + "android" + ], + "cpu": [ + "arm64" + ], + "main": "argon2.android-arm64.node", + "files": [ + "argon2.android-arm64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/darwin-arm64/README.md b/packages/argon2/npm/darwin-arm64/README.md new file mode 100644 index 00000000..f063f7cf --- /dev/null +++ b/packages/argon2/npm/darwin-arm64/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-darwin-arm64` + +This is the **aarch64-apple-darwin** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/darwin-arm64/package.json b/packages/argon2/npm/darwin-arm64/package.json new file mode 100644 index 00000000..a5161a20 --- /dev/null +++ b/packages/argon2/npm/darwin-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-darwin-arm64", + "version": "0.0.0", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "main": "argon2.darwin-arm64.node", + "files": [ + "argon2.darwin-arm64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/darwin-x64/README.md b/packages/argon2/npm/darwin-x64/README.md new file mode 100644 index 00000000..0c387a3b --- /dev/null +++ b/packages/argon2/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/darwin-x64/package.json b/packages/argon2/npm/darwin-x64/package.json new file mode 100644 index 00000000..c813f819 --- /dev/null +++ b/packages/argon2/npm/darwin-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-darwin-x64", + "version": "0.0.0", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "main": "argon2.darwin-x64.node", + "files": [ + "argon2.darwin-x64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/freebsd-x64/README.md b/packages/argon2/npm/freebsd-x64/README.md new file mode 100644 index 00000000..bbc66d4d --- /dev/null +++ b/packages/argon2/npm/freebsd-x64/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-freebsd-x64` + +This is the **x86_64-unknown-freebsd** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/freebsd-x64/package.json b/packages/argon2/npm/freebsd-x64/package.json new file mode 100644 index 00000000..5e26cecb --- /dev/null +++ b/packages/argon2/npm/freebsd-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-freebsd-x64", + "version": "0.0.0", + "os": [ + "freebsd" + ], + "cpu": [ + "x64" + ], + "main": "argon2.freebsd-x64.node", + "files": [ + "argon2.freebsd-x64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/linux-arm-gnueabihf/README.md b/packages/argon2/npm/linux-arm-gnueabihf/README.md new file mode 100644 index 00000000..a193e4e7 --- /dev/null +++ b/packages/argon2/npm/linux-arm-gnueabihf/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-linux-arm-gnueabihf` + +This is the **armv7-unknown-linux-gnueabihf** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/linux-arm-gnueabihf/package.json b/packages/argon2/npm/linux-arm-gnueabihf/package.json new file mode 100644 index 00000000..e5a23872 --- /dev/null +++ b/packages/argon2/npm/linux-arm-gnueabihf/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-linux-arm-gnueabihf", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm" + ], + "main": "argon2.linux-arm-gnueabihf.node", + "files": [ + "argon2.linux-arm-gnueabihf.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/linux-arm64-gnu/README.md b/packages/argon2/npm/linux-arm64-gnu/README.md new file mode 100644 index 00000000..f8f955ed --- /dev/null +++ b/packages/argon2/npm/linux-arm64-gnu/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-linux-arm64-gnu` + +This is the **aarch64-unknown-linux-gnu** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/linux-arm64-gnu/package.json b/packages/argon2/npm/linux-arm64-gnu/package.json new file mode 100644 index 00000000..ce18f931 --- /dev/null +++ b/packages/argon2/npm/linux-arm64-gnu/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-linux-arm64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "argon2.linux-arm64-gnu.node", + "files": [ + "argon2.linux-arm64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/linux-arm64-musl/README.md b/packages/argon2/npm/linux-arm64-musl/README.md new file mode 100644 index 00000000..4f4bac14 --- /dev/null +++ b/packages/argon2/npm/linux-arm64-musl/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-linux-arm64-musl` + +This is the **aarch64-unknown-linux-musl** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/linux-arm64-musl/package.json b/packages/argon2/npm/linux-arm64-musl/package.json new file mode 100644 index 00000000..9fc146ed --- /dev/null +++ b/packages/argon2/npm/linux-arm64-musl/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-linux-arm64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "argon2.linux-arm64-musl.node", + "files": [ + "argon2.linux-arm64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/linux-x64-gnu/README.md b/packages/argon2/npm/linux-x64-gnu/README.md new file mode 100644 index 00000000..19355d3c --- /dev/null +++ b/packages/argon2/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/linux-x64-gnu/package.json b/packages/argon2/npm/linux-x64-gnu/package.json new file mode 100644 index 00000000..4e625e9b --- /dev/null +++ b/packages/argon2/npm/linux-x64-gnu/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-linux-x64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "argon2.linux-x64-gnu.node", + "files": [ + "argon2.linux-x64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/linux-x64-musl/README.md b/packages/argon2/npm/linux-x64-musl/README.md new file mode 100644 index 00000000..3786de0e --- /dev/null +++ b/packages/argon2/npm/linux-x64-musl/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-linux-x64-musl` + +This is the **x86_64-unknown-linux-musl** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/linux-x64-musl/package.json b/packages/argon2/npm/linux-x64-musl/package.json new file mode 100644 index 00000000..48087f69 --- /dev/null +++ b/packages/argon2/npm/linux-x64-musl/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-linux-x64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "argon2.linux-x64-musl.node", + "files": [ + "argon2.linux-x64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/win32-arm64-msvc/README.md b/packages/argon2/npm/win32-arm64-msvc/README.md new file mode 100644 index 00000000..b4280aec --- /dev/null +++ b/packages/argon2/npm/win32-arm64-msvc/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-win32-arm64-msvc` + +This is the **aarch64-pc-windows-msvc** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/win32-arm64-msvc/package.json b/packages/argon2/npm/win32-arm64-msvc/package.json new file mode 100644 index 00000000..0ffbfbeb --- /dev/null +++ b/packages/argon2/npm/win32-arm64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-win32-arm64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "arm64" + ], + "main": "argon2.win32-arm64-msvc.node", + "files": [ + "argon2.win32-arm64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/win32-ia32-msvc/README.md b/packages/argon2/npm/win32-ia32-msvc/README.md new file mode 100644 index 00000000..712f0d5c --- /dev/null +++ b/packages/argon2/npm/win32-ia32-msvc/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-win32-ia32-msvc` + +This is the **i686-pc-windows-msvc** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/win32-ia32-msvc/package.json b/packages/argon2/npm/win32-ia32-msvc/package.json new file mode 100644 index 00000000..cebef5e3 --- /dev/null +++ b/packages/argon2/npm/win32-ia32-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-win32-ia32-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "ia32" + ], + "main": "argon2.win32-ia32-msvc.node", + "files": [ + "argon2.win32-ia32-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/npm/win32-x64-msvc/README.md b/packages/argon2/npm/win32-x64-msvc/README.md new file mode 100644 index 00000000..fd9ddadc --- /dev/null +++ b/packages/argon2/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `@node-rs/argon2-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `@node-rs/argon2` diff --git a/packages/argon2/npm/win32-x64-msvc/package.json b/packages/argon2/npm/win32-x64-msvc/package.json new file mode 100644 index 00000000..83b5adcc --- /dev/null +++ b/packages/argon2/npm/win32-x64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "@node-rs/argon2-win32-x64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "argon2.win32-x64-msvc.node", + "files": [ + "argon2.win32-x64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/argon2/package.json b/packages/argon2/package.json new file mode 100644 index 00000000..8ac48182 --- /dev/null +++ b/packages/argon2/package.json @@ -0,0 +1,38 @@ +{ + "name": "@node-rs/argon2", + "version": "0.0.0", + "main": "index.js", + "types": "index.d.ts", + "napi": { + "name": "argon2", + "triples": { + "additional": [ + "aarch64-apple-darwin", + "aarch64-linux-android", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "aarch64-pc-windows-msvc", + "armv7-unknown-linux-gnueabihf", + "x86_64-unknown-linux-musl", + "x86_64-unknown-freebsd", + "i686-pc-windows-msvc", + "armv7-linux-androideabi" + ] + } + }, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "scripts": { + "artifacts": "napi artifacts -d ../../artifacts", + "bench": "cross-env NODE_ENV=production node benchmark/argon2.js", + "build": "napi build --platform --release --pipe \"prettier -w\"", + "build:debug": "napi build --platform --pipe \"prettier -w\"", + "prepublishOnly": "napi prepublish", + "version": "napi version" + }, + "devDependencies": { + "argon2": "^0.28.3" + } +} diff --git a/packages/argon2/src/lib.rs b/packages/argon2/src/lib.rs new file mode 100644 index 00000000..391fb021 --- /dev/null +++ b/packages/argon2/src/lib.rs @@ -0,0 +1,175 @@ +#![deny(clippy::all)] + +use argon2::{ + password_hash::{rand_core::OsRng, PasswordHasher, PasswordVerifier, SaltString}, + Argon2, +}; +use napi::bindgen_prelude::*; +use napi_derive::napi; + +#[napi] +pub enum Algorithm { + Argon2d, + Argon2i, + Argon2id, +} + +impl Algorithm { + #[inline] + fn to_argon(self) -> argon2::Algorithm { + match self { + Self::Argon2d => argon2::Algorithm::Argon2d, + Self::Argon2i => argon2::Algorithm::Argon2i, + Self::Argon2id => argon2::Algorithm::Argon2id, + } + } +} + +#[napi] +pub enum Version { + V0x10, + V0x13, +} + +impl Version { + #[inline] + fn to_argon(self) -> argon2::Version { + match self { + Self::V0x10 => argon2::Version::V0x10, + Self::V0x13 => argon2::Version::V0x13, + } + } +} + +#[napi(object)] +#[derive(Default)] +pub struct Options { + /// Memory size, expressed in kilobytes, between 1 and (2^32)-1. + /// Value is an integer in decimal (1 to 10 digits). + pub memory_cost: Option, + /// Number of iterations, between 1 and (2^32)-1. + /// Value is an integer in decimal (1 to 10 digits). + pub time_cost: Option, + /// Degree of parallelism, between 1 and 255. + /// Value is an integer in decimal (1 to 3 digits). + pub output_len: Option, + pub parallelism: Option, + pub algorithm: Option, + pub version: Option, + pub secret: Option, +} + +impl Options { + #[inline] + fn to_argon(&self) -> std::result::Result { + let algorithm = self.algorithm.map(|a| a.to_argon()).unwrap_or_default(); + let version = self.version.map(|v| v.to_argon()).unwrap_or_default(); + let params = argon2::Params::new( + self.memory_cost.unwrap_or(argon2::Params::DEFAULT_M_COST), + self.time_cost.unwrap_or(argon2::Params::DEFAULT_T_COST), + self.parallelism.unwrap_or(argon2::Params::DEFAULT_P_COST), + self.output_len.map(|o| o as usize), + )?; + if let Some(sec) = &self.secret { + Argon2::new_with_secret(sec.as_ref(), algorithm, version, params) + } else { + Ok(Argon2::new(algorithm, version, params)) + } + } +} + +pub struct HashTask { + password: Vec, + options: Options, +} + +#[napi] +impl Task for HashTask { + type Output = String; + type JsValue = String; + + fn compute(&mut self) -> Result { + let salt = SaltString::generate(&mut OsRng); + let hasher = self.options.to_argon(); + hasher + .map_err(|err| Error::new(Status::InvalidArg, format!("{}", err)))? + .hash_password(self.password.as_slice(), &salt) + .map_err(|err| Error::new(Status::GenericFailure, format!("{}", err))) + .map(|h| h.to_string()) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[napi] +pub fn hash( + password: Either, + options: Option, + abort_signal: Option, +) -> AsyncTask { + AsyncTask::with_optional_signal( + HashTask { + password: match password { + Either::A(s) => s.as_bytes().to_vec(), + Either::B(b) => b.to_vec(), + }, + options: options.unwrap_or_default(), + }, + abort_signal, + ) +} + +pub struct VerifyTask { + password: String, + hashed: String, + options: Options, +} + +#[napi] +impl Task for VerifyTask { + type Output = bool; + type JsValue = bool; + + fn compute(&mut self) -> Result { + let parsed_hash = argon2::PasswordHash::new(self.hashed.as_str()) + .map_err(|err| Error::new(Status::InvalidArg, format!("{}", err)))?; + let argon2 = self.options.to_argon(); + Ok( + argon2 + .map_err(|err| Error::new(Status::InvalidArg, format!("{}", err)))? + .verify_password(self.password.as_bytes(), &parsed_hash) + .is_ok(), + ) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[napi] +pub fn verify( + hashed: Either, + password: Either, + options: Option, + abort_signal: Option, +) -> Result> { + Ok(AsyncTask::with_optional_signal( + VerifyTask { + password: match password { + Either::A(s) => s, + Either::B(b) => String::from_utf8(b.to_vec()) + .map_err(|err| Error::new(Status::InvalidArg, format!("{}", err)))?, + }, + hashed: match hashed { + Either::A(s) => s, + Either::B(b) => String::from_utf8(b.to_vec()) + .map_err(|err| Error::new(Status::InvalidArg, format!("{}", err)))?, + }, + options: options.unwrap_or_default(), + }, + abort_signal, + )) +} diff --git a/packages/crc32/package.json b/packages/crc32/package.json index 690376b8..405519b7 100644 --- a/packages/crc32/package.json +++ b/packages/crc32/package.json @@ -62,6 +62,7 @@ }, "devDependencies": { "@types/crc": "^3.4.0", + "buffer": "^6.0.3", "crc": "^4.0.0", "sse4_crc32": "^6.0.1" }, diff --git a/yarn.lock b/yarn.lock index fceb65fd..156c9c26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -918,7 +918,7 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" -"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.4": +"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.4", "@mapbox/node-pre-gyp@^1.0.7": version "1.0.8" resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz#32abc8a5c624bc4e46c43d84dfb8b26d33a96f58" integrity sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg== @@ -1135,6 +1135,11 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@phc/format@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" + integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1727,6 +1732,16 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +argon2@^0.28.3: + version "0.28.3" + resolved "https://registry.npmjs.org/argon2/-/argon2-0.28.3.tgz#e5234eccf20a643ffc3b1bbd1aa9e81092e0d8e9" + integrity sha512-NkEJOImg+T7nnkx6/Fy8EbjZsF20hbBBKdVP/YUxujuLTAjIODmrFeY4vVpekKwGAGDm6roXxluFQ+CIaoVrbg== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.7" + "@phc/format" "^1.0.0" + node-addon-api "^4.2.0" + opencollective-postinstall "^2.0.3" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2025,6 +2040,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtins@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -3791,7 +3814,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4981,6 +5004,11 @@ node-addon-api@^3.0.2, node-addon-api@^3.1.0: resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-addon-api@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" + integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== + node-fetch@^2.6.1, node-fetch@^2.6.5: version "2.6.6" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" @@ -5321,6 +5349,11 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + optionator@^0.9.1: version "0.9.1" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"