Skip to content

Commit 7e37cf1

Browse files
committed
crypto: add Argon2 Web Cryptography algorithms
1 parent 589ef79 commit 7e37cf1

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/59569
710
description: ML-KEM algorithms are now supported.
@@ -104,15 +107,18 @@ WICG proposal:
104107

105108
Algorithms:
106109

110+
* `'Argon2d'`[^openssl32]
111+
* `'Argon2i'`[^openssl32]
112+
* `'Argon2id'`[^openssl32]
107113
* `'ChaCha20-Poly1305'`
108114
* `'cSHAKE128'`
109115
* `'cSHAKE256'`
110116
* `'ML-DSA-44'`[^openssl35]
111117
* `'ML-DSA-65'`[^openssl35]
112118
* `'ML-DSA-87'`[^openssl35]
113-
* `'ML-KEM-1024'`[^openssl35]
114119
* `'ML-KEM-512'`[^openssl35]
115120
* `'ML-KEM-768'`[^openssl35]
121+
* `'ML-KEM-1024'`[^openssl35]
116122
* `'SHA3-256'`
117123
* `'SHA3-384'`
118124
* `'SHA3-512'`
@@ -501,6 +507,9 @@ implementation and the APIs supported for each:
501507
| `'AES-CTR'` | ✔ | ✔ | ✔ | |
502508
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
503509
| `'AES-KW'` | ✔ | ✔ | ✔ | |
510+
| `'Argon2d'` | | | ✔ | |
511+
| `'Argon2i'` | | | ✔ | |
512+
| `'Argon2id'` | | | ✔ | |
504513
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
505514
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
506515
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
@@ -539,6 +548,9 @@ implementation and the APIs supported for each:
539548
| `'AES-CTR'` | ✔ | | | ✔ | | |
540549
| `'AES-GCM'` | ✔ | | | ✔ | | |
541550
| `'AES-KW'` | | | | ✔ | | |
551+
| `'Argon2d'` | | | ✔ | | | |
552+
| `'Argon2i'` | | | ✔ | | | |
553+
| `'Argon2id'` | | | ✔ | | | |
542554
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
543555
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
544556
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
@@ -707,6 +719,9 @@ Valid key usages depend on the key algorithm (identified by
707719
| `'AES-CTR'` | ✔ | | | ✔ | |
708720
| `'AES-GCM'` | ✔ | | | ✔ | |
709721
| `'AES-KW'` | | | | ✔ | |
722+
| `'Argon2d'` | | | ✔ | | |
723+
| `'Argon2i'` | | | ✔ | | |
724+
| `'Argon2id'` | | | ✔ | | |
710725
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
711726
| `'ECDH'` | | | ✔ | | |
712727
| `'ECDSA'` | | ✔ | | | |
@@ -853,6 +868,9 @@ The algorithms currently supported include:
853868
<!-- YAML
854869
added: v15.0.0
855870
changes:
871+
- version: REPLACEME
872+
pr-url: https://github.com/nodejs/node/pull/59544
873+
description: Argon2 algorithms are now supported.
856874
- version:
857875
- v22.5.0
858876
- v20.17.0
@@ -869,7 +887,7 @@ changes:
869887
870888
<!--lint disable maximum-line-length remark-lint-->
871889
872-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
890+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
873891
* `baseKey` {CryptoKey}
874892
* `length` {number|null} **Default:** `null`
875893
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
@@ -889,6 +907,9 @@ containing the generated data.
889907
890908
The algorithms currently supported include:
891909
910+
* `'Argon2d'`[^modern-algos]
911+
* `'Argon2i'`[^modern-algos]
912+
* `'Argon2id'`[^modern-algos]
892913
* `'ECDH'`
893914
* `'HKDF'`
894915
* `'PBKDF2'`
@@ -900,6 +921,9 @@ The algorithms currently supported include:
900921
<!-- YAML
901922
added: v15.0.0
902923
changes:
924+
- version: REPLACEME
925+
pr-url: https://github.com/nodejs/node/pull/59544
926+
description: Argon2 algorithms are now supported.
903927
- version:
904928
- v18.4.0
905929
- v16.17.0
@@ -909,7 +933,7 @@ changes:
909933
910934
<!--lint disable maximum-line-length remark-lint-->
911935
912-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
936+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
913937
* `baseKey` {CryptoKey}
914938
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
915939
* `extractable` {boolean}
@@ -929,6 +953,9 @@ generate raw keying material, then passing the result into the
929953
930954
The algorithms currently supported include:
931955
956+
* `'Argon2d'`[^modern-algos]
957+
* `'Argon2i'`[^modern-algos]
958+
* `'Argon2id'`[^modern-algos]
932959
* `'ECDH'`
933960
* `'HKDF'`
934961
* `'PBKDF2'`
@@ -1218,7 +1245,7 @@ as the given `format` to create a {CryptoKey} instance using the provided
12181245
`algorithm`, `extractable`, and `keyUsages` arguments. If the import is
12191246
successful, the returned promise will be resolved with the created {CryptoKey}.
12201247
1221-
If importing a `'PBKDF2'` key, `extractable` must be `false`.
1248+
If importing KDF algorithm keys, `extractable` must be `false`.
12221249
12231250
The algorithms currently supported include:
12241251
@@ -1228,6 +1255,9 @@ The algorithms currently supported include:
12281255
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
12291256
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
12301257
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1258+
| `'Argon2d'`[^modern-algos] | | | | | ✔ | | |
1259+
| `'Argon2i'`[^modern-algos] | | | | | ✔ | | |
1260+
| `'Argon2id'`[^modern-algos] | | | | | ✔ | | |
12311261
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
12321262
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
12331263
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1640,6 +1670,90 @@ added: v15.0.0
16401670
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
16411671
`'AES-KW'`
16421672
1673+
### Class: `Argon2Params`
1674+
1675+
<!-- YAML
1676+
added: REPLACEME
1677+
-->
1678+
1679+
#### `argon2Params.associatedData`
1680+
1681+
<!-- YAML
1682+
added: REPLACEME
1683+
-->
1684+
1685+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1686+
1687+
Represents the optional associated data.
1688+
1689+
#### `argon2Params.memory`
1690+
1691+
<!-- YAML
1692+
added: REPLACEME
1693+
-->
1694+
1695+
* Type: {number}
1696+
1697+
Represents the memory size in kibibytes. It must be at least 8 times the degree of parallelism.
1698+
1699+
#### `argon2Params.name`
1700+
1701+
<!-- YAML
1702+
added: REPLACEME
1703+
-->
1704+
1705+
* Type: {string} Must be one of `'Argon2d'`, `'Argon2i'`, or `'Argon2id'`.
1706+
1707+
#### `argon2Params.nonce`
1708+
1709+
<!-- YAML
1710+
added: REPLACEME
1711+
-->
1712+
1713+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1714+
1715+
Represents the nonce, which is a salt for password hashing applications.
1716+
1717+
#### `argon2Params.parallelism`
1718+
1719+
<!-- YAML
1720+
added: REPLACEME
1721+
-->
1722+
1723+
* Type: {number}
1724+
1725+
Represents the degree of parallelism.
1726+
1727+
#### `argon2Params.passes`
1728+
1729+
<!-- YAML
1730+
added: REPLACEME
1731+
-->
1732+
1733+
* Type: {number}
1734+
1735+
Represents the number of passes.
1736+
1737+
#### `argon2Params.secretValue`
1738+
1739+
<!-- YAML
1740+
added: REPLACEME
1741+
-->
1742+
1743+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1744+
1745+
Represents the optional secret value.
1746+
1747+
#### `argon2Params.version`
1748+
1749+
<!-- YAML
1750+
added: REPLACEME
1751+
-->
1752+
1753+
* Type: {number}
1754+
1755+
Represents the Argon2 version number. The default and currently only defined version is `19` (`0x13`).
1756+
16431757
### Class: `ContextParams`
16441758
16451759
<!-- YAML
@@ -2392,6 +2506,8 @@ The length (in bytes) of the random salt to use.
23922506
23932507
[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]
23942508
2509+
[^openssl32]: Requires OpenSSL >= 3.2
2510+
23952511
[^openssl35]: Requires OpenSSL >= 3.5
23962512
23972513
[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
@@ -209,6 +209,12 @@ const {
209209
case 'HKDF':
210210
// Fall through
211211
case 'PBKDF2':
212+
// Fall through
213+
case 'Argon2d':
214+
// Fall through
215+
case 'Argon2i':
216+
// Fall through
217+
case 'Argon2id':
212218
result = importGenericSecretKey(
213219
algorithm,
214220
'KeyObject',
@@ -996,6 +1002,7 @@ function importGenericSecretKey(
9961002
keyObject = keyData;
9971003
break;
9981004
}
1005+
case 'raw-secret':
9991006
case 'raw': {
10001007
keyObject = createSecretKey(keyData);
10011008
break;

0 commit comments

Comments
 (0)