Skip to content

Commit 4cca046

Browse files
committed
crypto: add Argon2 Web Cryptography algorithms
1 parent bdcab71 commit 4cca046

File tree

9 files changed

+449
-4
lines changed

9 files changed

+449
-4
lines changed

doc/api/webcrypto.md

Lines changed: 116 additions & 3 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/59365
710
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -101,6 +104,9 @@ WICG proposal:
101104

102105
Algorithms:
103106

107+
* `'Argon2d'`[^openssl32]
108+
* `'Argon2i'`[^openssl32]
109+
* `'Argon2id'`[^openssl32]
104110
* `'ChaCha20-Poly1305'`
105111
* `'cSHAKE128'`
106112
* `'cSHAKE256'`
@@ -489,6 +495,9 @@ implementation and the APIs supported for each:
489495
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
490496
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
491497
| `'AES-KW'` | ✔ | ✔ | ✔ | | ✔ | | | | |
498+
| `'Argon2d'`[^modern-algos] | | | ✔ | | | ✔ | | | |
499+
| `'Argon2i'`[^modern-algos] | | | ✔ | | | ✔ | | | |
500+
| `'Argon2id'`[^modern-algos] | | | ✔ | | | ✔ | | | |
492501
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
493502
| `'cSHAKE128'`[^modern-algos] | | | | | | | | ✔ | |
494503
| `'cSHAKE256'`[^modern-algos] | | | | | | | | ✔ | |
@@ -641,6 +650,9 @@ Valid key usages depend on the key algorithm (identified by
641650
| `'AES-CTR'` | ✔ | ✔ | | | | | ✔ | ✔ |
642651
| `'AES-GCM'` | ✔ | ✔ | | | | | ✔ | ✔ |
643652
| `'AES-KW'` | | | | | | | ✔ | ✔ |
653+
| `'Argon2d'`[^modern-algos] | | | | | ✔ | ✔ | | |
654+
| `'Argon2i'`[^modern-algos] | | | | | ✔ | ✔ | | |
655+
| `'Argon2id'`[^modern-algos] | | | | | ✔ | ✔ | | |
644656
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | | | | | ✔ | ✔ |
645657
| `'ECDH'` | | | | | ✔ | ✔ | | |
646658
| `'ECDSA'` | | | ✔ | ✔ | | | | |
@@ -743,6 +755,9 @@ The algorithms currently supported include:
743755
<!-- YAML
744756
added: v15.0.0
745757
changes:
758+
- version: REPLACEME
759+
pr-url: https://github.com/nodejs/node/pull/59544
760+
description: Argon2 algorithms are now supported.
746761
- version:
747762
- v22.5.0
748763
- v20.17.0
@@ -759,7 +774,7 @@ changes:
759774
760775
<!--lint disable maximum-line-length remark-lint-->
761776
762-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
777+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
763778
* `baseKey` {CryptoKey}
764779
* `length` {number|null} **Default:** `null`
765780
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
@@ -779,6 +794,9 @@ containing the generated data.
779794
780795
The algorithms currently supported include:
781796
797+
* `'Argon2d'`[^modern-algos]
798+
* `'Argon2i'`[^modern-algos]
799+
* `'Argon2id'`[^modern-algos]
782800
* `'ECDH'`
783801
* `'HKDF'`
784802
* `'PBKDF2'`
@@ -790,6 +808,9 @@ The algorithms currently supported include:
790808
<!-- YAML
791809
added: v15.0.0
792810
changes:
811+
- version: REPLACEME
812+
pr-url: https://github.com/nodejs/node/pull/59544
813+
description: Argon2 algorithms are now supported.
793814
- version:
794815
- v18.4.0
795816
- v16.17.0
@@ -799,7 +820,7 @@ changes:
799820
800821
<!--lint disable maximum-line-length remark-lint-->
801822
802-
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
823+
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
803824
* `baseKey` {CryptoKey}
804825
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
805826
* `extractable` {boolean}
@@ -819,6 +840,9 @@ generate raw keying material, then passing the result into the
819840
820841
The algorithms currently supported include:
821842
843+
* `'Argon2d'`[^modern-algos]
844+
* `'Argon2i'`[^modern-algos]
845+
* `'Argon2id'`[^modern-algos]
822846
* `'ECDH'`
823847
* `'HKDF'`
824848
* `'PBKDF2'`
@@ -1054,7 +1078,7 @@ as the given `format` to create a {CryptoKey} instance using the provided
10541078
`algorithm`, `extractable`, and `keyUsages` arguments. If the import is
10551079
successful, the returned promise will be resolved with the created {CryptoKey}.
10561080
1057-
If importing a `'PBKDF2'` key, `extractable` must be `false`.
1081+
If importing KDF algorithm keys, `extractable` must be `false`.
10581082
10591083
The algorithms currently supported include:
10601084
@@ -1064,6 +1088,9 @@ The algorithms currently supported include:
10641088
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
10651089
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
10661090
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1091+
| `'Argon2d'`[^modern-algos] | | | | | ✔ | | |
1092+
| `'Argon2i'`[^modern-algos] | | | | | ✔ | | |
1093+
| `'Argon2id'`[^modern-algos] | | | | | ✔ | | |
10671094
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
10681095
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
10691096
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1470,6 +1497,90 @@ added: v15.0.0
14701497
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
14711498
`'AES-KW'`
14721499
1500+
### Class: `Argon2Params`
1501+
1502+
<!-- YAML
1503+
added: REPLACEME
1504+
-->
1505+
1506+
#### `argon2Params.associatedData`
1507+
1508+
<!-- YAML
1509+
added: REPLACEME
1510+
-->
1511+
1512+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1513+
1514+
Represents the optional associated data.
1515+
1516+
#### `argon2Params.memory`
1517+
1518+
<!-- YAML
1519+
added: REPLACEME
1520+
-->
1521+
1522+
* Type: {number}
1523+
1524+
Represents the memory size in kibibytes. It must be at least 8 times the degree of parallelism.
1525+
1526+
#### `argon2Params.name`
1527+
1528+
<!-- YAML
1529+
added: REPLACEME
1530+
-->
1531+
1532+
* Type: {string} Must be one of `'Argon2d'`, `'Argon2i'`, or `'Argon2id'`.
1533+
1534+
#### `argon2Params.nonce`
1535+
1536+
<!-- YAML
1537+
added: REPLACEME
1538+
-->
1539+
1540+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1541+
1542+
Represents the nonce, which is a salt for password hashing applications.
1543+
1544+
#### `argon2Params.parallelism`
1545+
1546+
<!-- YAML
1547+
added: REPLACEME
1548+
-->
1549+
1550+
* Type: {number}
1551+
1552+
Represents the degree of parallelism.
1553+
1554+
#### `argon2Params.passes`
1555+
1556+
<!-- YAML
1557+
added: REPLACEME
1558+
-->
1559+
1560+
* Type: {number}
1561+
1562+
Represents the number of passes.
1563+
1564+
#### `argon2Params.secretValue`
1565+
1566+
<!-- YAML
1567+
added: REPLACEME
1568+
-->
1569+
1570+
* Type: {ArrayBuffer|TypedArray|DataView|Buffer}
1571+
1572+
Represents the optional secret value.
1573+
1574+
#### `argon2Params.version`
1575+
1576+
<!-- YAML
1577+
added: REPLACEME
1578+
-->
1579+
1580+
* Type: {number}
1581+
1582+
Represents the Argon2 version number. The default and currently only defined version is `19` (`0x13`).
1583+
14731584
### Class: `ContextParams`
14741585
14751586
<!-- YAML
@@ -2178,6 +2289,8 @@ The length (in bytes) of the random salt to use.
21782289
21792290
[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]
21802291
2292+
[^openssl32]: Requires OpenSSL >= 3.2
2293+
21812294
[^openssl35]: Requires OpenSSL >= 3.5
21822295
21832296
[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
@@ -206,6 +206,12 @@ const {
206206
case 'HKDF':
207207
// Fall through
208208
case 'PBKDF2':
209+
// Fall through
210+
case 'Argon2d':
211+
// Fall through
212+
case 'Argon2i':
213+
// Fall through
214+
case 'Argon2id':
209215
result = importGenericSecretKey(
210216
algorithm,
211217
'KeyObject',
@@ -975,6 +981,7 @@ function importGenericSecretKey(
975981
keyObject = keyData;
976982
break;
977983
}
984+
case 'raw-secret':
978985
case 'raw': {
979986
keyObject = createSecretKey(keyData);
980987
break;

lib/internal/crypto/util.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const {
3636
EVP_PKEY_ML_DSA_44,
3737
EVP_PKEY_ML_DSA_65,
3838
EVP_PKEY_ML_DSA_87,
39+
Argon2Job,
3940
} = internalBinding('crypto');
4041

4142
const { getOptionValue } = require('internal/options');
@@ -205,6 +206,21 @@ const kAlgorithmDefinitions = {
205206
'wrapKey': null,
206207
'unwrapKey': null,
207208
},
209+
'Argon2d': {
210+
'deriveBits': 'Argon2Params',
211+
'get key length': null,
212+
'importKey': null,
213+
},
214+
'Argon2i': {
215+
'deriveBits': 'Argon2Params',
216+
'get key length': null,
217+
'importKey': null,
218+
},
219+
'Argon2id': {
220+
'deriveBits': 'Argon2Params',
221+
'get key length': null,
222+
'importKey': null,
223+
},
208224
'ChaCha20-Poly1305': {
209225
'generateKey': null,
210226
'exportKey': null,
@@ -326,6 +342,9 @@ const kAlgorithmDefinitions = {
326342
// Conditionally supported algorithms
327343
const conditionalAlgorithms = {
328344
'AES-KW': !process.features.openssl_is_boringssl,
345+
'Argon2d': !!Argon2Job,
346+
'Argon2i': !!Argon2Job,
347+
'Argon2id': !!Argon2Job,
329348
'ChaCha20-Poly1305': !process.features.openssl_is_boringssl ||
330349
ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
331350
'cSHAKE128': !process.features.openssl_is_boringssl ||
@@ -347,6 +366,9 @@ const conditionalAlgorithms = {
347366

348367
// Experimental algorithms
349368
const experimentalAlgorithms = [
369+
'Argon2d',
370+
'Argon2i',
371+
'Argon2id',
350372
'ChaCha20-Poly1305',
351373
'cSHAKE128',
352374
'cSHAKE256',
@@ -418,6 +440,11 @@ const simpleAlgorithmDictionaries = {
418440
functionName: 'BufferSource',
419441
customization: 'BufferSource',
420442
},
443+
Argon2Params: {
444+
associatedData: 'BufferSource',
445+
nonce: 'BufferSource',
446+
secretValue: 'BufferSource',
447+
},
421448
};
422449

423450
function validateMaxBufferLength(data, name) {

0 commit comments

Comments
 (0)