Skip to content

Commit b1d2878

Browse files
panvatargos
authored andcommitted
crypto: add Argon2 Web Cryptography algorithms
PR-URL: #59544 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
1 parent 430691d commit b1d2878

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: v24.7.0
69
pr-url: https://github.com/nodejs/node/pull/59539
710
description: AES-OCB algorithm is now supported.
@@ -107,15 +110,18 @@ WICG proposal:
107110
Algorithms:
108111

109112
* `'AES-OCB'`[^openssl30]
113+
* `'Argon2d'`[^openssl32]
114+
* `'Argon2i'`[^openssl32]
115+
* `'Argon2id'`[^openssl32]
110116
* `'ChaCha20-Poly1305'`
111117
* `'cSHAKE128'`
112118
* `'cSHAKE256'`
113119
* `'ML-DSA-44'`[^openssl35]
114120
* `'ML-DSA-65'`[^openssl35]
115121
* `'ML-DSA-87'`[^openssl35]
116-
* `'ML-KEM-1024'`[^openssl35]
117122
* `'ML-KEM-512'`[^openssl35]
118123
* `'ML-KEM-768'`[^openssl35]
124+
* `'ML-KEM-1024'`[^openssl35]
119125
* `'SHA3-256'`
120126
* `'SHA3-384'`
121127
* `'SHA3-512'`
@@ -505,6 +511,9 @@ implementation and the APIs supported for each:
505511
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
506512
| `'AES-KW'` | ✔ | ✔ | ✔ | |
507513
| `'AES-OCB'` | ✔ | ✔ | ✔ | |
514+
| `'Argon2d'` | | | ✔ | |
515+
| `'Argon2i'` | | | ✔ | |
516+
| `'Argon2id'` | | | ✔ | |
508517
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
509518
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
510519
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
@@ -544,6 +553,9 @@ implementation and the APIs supported for each:
544553
| `'AES-GCM'` | ✔ | | | ✔ | | |
545554
| `'AES-KW'` | | | | ✔ | | |
546555
| `'AES-OCB'` | ✔ | | | ✔ | | |
556+
| `'Argon2d'` | | | ✔ | | | |
557+
| `'Argon2i'` | | | ✔ | | | |
558+
| `'Argon2id'` | | | ✔ | | | |
547559
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
548560
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
549561
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
@@ -713,6 +725,9 @@ Valid key usages depend on the key algorithm (identified by
713725
| `'AES-GCM'` | ✔ | | | ✔ | |
714726
| `'AES-KW'` | | | | ✔ | |
715727
| `'AES-OCB'` | ✔ | | | ✔ | |
728+
| `'Argon2d'` | | | ✔ | | |
729+
| `'Argon2i'` | | | ✔ | | |
730+
| `'Argon2id'` | | | ✔ | | |
716731
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
717732
| `'ECDH'` | | | ✔ | | |
718733
| `'ECDSA'` | | ✔ | | | |
@@ -863,6 +878,9 @@ The algorithms currently supported include:
863878
<!-- YAML
864879
added: v15.0.0
865880
changes:
881+
- version: REPLACEME
882+
pr-url: https://github.com/nodejs/node/pull/59544
883+
description: Argon2 algorithms are now supported.
866884
- version:
867885
- v22.5.0
868886
- v20.17.0
@@ -879,7 +897,7 @@ changes:
879897
880898
<!--lint disable maximum-line-length remark-lint-->
881899
882-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
900+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
883901
* `baseKey` {CryptoKey}
884902
* `length` {number|null} **Default:** `null`
885903
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
@@ -899,6 +917,9 @@ containing the generated data.
899917
900918
The algorithms currently supported include:
901919
920+
* `'Argon2d'`[^modern-algos]
921+
* `'Argon2i'`[^modern-algos]
922+
* `'Argon2id'`[^modern-algos]
902923
* `'ECDH'`
903924
* `'HKDF'`
904925
* `'PBKDF2'`
@@ -910,6 +931,9 @@ The algorithms currently supported include:
910931
<!-- YAML
911932
added: v15.0.0
912933
changes:
934+
- version: REPLACEME
935+
pr-url: https://github.com/nodejs/node/pull/59544
936+
description: Argon2 algorithms are now supported.
913937
- version:
914938
- v18.4.0
915939
- v16.17.0
@@ -919,7 +943,7 @@ changes:
919943
920944
<!--lint disable maximum-line-length remark-lint-->
921945
922-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
946+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
923947
* `baseKey` {CryptoKey}
924948
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
925949
* `extractable` {boolean}
@@ -939,6 +963,9 @@ generate raw keying material, then passing the result into the
939963
940964
The algorithms currently supported include:
941965
966+
* `'Argon2d'`[^modern-algos]
967+
* `'Argon2i'`[^modern-algos]
968+
* `'Argon2id'`[^modern-algos]
942969
* `'ECDH'`
943970
* `'HKDF'`
944971
* `'PBKDF2'`
@@ -1234,7 +1261,7 @@ as the given `format` to create a {CryptoKey} instance using the provided
12341261
`algorithm`, `extractable`, and `keyUsages` arguments. If the import is
12351262
successful, the returned promise will be resolved with the created {CryptoKey}.
12361263
1237-
If importing a `'PBKDF2'` key, `extractable` must be `false`.
1264+
If importing KDF algorithm keys, `extractable` must be `false`.
12381265
12391266
The algorithms currently supported include:
12401267
@@ -1245,6 +1272,9 @@ The algorithms currently supported include:
12451272
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
12461273
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
12471274
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
1275+
| `'Argon2d'`[^modern-algos] | | | | | ✔ | | |
1276+
| `'Argon2i'`[^modern-algos] | | | | | ✔ | | |
1277+
| `'Argon2id'`[^modern-algos] | | | | | ✔ | | |
12481278
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
12491279
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
12501280
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1665,6 +1695,90 @@ added: v15.0.0
16651695
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
16661696
`'AES-KW'`
16671697
1698+
### Class: `Argon2Params`
1699+
1700+
<!-- YAML
1701+
added: REPLACEME
1702+
-->
1703+
1704+
#### `argon2Params.associatedData`
1705+
1706+
<!-- YAML
1707+
added: REPLACEME
1708+
-->
1709+
1710+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1711+
1712+
Represents the optional associated data.
1713+
1714+
#### `argon2Params.memory`
1715+
1716+
<!-- YAML
1717+
added: REPLACEME
1718+
-->
1719+
1720+
* Type: {number}
1721+
1722+
Represents the memory size in kibibytes. It must be at least 8 times the degree of parallelism.
1723+
1724+
#### `argon2Params.name`
1725+
1726+
<!-- YAML
1727+
added: REPLACEME
1728+
-->
1729+
1730+
* Type: {string} Must be one of `'Argon2d'`, `'Argon2i'`, or `'Argon2id'`.
1731+
1732+
#### `argon2Params.nonce`
1733+
1734+
<!-- YAML
1735+
added: REPLACEME
1736+
-->
1737+
1738+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1739+
1740+
Represents the nonce, which is a salt for password hashing applications.
1741+
1742+
#### `argon2Params.parallelism`
1743+
1744+
<!-- YAML
1745+
added: REPLACEME
1746+
-->
1747+
1748+
* Type: {number}
1749+
1750+
Represents the degree of parallelism.
1751+
1752+
#### `argon2Params.passes`
1753+
1754+
<!-- YAML
1755+
added: REPLACEME
1756+
-->
1757+
1758+
* Type: {number}
1759+
1760+
Represents the number of passes.
1761+
1762+
#### `argon2Params.secretValue`
1763+
1764+
<!-- YAML
1765+
added: REPLACEME
1766+
-->
1767+
1768+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1769+
1770+
Represents the optional secret value.
1771+
1772+
#### `argon2Params.version`
1773+
1774+
<!-- YAML
1775+
added: REPLACEME
1776+
-->
1777+
1778+
* Type: {number}
1779+
1780+
Represents the Argon2 version number. The default and currently only defined version is `19` (`0x13`).
1781+
16681782
### Class: `ContextParams`
16691783
16701784
<!-- YAML
@@ -2419,6 +2533,8 @@ The length (in bytes) of the random salt to use.
24192533
24202534
[^openssl30]: Requires OpenSSL >= 3.0
24212535
2536+
[^openssl32]: Requires OpenSSL >= 3.2
2537+
24222538
[^openssl35]: Requires OpenSSL >= 3.5
24232539
24242540
[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)