@pirxpilot/argon2 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +95 -0
- package/argon2.js +178 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Ranieri Althoff
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[![NPM version][npm-image]][npm-url]
|
|
2
|
+
[![Build Status][build-image]][build-url]
|
|
3
|
+
[![Dependency Status][deps-image]][deps-url]
|
|
4
|
+
[![Financial contributors on Open Collective][opencollective-image]][opencollective-url]
|
|
5
|
+
|
|
6
|
+
# @pirxpilot/argon2
|
|
7
|
+
|
|
8
|
+
This is a fork of [node-argon2] that is using [`crypto.argon2`][crypto.argon2] implementation as described in [#469]
|
|
9
|
+
It can be used with node >= 24.7.0
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
It's possible to hash using either Argon2i, Argon2d or Argon2id (default), and
|
|
13
|
+
verify if a password matches a hash.
|
|
14
|
+
|
|
15
|
+
To hash a password:
|
|
16
|
+
```js
|
|
17
|
+
import * as argon2 from '@pirxpilot/argon2';
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const hash = await argon2.hash("password");
|
|
21
|
+
} catch (err) {
|
|
22
|
+
//...
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To verify a password:
|
|
27
|
+
```js
|
|
28
|
+
try {
|
|
29
|
+
if (await argon2.verify("<big long hash>", "password")) {
|
|
30
|
+
// password match
|
|
31
|
+
} else {
|
|
32
|
+
// password did not match
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// internal failure
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> [!NOTE]
|
|
40
|
+
> By default, argon2.hash will generate secure hashes according to the security recommendations by the team that develops Argon2.
|
|
41
|
+
> **For password hashing, there is no need to modify them.**
|
|
42
|
+
|
|
43
|
+
To see how you can modify the output (hash length, encoding) and parameters
|
|
44
|
+
(time cost, memory cost and parallelism),
|
|
45
|
+
[read the wiki](https://github.com/ranisalt/node-argon2/wiki/Options)
|
|
46
|
+
|
|
47
|
+
## Contributors
|
|
48
|
+
|
|
49
|
+
### Code contributors
|
|
50
|
+
|
|
51
|
+
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
|
52
|
+
<a href="https://github.com/ranisalt/node-argon2/graphs/contributors"><img src="https://opencollective.com/node-argon2/contributors.svg?width=890&button=false" /></a>
|
|
53
|
+
|
|
54
|
+
### Financial contributors
|
|
55
|
+
|
|
56
|
+
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/node-argon2/contribute)]
|
|
57
|
+
|
|
58
|
+
#### Individuals
|
|
59
|
+
|
|
60
|
+
<a href="https://opencollective.com/node-argon2"><img src="https://opencollective.com/node-argon2/individuals.svg?width=890"></a>
|
|
61
|
+
|
|
62
|
+
#### Organizations
|
|
63
|
+
|
|
64
|
+
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/node-argon2/contribute)]
|
|
65
|
+
|
|
66
|
+
<a href="https://opencollective.com/node-argon2/organization/0/website"><img src="https://opencollective.com/node-argon2/organization/0/avatar.svg"></a>
|
|
67
|
+
<a href="https://opencollective.com/node-argon2/organization/1/website"><img src="https://opencollective.com/node-argon2/organization/1/avatar.svg"></a>
|
|
68
|
+
<a href="https://opencollective.com/node-argon2/organization/2/website"><img src="https://opencollective.com/node-argon2/organization/2/avatar.svg"></a>
|
|
69
|
+
<a href="https://opencollective.com/node-argon2/organization/3/website"><img src="https://opencollective.com/node-argon2/organization/3/avatar.svg"></a>
|
|
70
|
+
<a href="https://opencollective.com/node-argon2/organization/4/website"><img src="https://opencollective.com/node-argon2/organization/4/avatar.svg"></a>
|
|
71
|
+
<a href="https://opencollective.com/node-argon2/organization/5/website"><img src="https://opencollective.com/node-argon2/organization/5/avatar.svg"></a>
|
|
72
|
+
<a href="https://opencollective.com/node-argon2/organization/6/website"><img src="https://opencollective.com/node-argon2/organization/6/avatar.svg"></a>
|
|
73
|
+
<a href="https://opencollective.com/node-argon2/organization/7/website"><img src="https://opencollective.com/node-argon2/organization/7/avatar.svg"></a>
|
|
74
|
+
<a href="https://opencollective.com/node-argon2/organization/8/website"><img src="https://opencollective.com/node-argon2/organization/8/avatar.svg"></a>
|
|
75
|
+
<a href="https://opencollective.com/node-argon2/organization/9/website"><img src="https://opencollective.com/node-argon2/organization/9/avatar.svg"></a>
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
Work licensed under the [MIT License](LICENSE). Please check
|
|
79
|
+
[P-H-C/phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2) for
|
|
80
|
+
license over Argon2 and the reference implementation.
|
|
81
|
+
|
|
82
|
+
[opencollective-image]: https://img.shields.io/opencollective/all/node-argon2.svg?style=flat-square
|
|
83
|
+
[opencollective-url]: https://opencollective.com/node-argon2
|
|
84
|
+
[node-argon2]: https://www.npmjs.com/package/argon2
|
|
85
|
+
[#469]: https://github.com/ranisalt/node-argon2/issues/469
|
|
86
|
+
[crypto.argon2]: https://nodejs.org/api/crypto.html#cryptoargon2algorithm-parameters-callback
|
|
87
|
+
|
|
88
|
+
[npm-image]: https://img.shields.io/npm/v/@pirxpilot/argon2
|
|
89
|
+
[npm-url]: https://npmjs.org/package/@pirxpilot/argon2
|
|
90
|
+
|
|
91
|
+
[build-url]: https://github.com/pirxpilot/argon2/actions/workflows/check.yaml
|
|
92
|
+
[build-image]: https://img.shields.io/github/actions/workflow/status/pirxpilot/argon2/check.yaml?branch=main
|
|
93
|
+
|
|
94
|
+
[deps-image]: https://img.shields.io/librariesio/release/npm/@pirxpilot/argon2
|
|
95
|
+
[deps-url]: https://libraries.io/npm/@pirxpilot%2Fargon2
|
package/argon2.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { argon2, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { deserialize, serialize } from '@phc/format';
|
|
4
|
+
|
|
5
|
+
const asyncArgon2 = promisify(argon2);
|
|
6
|
+
|
|
7
|
+
/** @type {(size: number) => Promise<Buffer>} */
|
|
8
|
+
const generateSalt = promisify(randomBytes);
|
|
9
|
+
|
|
10
|
+
export const argon2d = 0;
|
|
11
|
+
export const argon2i = 1;
|
|
12
|
+
export const argon2id = 2;
|
|
13
|
+
|
|
14
|
+
/** @enum {argon2i | argon2d | argon2id} */
|
|
15
|
+
const types = Object.freeze({ argon2d, argon2i, argon2id });
|
|
16
|
+
|
|
17
|
+
/** @enum {'argon2d' | 'argon2i' | 'argon2id'} */
|
|
18
|
+
const names = Object.freeze({
|
|
19
|
+
[types.argon2d]: 'argon2d',
|
|
20
|
+
[types.argon2i]: 'argon2i',
|
|
21
|
+
[types.argon2id]: 'argon2id'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const defaults = {
|
|
25
|
+
hashLength: 32,
|
|
26
|
+
timeCost: 3,
|
|
27
|
+
memoryCost: 1 << 16,
|
|
28
|
+
parallelism: 4,
|
|
29
|
+
type: argon2id,
|
|
30
|
+
version: 0x13
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} Options
|
|
35
|
+
* @property {number} [hashLength=32]
|
|
36
|
+
* @property {number} [timeCost=3]
|
|
37
|
+
* @property {number} [memoryCost=65536]
|
|
38
|
+
* @property {number} [parallelism=4]
|
|
39
|
+
* @property {keyof typeof names} [type=argon2id]
|
|
40
|
+
* @property {number} [version=19]
|
|
41
|
+
* @property {Buffer} [salt]
|
|
42
|
+
* @property {Buffer} [associatedData]
|
|
43
|
+
* @property {Buffer} [secret]
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Hashes a password with Argon2, producing a raw hash
|
|
48
|
+
*
|
|
49
|
+
* @overload
|
|
50
|
+
* @param {Buffer | string} password The plaintext password to be hashed
|
|
51
|
+
* @param {Options & { raw: true }} options The parameters for Argon2
|
|
52
|
+
* @returns {Promise<Buffer>} The raw hash generated from `password`
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Hashes a password with Argon2, producing an encoded hash
|
|
56
|
+
*
|
|
57
|
+
* @overload
|
|
58
|
+
* @param {Buffer | string} password The plaintext password to be hashed
|
|
59
|
+
* @param {Options & { raw?: boolean }} [options] The parameters for Argon2
|
|
60
|
+
* @returns {Promise<string>} The encoded hash generated from `password`
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* @param {Buffer | string} password The plaintext password to be hashed
|
|
64
|
+
* @param {Options & { raw?: boolean }} [options] The parameters for Argon2
|
|
65
|
+
*/
|
|
66
|
+
export async function hash(password, options) {
|
|
67
|
+
let { raw, salt, ...rest } = { ...defaults, ...options };
|
|
68
|
+
|
|
69
|
+
if (rest.hashLength > 2 ** 32 - 1) {
|
|
70
|
+
throw new RangeError('Hash length is too large');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (rest.memoryCost > 2 ** 32 - 1) {
|
|
74
|
+
throw new RangeError('Memory cost is too large');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (rest.timeCost > 2 ** 32 - 1) {
|
|
78
|
+
throw new RangeError('Time cost is too large');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (rest.parallelism > 2 ** 24 - 1) {
|
|
82
|
+
throw new RangeError('Parallelism is too large');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
salt = salt ?? (await generateSalt(16));
|
|
86
|
+
|
|
87
|
+
const {
|
|
88
|
+
hashLength,
|
|
89
|
+
secret = Buffer.alloc(0),
|
|
90
|
+
type,
|
|
91
|
+
version,
|
|
92
|
+
memoryCost: m,
|
|
93
|
+
timeCost: t,
|
|
94
|
+
parallelism: p,
|
|
95
|
+
associatedData: data = Buffer.alloc(0)
|
|
96
|
+
} = rest;
|
|
97
|
+
|
|
98
|
+
const hash = await asyncArgon2(names[type], {
|
|
99
|
+
message: Buffer.from(password),
|
|
100
|
+
nonce: salt,
|
|
101
|
+
tagLength: hashLength,
|
|
102
|
+
secret,
|
|
103
|
+
associatedData: data,
|
|
104
|
+
parallelism: p,
|
|
105
|
+
memory: m,
|
|
106
|
+
passes: t
|
|
107
|
+
});
|
|
108
|
+
if (raw) {
|
|
109
|
+
return hash;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return serialize({
|
|
113
|
+
id: names[type],
|
|
114
|
+
version,
|
|
115
|
+
params: { m, t, p, ...(data.byteLength > 0 ? { data } : {}) },
|
|
116
|
+
salt,
|
|
117
|
+
hash
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {string} digest The digest to be checked
|
|
123
|
+
* @param {Object} [options] The current parameters for Argon2
|
|
124
|
+
* @param {number} [options.timeCost=3]
|
|
125
|
+
* @param {number} [options.memoryCost=65536]
|
|
126
|
+
* @param {number} [options.parallelism=4]
|
|
127
|
+
* @param {number} [options.version=0x13]
|
|
128
|
+
* @returns {boolean} `true` if the digest parameters do not match the parameters in `options`, otherwise `false`
|
|
129
|
+
*/
|
|
130
|
+
export function needsRehash(digest, options = {}) {
|
|
131
|
+
const { memoryCost, timeCost, parallelism, version } = {
|
|
132
|
+
...defaults,
|
|
133
|
+
...options
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const {
|
|
137
|
+
version: v,
|
|
138
|
+
params: { m, t, p }
|
|
139
|
+
} = deserialize(digest);
|
|
140
|
+
|
|
141
|
+
return +v !== +version || +m !== +memoryCost || +t !== +timeCost || +p !== +parallelism;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {string} digest The digest to be checked
|
|
146
|
+
* @param {Buffer | string} password The plaintext password to be verified
|
|
147
|
+
* @param {Object} [options] The current parameters for Argon2
|
|
148
|
+
* @param {Buffer} [options.secret]
|
|
149
|
+
* @returns {Promise<boolean>} `true` if the digest parameters matches the hash generated from `password`, otherwise `false`
|
|
150
|
+
*/
|
|
151
|
+
export async function verify(digest, password, options = {}) {
|
|
152
|
+
const { id, ...rest } = deserialize(digest);
|
|
153
|
+
if (!(id in types)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const {
|
|
158
|
+
params: { m, t, p, data = '' },
|
|
159
|
+
salt,
|
|
160
|
+
hash
|
|
161
|
+
} = rest;
|
|
162
|
+
|
|
163
|
+
const { secret = Buffer.alloc(0) } = options;
|
|
164
|
+
|
|
165
|
+
return timingSafeEqual(
|
|
166
|
+
await asyncArgon2(names[types[id]], {
|
|
167
|
+
message: Buffer.from(password),
|
|
168
|
+
nonce: salt,
|
|
169
|
+
tagLength: hash.byteLength,
|
|
170
|
+
secret,
|
|
171
|
+
associatedData: Buffer.from(data, 'base64'),
|
|
172
|
+
parallelism: p,
|
|
173
|
+
memory: m,
|
|
174
|
+
passes: t
|
|
175
|
+
}),
|
|
176
|
+
hash
|
|
177
|
+
);
|
|
178
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pirxpilot/argon2",
|
|
3
|
+
"version": "0.44.0",
|
|
4
|
+
"description": "An Argon2 library for Node",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"argon2",
|
|
7
|
+
"crypto",
|
|
8
|
+
"encryption",
|
|
9
|
+
"hashing",
|
|
10
|
+
"password"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/pirxpilot/argon2.git"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Ranieri Althoff <ranisalt+argon2@gmail.com>",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"exports": "./argon2.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"argon2.js"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "make check"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@phc/format": "^1.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@biomejs/biome": "2.3.11"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">= 24.7.0"
|
|
34
|
+
},
|
|
35
|
+
"collective": {
|
|
36
|
+
"type": "opencollective",
|
|
37
|
+
"url": "https://opencollective.com/node-argon2"
|
|
38
|
+
}
|
|
39
|
+
}
|