Skip to content

Commit

Permalink
Convert to ESM and general overhaul
Browse files Browse the repository at this point in the history
- Run in Node.js & Deno
- Strings accepted for passphrase & key arguments
- Use Web Crypto API in place of node:crypto (except OpenSSL scrypt, timingSafeEqual)
- Use GitHub Actions CI in place of Travis CI
  • Loading branch information
chrisveness committed Oct 18, 2024
1 parent e1531ee commit cd42554
Show file tree
Hide file tree
Showing 12 changed files with 598 additions and 354 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Scrypt.js GitHub Actions Node.js CI workflow

name: Node.js CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
node-version: [ 'lts/*', current ]

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} on ${{ matrix.os }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm install
- run: npm run test-node
13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

### Changed

- BREAKING: Convert to ESM
- Run in Node.js & Deno
- Strings accepted for passphrase & key arguments
- Use Web Crypto API in place of node:crypto (except OpenSSL scrypt, timingSafeEqual)
- Indicate received type when parameter type checks fail
- Use GitHub Actions CI in place of Travis CI

## [2.0.1] - 2019-05-03

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2018 Chris Veness
Copyright (c) 2018-2024 Chris Veness

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
38 changes: 9 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
Scrypt Key Derivation Function
==============================

[![Build Status](https://travis-ci.org/chrisveness/scrypt-kdf.svg?branch=master)](https://travis-ci.org/chrisveness/scrypt-kdf)
[![Coverage Status](https://coveralls.io/repos/github/chrisveness/scrypt-kdf/badge.svg?branch=master)](https://coveralls.io/github/chrisveness/scrypt-kdf?branch=master)
![Node.js CI](https://github.com/chrisveness/scrypt-kdf/actions/workflows/node.js.yml/badge.svg)

Scrypt is a *password-based [key derivation function](https://en.wikipedia.org/wiki/Key_derivation_function)*, useful for storing password hashes for verifying interactive logins.

Expand All @@ -20,36 +19,30 @@ It was originally developed by Colin Percival as part of the [Tarsnap](http://ww

Example usage
-------------

`scrypt-kdf` is available from [npm](https://www.npmjs.com/package/scrypt-kdf):

$ npm install scrypt-kdf

### hashing

const Scrypt = require('scrypt-kdf');
import Scrypt from 'scrypt-kdf');

const keyBuf = await Scrypt.kdf('my secret pw', { logN: 15 });
const keyStr = keyBuf.toString('base64');
// keyStr is 128-char string which can be stored for subsequent verification

### verifying

const Scrypt = require('scrypt-kdf');
import Scrypt from 'scrypt-kdf');

const user = await users.findOne({ email: req.body.email }); // for example
const keyBuf = Buffer.from(user.password, 'base64');
const ok = await Scrypt.verify(keyBuf, req.body.password);

### ES modules

If using ES modules (for instance with the [esm](https://www.npmjs.com/package/esm) package), use

import Scrypt from 'scrypt-kdf';
### in Deno:

in place of

const Scrypt = require('scrypt-kdf');
import Scrypt from 'npm:scrypt-kdf@^3';

API
---
Expand Down Expand Up @@ -95,20 +88,7 @@ Note that results are dependent on the computer the calculation is run on; calcu
OpenSSL implementation
----------------------

`scrypt-kdf` is a wrapper around the [OpenSSL](https://www.openssl.org/docs/manmaster/man7/scrypt.html) implementation of scrypt made available through the Node.js [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback).

Scrypt was introduced into Node.js in [v10.5.0](https://nodejs.org/en/blog/release/v10.5.0/), so `scrypt-kdf` requires Node.js v10.5.0 or above; it can also be used with Node.js v8.5.0...v10.4.1 using the [scrypt-async](https://www.npmjs.com/package/scrypt-async) OpenSSL polyfill with the following code fragment:

const crypto = require('crypto');
if (!crypto.scrypt) {
const scryptAsync = require('scrypt-async');
crypto.scrypt = function(password, salt, keylen, options, callback) {
const opt = Object.assign({}, options, { dkLen: keylen });
scryptAsync(password, salt, opt, (derivedKey) => callback(null, Buffer.from(derivedKey)));
};
}

`Scrypt.pickParams()` will not be available with this polyfill.
`scrypt-kdf` is a wrapper around the [OpenSSL](https://www.openssl.org/1.1.1/man7/scrypt.html) implementation of scrypt made available through the Node.js [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback).


Key format
Expand Down
40 changes: 40 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import eslintjs from '@eslint/js';
import globals from 'globals';

export default [
eslintjs.configs.recommended,
{ ignores: [ 'tmp/' ] }, // github.com/eslint/eslint/discussions/18304
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
Deno: 'readonly',
},
},
rules: {
'array-bracket-spacing': [ 'error', 'always' ],
'comma-dangle': [ 'error', 'always-multiline' ],
'comma-spacing': [ 'error' ],
'curly': [ 'error', 'multi-line' ],
'indent': [ 'error', 4, { 'SwitchCase': 1 } ],
'key-spacing': [ 'error', { 'align': 'value' } ],
'keyword-spacing': [ 'error' ],
'no-case-declarations': 'warn',
'no-console': [ 'warn', { 'allow': [ 'error', 'info', 'debug' ] } ],
'no-irregular-whitespace': 'warn',
'no-redeclare': 'warn',
'no-shadow': 'warn',
'no-unused-vars': 'warn',
'no-var': 'error',
'object-curly-spacing': [ 'error', 'always' ],
'prefer-const': 'error',
'quotes': [ 'error', 'single', 'avoid-escape' ],
'require-await': 'error',
'semi': [ 'error', 'always' ],
'space-before-blocks': [ 'error', 'always' ],
'space-in-parens': [ 'error' ],
'strict': [ 'error', 'global' ],
},
},
];
43 changes: 15 additions & 28 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,46 +1,33 @@
{
"name": "scrypt-kdf",
"description": "Scrypt Key Derivation Function",
"keywords": [
"crypto",
"scrypt",
"kdf",
"password",
"hash",
"login",
"authenticate",
"verify"
],
"keywords": [ "crypto", "scrypt", "kdf", "password", "hash", "login", "authenticate", "verify" ],
"author": "Chris Veness",
"repository": {
"type": "git",
"url": "https://github.com/chrisveness/scrypt-kdf"
},
"version": "2.0.1",
"version": "3.0.0",
"license": "MIT",
"main": "scrypt.js",
"type": "module",
"types": "scrypt.d.ts",
"engines": {
"node": ">=8.5.0"
"node": ">=18.0.0"
},
"scripts": {
"test": "mocha --exit test/scrypt-tests.js",
"lint": "eslint scrypt.js test/scrypt-tests.js",
"cover": "c8 -r html npm test"
"test": "npm run test-node && npm run test-deno",
"test-node": "node --test test/tests-node.js",
"test-deno": "deno test -NES test/tests-deno.js",
"lint": "eslint scrypt.js test/tests-*.js",
"cover": "c8 npm test"
},
"devDependencies": {
"c8": "^7.0.0",
"chai": "^4.0.0",
"coveralls": "^3.0.0",
"eslint": "^7.0.0",
"mocha": "^7.0.0"
},
"eslintConfig": {
"env": {
"es2017": true,
"mocha": true,
"node": true
},
"extends": "eslint:recommended"
"@babel/eslint-parser": "^7",
"@babel/plugin-syntax-import-attributes": "^7",
"@eslint/js": "^9",
"@types/node": "^22",
"c8": "^10",
"globals": "^15"
}
}
6 changes: 3 additions & 3 deletions scrypt.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export type ScryptParams = Required<ScryptParamsKdf>;
* @example
* const key = await Scrypt.kdf('my secret password', { logN: 15 });
*/
export declare function kdf(passphrase: string|ArrayBufferView, params: Readonly<ScryptParamsKdf>): Promise<Buffer>;
export declare function kdf(passphrase: string|Uint8Array|Buffer, params: Readonly<ScryptParamsKdf>): Promise<Buffer>;

/**
* Check whether key was generated from passphrase.
Expand All @@ -45,7 +45,7 @@ export declare function kdf(passphrase: string|ArrayBufferView, params: Readonly
* @example
* const ok = await Scrypt.verify(key, 'my secret password');
*/
export declare function verify(key: Uint8Array, passphrase: string|ArrayBufferView): Promise<boolean>;
export declare function verify(key: string|Uint8Array|Buffer, passphrase: string|Uint8Array|Buffer): Promise<boolean>;

/**
* View scrypt parameters which were used to derive key.
Expand All @@ -57,7 +57,7 @@ export declare function verify(key: Uint8Array, passphrase: string|ArrayBufferVi
* const key = await Scrypt.kdf('my secret password', { logN: 15 } );
* const params = Scrypt.viewParams(key); // => { logN: 15, r: 8, p: 1 }
*/
export declare function viewParams(key: Uint8Array): ScryptParams;
export declare function viewParams(key: string|Uint8Array|Buffer): ScryptParams;

/**
* Calculate scrypt parameters from maxtime, maxmem, maxmemfrac values.
Expand Down
Loading

0 comments on commit cd42554

Please sign in to comment.