Skip to content

Commit ae39143

Browse files
committed
crypto: add Argon2 Web Cryptography algorithms
1 parent 8692e60 commit ae39143

File tree

9 files changed

+452
-5
lines changed

9 files changed

+452
-5
lines changed

doc/api/webcrypto.md

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
<!-- YAML
44
changes:
5+
- version: REPLACEME
6+
pr-url: https://github.com/nodejs/node/pull/59544
7+
description: Argon2 algorithms are now supported.
58
- version: REPLACEME
69
pr-url: https://github.com/nodejs/node/pull/59539
710
description: AES-OCB algorithm is now supported.
@@ -108,15 +111,18 @@ WICG proposal:
108111
Algorithms:
109112

110113
* `'AES-OCB'`[^openssl30]
114+
* `'Argon2d'`[^openssl32]
115+
* `'Argon2i'`[^openssl32]
116+
* `'Argon2id'`[^openssl32]
111117
* `'ChaCha20-Poly1305'`
112118
* `'cSHAKE128'`
113119
* `'cSHAKE256'`
114120
* `'ML-DSA-44'`[^openssl35]
115121
* `'ML-DSA-65'`[^openssl35]
116122
* `'ML-DSA-87'`[^openssl35]
117-
* `'ML-KEM-1024'`[^openssl35]
118123
* `'ML-KEM-512'`[^openssl35]
119124
* `'ML-KEM-768'`[^openssl35]
125+
* `'ML-KEM-1024'`[^openssl35]
120126
* `'SHA3-256'`
121127
* `'SHA3-384'`
122128
* `'SHA3-512'`
@@ -506,6 +512,9 @@ implementation and the APIs supported for each:
506512
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
507513
| `'AES-KW'` | ✔ | ✔ | ✔ | |
508514
| `'AES-OCB'` | ✔ | ✔ | ✔ | |
515+
| `'Argon2d'` | | | ✔ | |
516+
| `'Argon2i'` | | | ✔ | |
517+
| `'Argon2id'` | | | ✔ | |
509518
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
510519
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
511520
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
@@ -545,6 +554,9 @@ implementation and the APIs supported for each:
545554
| `'AES-GCM'` | ✔ | | | ✔ | | |
546555
| `'AES-KW'` | | | | ✔ | | |
547556
| `'AES-OCB'` | ✔ | | | ✔ | | |
557+
| `'Argon2d'` | | | ✔ | | | |
558+
| `'Argon2i'` | | | ✔ | | | |
559+
| `'Argon2id'` | | | ✔ | | | |
548560
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
549561
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
550562
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
@@ -714,6 +726,9 @@ Valid key usages depend on the key algorithm (identified by
714726
| `'AES-GCM'` | ✔ | | | ✔ | |
715727
| `'AES-KW'` | | | | ✔ | |
716728
| `'AES-OCB'` | ✔ | | | ✔ | |
729+
| `'Argon2d'` | | | ✔ | | |
730+
| `'Argon2i'` | | | ✔ | | |
731+
| `'Argon2id'` | | | ✔ | | |
717732
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
718733
| `'ECDH'` | | | ✔ | | |
719734
| `'ECDSA'` | | ✔ | | | |
@@ -864,6 +879,9 @@ The algorithms currently supported include:
864879
<!-- YAML
865880
added: v15.0.0
866881
changes:
882+
- version: REPLACEME
883+
pr-url: https://github.com/nodejs/node/pull/59544
884+
description: Argon2 algorithms are now supported.
867885
- version:
868886
- v22.5.0
869887
- v20.17.0
@@ -880,7 +898,7 @@ changes:
880898
881899
<!--lint disable maximum-line-length remark-lint-->
882900
883-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
901+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
884902
* `baseKey` {CryptoKey}
885903
* `length` {number|null} **Default:** `null`
886904
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
@@ -900,6 +918,9 @@ containing the generated data.
900918
901919
The algorithms currently supported include:
902920
921+
* `'Argon2d'`[^modern-algos]
922+
* `'Argon2i'`[^modern-algos]
923+
* `'Argon2id'`[^modern-algos]
903924
* `'ECDH'`
904925
* `'HKDF'`
905926
* `'PBKDF2'`
@@ -911,6 +932,9 @@ The algorithms currently supported include:
911932
<!-- YAML
912933
added: v15.0.0
913934
changes:
935+
- version: REPLACEME
936+
pr-url: https://github.com/nodejs/node/pull/59544
937+
description: Argon2 algorithms are now supported.
914938
- version:
915939
- v18.4.0
916940
- v16.17.0
@@ -920,7 +944,7 @@ changes:
920944
921945
<!--lint disable maximum-line-length remark-lint-->
922946
923-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
947+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
924948
* `baseKey` {CryptoKey}
925949
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
926950
* `extractable` {boolean}
@@ -940,6 +964,9 @@ generate raw keying material, then passing the result into the
940964
941965
The algorithms currently supported include:
942966
967+
* `'Argon2d'`[^modern-algos]
968+
* `'Argon2i'`[^modern-algos]
969+
* `'Argon2id'`[^modern-algos]
943970
* `'ECDH'`
944971
* `'HKDF'`
945972
* `'PBKDF2'`
@@ -1235,7 +1262,7 @@ as the given `format` to create a {CryptoKey} instance using the provided
12351262
`algorithm`, `extractable`, and `keyUsages` arguments. If the import is
12361263
successful, the returned promise will be resolved with the created {CryptoKey}.
12371264
1238-
If importing a `'PBKDF2'` key, `extractable` must be `false`.
1265+
If importing KDF algorithm keys, `extractable` must be `false`.
12391266
12401267
The algorithms currently supported include:
12411268
@@ -1246,6 +1273,9 @@ The algorithms currently supported include:
12461273
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
12471274
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
12481275
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
1276+
| `'Argon2d'`[^modern-algos] | | | | | ✔ | | |
1277+
| `'Argon2i'`[^modern-algos] | | | | | ✔ | | |
1278+
| `'Argon2id'`[^modern-algos] | | | | | ✔ | | |
12491279
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
12501280
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
12511281
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1666,6 +1696,90 @@ added: v15.0.0
16661696
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
16671697
`'AES-KW'`
16681698
1699+
### Class: `Argon2Params`
1700+
1701+
<!-- YAML
1702+
added: REPLACEME
1703+
-->
1704+
1705+
#### `argon2Params.associatedData`
1706+
1707+
<!-- YAML
1708+
added: REPLACEME
1709+
-->
1710+
1711+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1712+
1713+
Represents the optional associated data.
1714+
1715+
#### `argon2Params.memory`
1716+
1717+
<!-- YAML
1718+
added: REPLACEME
1719+
-->
1720+
1721+
* Type: {number}
1722+
1723+
Represents the memory size in kibibytes. It must be at least 8 times the degree of parallelism.
1724+
1725+
#### `argon2Params.name`
1726+
1727+
<!-- YAML
1728+
added: REPLACEME
1729+
-->
1730+
1731+
* Type: {string} Must be one of `'Argon2d'`, `'Argon2i'`, or `'Argon2id'`.
1732+
1733+
#### `argon2Params.nonce`
1734+
1735+
<!-- YAML
1736+
added: REPLACEME
1737+
-->
1738+
1739+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1740+
1741+
Represents the nonce, which is a salt for password hashing applications.
1742+
1743+
#### `argon2Params.parallelism`
1744+
1745+
<!-- YAML
1746+
added: REPLACEME
1747+
-->
1748+
1749+
* Type: {number}
1750+
1751+
Represents the degree of parallelism.
1752+
1753+
#### `argon2Params.passes`
1754+
1755+
<!-- YAML
1756+
added: REPLACEME
1757+
-->
1758+
1759+
* Type: {number}
1760+
1761+
Represents the number of passes.
1762+
1763+
#### `argon2Params.secretValue`
1764+
1765+
<!-- YAML
1766+
added: REPLACEME
1767+
-->
1768+
1769+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1770+
1771+
Represents the optional secret value.
1772+
1773+
#### `argon2Params.version`
1774+
1775+
<!-- YAML
1776+
added: REPLACEME
1777+
-->
1778+
1779+
* Type: {number}
1780+
1781+
Represents the Argon2 version number. The default and currently only defined version is `19` (`0x13`).
1782+
16691783
### Class: `ContextParams`
16701784
16711785
<!-- YAML
@@ -2420,6 +2534,8 @@ The length (in bytes) of the random salt to use.
24202534
24212535
[^openssl30]: Requires OpenSSL >= 3.0
24222536
2537+
[^openssl32]: Requires OpenSSL >= 3.2
2538+
24232539
[^openssl35]: Requires OpenSSL >= 3.5
24242540
24252541
[JSON Web Key]: https://tools.ietf.org/html/rfc7517

lib/internal/crypto/argon2.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const {
44
FunctionPrototypeCall,
55
MathPow,
6+
StringPrototypeToLowerCase,
67
Uint8Array,
78
} = primordials;
89

@@ -17,7 +18,16 @@ const {
1718
kTypeArgon2id,
1819
} = internalBinding('crypto');
1920

20-
const { getArrayBufferOrView } = require('internal/crypto/util');
21+
const {
22+
lazyDOMException,
23+
promisify,
24+
} = require('internal/util');
25+
26+
const {
27+
getArrayBufferOrView,
28+
kKeyObject,
29+
} = require('internal/crypto/util');
30+
2131
const {
2232
validateString,
2333
validateFunction,
@@ -179,7 +189,54 @@ function check(algorithm, parameters) {
179189
return { message, nonce, secret, associatedData, tagLength, passes, parallelism, memory, type };
180190
}
181191

192+
const argon2Promise = promisify(argon2);
193+
function validateArgon2DeriveBitsLength(length) {
194+
if (length === null)
195+
throw lazyDOMException('length cannot be null', 'OperationError');
196+
197+
if (length % 8) {
198+
throw lazyDOMException(
199+
'length must be a multiple of 8',
200+
'OperationError');
201+
}
202+
203+
if (length < 32) {
204+
throw lazyDOMException(
205+
'length must be >= 32',
206+
'OperationError');
207+
}
208+
}
209+
210+
async function argon2DeriveBits(algorithm, baseKey, length) {
211+
validateArgon2DeriveBitsLength(length);
212+
213+
let result;
214+
try {
215+
result = await argon2Promise(
216+
StringPrototypeToLowerCase(algorithm.name),
217+
{
218+
message: baseKey[kKeyObject].export(),
219+
nonce: algorithm.nonce,
220+
parallelism: algorithm.parallelism,
221+
tagLength: length / 8,
222+
memory: algorithm.memory,
223+
passes: algorithm.passes,
224+
secret: algorithm.secretValue,
225+
associatedData: algorithm.associatedData,
226+
},
227+
);
228+
} catch (err) {
229+
throw lazyDOMException(
230+
'The operation failed for an operation-specific reason',
231+
{ name: 'OperationError', cause: err });
232+
}
233+
234+
return result.buffer;
235+
}
236+
182237
module.exports = {
183238
argon2,
184239
argon2Sync,
240+
argon2DeriveBits,
241+
validateArgon2DeriveBitsLength,
185242
};

lib/internal/crypto/keys.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ const {
211211
case 'HKDF':
212212
// Fall through
213213
case 'PBKDF2':
214+
// Fall through
215+
case 'Argon2d':
216+
// Fall through
217+
case 'Argon2i':
218+
// Fall through
219+
case 'Argon2id':
214220
result = importGenericSecretKey(
215221
algorithm,
216222
'KeyObject',
@@ -998,6 +1004,7 @@ function importGenericSecretKey(
9981004
keyObject = keyData;
9991005
break;
10001006
}
1007+
case 'raw-secret':
10011008
case 'raw': {
10021009
keyObject = createSecretKey(keyData);
10031010
break;

0 commit comments

Comments
 (0)