Skip to content

Commit a43cc3f

Browse files
committed
crypto: support Ed448 and ML-DSA context parameter in Web Cryptography
1 parent d30090b commit a43cc3f

File tree

21 files changed

+357
-204
lines changed

21 files changed

+357
-204
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4243,6 +4243,56 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInit(
42434243
return ctx;
42444244
}
42454245

4246+
std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::signInitWithContext(
4247+
const EVPKeyPointer& key,
4248+
const Digest& digest,
4249+
const Buffer<const unsigned char>& context_string) {
4250+
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
4251+
EVP_PKEY_CTX* ctx = nullptr;
4252+
4253+
const OSSL_PARAM params[] = {
4254+
OSSL_PARAM_construct_octet_string(
4255+
OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
4256+
const_cast<unsigned char*>(context_string.data),
4257+
context_string.len),
4258+
OSSL_PARAM_END};
4259+
4260+
const char* digest_name = digest ? EVP_MD_get0_name(digest) : nullptr;
4261+
if (!EVP_DigestSignInit_ex(
4262+
ctx_.get(), &ctx, digest_name, nullptr, nullptr, key.get(), params)) {
4263+
return std::nullopt;
4264+
}
4265+
return ctx;
4266+
#else
4267+
return std::nullopt;
4268+
#endif
4269+
}
4270+
4271+
std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInitWithContext(
4272+
const EVPKeyPointer& key,
4273+
const Digest& digest,
4274+
const Buffer<const unsigned char>& context_string) {
4275+
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
4276+
EVP_PKEY_CTX* ctx = nullptr;
4277+
4278+
const OSSL_PARAM params[] = {
4279+
OSSL_PARAM_construct_octet_string(
4280+
OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
4281+
const_cast<unsigned char*>(context_string.data),
4282+
context_string.len),
4283+
OSSL_PARAM_END};
4284+
4285+
const char* digest_name = digest ? EVP_MD_get0_name(digest) : nullptr;
4286+
if (!EVP_DigestVerifyInit_ex(
4287+
ctx_.get(), &ctx, digest_name, nullptr, nullptr, key.get(), params)) {
4288+
return std::nullopt;
4289+
}
4290+
return ctx;
4291+
#else
4292+
return std::nullopt;
4293+
#endif
4294+
}
4295+
42464296
DataPointer EVPMDCtxPointer::signOneShot(
42474297
const Buffer<const unsigned char>& buf) const {
42484298
if (!ctx_) return {};

deps/ncrypto/ncrypto.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,15 @@ class EVPMDCtxPointer final {
14031403
std::optional<EVP_PKEY_CTX*> verifyInit(const EVPKeyPointer& key,
14041404
const Digest& digest);
14051405

1406+
std::optional<EVP_PKEY_CTX*> signInitWithContext(
1407+
const EVPKeyPointer& key,
1408+
const Digest& digest,
1409+
const Buffer<const unsigned char>& context_string);
1410+
std::optional<EVP_PKEY_CTX*> verifyInitWithContext(
1411+
const EVPKeyPointer& key,
1412+
const Digest& digest,
1413+
const Buffer<const unsigned char>& context_string);
1414+
14061415
DataPointer signOneShot(const Buffer<const unsigned char>& buf) const;
14071416
DataPointer sign(const Buffer<const unsigned char>& buf) const;
14081417
bool verify(const Buffer<const unsigned char>& buf,

doc/api/webcrypto.md

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ changes:
10981098
10991099
<!--lint disable maximum-line-length remark-lint-->
11001100
1101-
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams}
1101+
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|ContextParams}
11021102
* `key` {CryptoKey}
11031103
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
11041104
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
@@ -1204,7 +1204,7 @@ changes:
12041204
12051205
<!--lint disable maximum-line-length remark-lint-->
12061206
1207-
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams}
1207+
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|ContextParams}
12081208
* `key` {CryptoKey}
12091209
* `signature` {ArrayBuffer|TypedArray|DataView|Buffer}
12101210
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
@@ -1482,20 +1482,23 @@ added: REPLACEME
14821482
added: REPLACEME
14831483
-->
14841484
1485-
* Type: {string} Must be `'ML-DSA-44'`[^modern-algos], `'ML-DSA-65'`[^modern-algos], or `'ML-DSA-87'`[^modern-algos].
1485+
* Type: {string} Must be `Ed448`[^secure-curves], `'ML-DSA-44'`[^modern-algos],
1486+
`'ML-DSA-65'`[^modern-algos], or `'ML-DSA-87'`[^modern-algos].
14861487
14871488
#### `contextParams.context`
14881489
14891490
<!-- YAML
14901491
added: REPLACEME
1492+
changes:
1493+
- version: REPLACEME
1494+
pr-url: https://github.com/nodejs/node/pull/00000
1495+
description: Non-empty context is now supported.
14911496
-->
14921497
14931498
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
14941499
14951500
The `context` member represents the optional context data to associate with
14961501
the message.
1497-
The Node.js Web Crypto API implementation only supports zero-length context
1498-
which is equivalent to not providing context at all.
14991502
15001503
### Class: `CShakeParams`
15011504
@@ -1676,37 +1679,6 @@ added: v15.0.0
16761679
16771680
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`.
16781681
1679-
### Class: `Ed448Params`
1680-
1681-
<!-- YAML
1682-
added: v15.0.0
1683-
-->
1684-
1685-
#### `ed448Params.name`
1686-
1687-
<!-- YAML
1688-
added:
1689-
- v18.4.0
1690-
- v16.17.0
1691-
-->
1692-
1693-
* Type: {string} Must be `'Ed448'`[^secure-curves].
1694-
1695-
#### `ed448Params.context`
1696-
1697-
<!-- YAML
1698-
added:
1699-
- v18.4.0
1700-
- v16.17.0
1701-
-->
1702-
1703-
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
1704-
1705-
The `context` member represents the optional context data to associate with
1706-
the message.
1707-
The Node.js Web Crypto API implementation only supports zero-length context
1708-
which is equivalent to not providing context at all.
1709-
17101682
### Class: `HkdfParams`
17111683
17121684
<!-- YAML

lib/internal/crypto/cfrg.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ function eddsaSignVerify(key, data, algorithm, signature) {
359359
undefined,
360360
undefined,
361361
undefined,
362+
algorithm.name === 'Ed448' ? algorithm.context : undefined,
362363
signature));
363364
}
364365

lib/internal/crypto/ec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
302302
undefined, // Salt length, not used with ECDSA
303303
undefined, // PSS Padding, not used with ECDSA
304304
kSigEncP1363,
305+
undefined,
305306
signature));
306307
}
307308

lib/internal/crypto/ml_dsa.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ function mlDsaSignVerify(key, data, algorithm, signature) {
309309
undefined,
310310
undefined,
311311
undefined,
312+
algorithm.context,
312313
signature));
313314
}
314315

lib/internal/crypto/rsa.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ function rsaSignVerify(key, data, { saltLength }, signature) {
355355
saltLength,
356356
key[kAlgorithm].name === 'RSA-PSS' ? RSA_PKCS1_PSS_PADDING : undefined,
357357
undefined,
358+
undefined,
358359
signature);
359360
});
360361
}

lib/internal/crypto/sig.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ function signOneShot(algorithm, data, key, callback) {
171171
algorithm,
172172
pssSaltLength,
173173
rsaPadding,
174-
dsaSigEnc);
174+
dsaSigEnc,
175+
undefined,
176+
undefined);
175177

176178
if (!callback) {
177179
const { 0: err, 1: signature } = job.run();
@@ -276,6 +278,7 @@ function verifyOneShot(algorithm, data, key, signature, callback) {
276278
pssSaltLength,
277279
rsaPadding,
278280
dsaSigEnc,
281+
undefined,
279282
signature);
280283

281284
if (!callback) {

lib/internal/crypto/util.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ const kAlgorithmDefinitions = {
239239
'generateKey': null,
240240
'exportKey': null,
241241
'importKey': null,
242-
'sign': 'Ed448Params',
243-
'verify': 'Ed448Params',
242+
'sign': 'ContextParams',
243+
'verify': 'ContextParams',
244244
},
245245
'HKDF': {
246246
'importKey': null,
@@ -410,7 +410,6 @@ const simpleAlgorithmDictionaries = {
410410
salt: 'BufferSource',
411411
info: 'BufferSource',
412412
},
413-
Ed448Params: { context: 'BufferSource' },
414413
ContextParams: { context: 'BufferSource' },
415414
Pbkdf2Params: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource' },
416415
RsaOaepParams: { label: 'BufferSource' },

lib/internal/crypto/webidl.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -775,18 +775,21 @@ converters.EcdhKeyDeriveParams = createDictionaryConverter(
775775
},
776776
]);
777777

778-
for (const name of ['Ed448Params', 'ContextParams']) {
779-
converters[name] = createDictionaryConverter(
780-
name, [
781-
...new SafeArrayIterator(dictAlgorithm),
782-
{
783-
key: 'context',
784-
converter: converters.BufferSource,
785-
validator: validateZeroLength(`${name}.context`),
786-
},
787-
]);
778+
function isOpenSSL32() {
779+
const { 0: major, 1: minor } = process.versions.openssl.split('.').map(i => parseInt(i, 10))
780+
return major > 3 || (major === 3 && minor >= 2);
788781
}
789782

783+
converters.ContextParams = createDictionaryConverter(
784+
'ContextParams', [
785+
...new SafeArrayIterator(dictAlgorithm),
786+
{
787+
key: 'context',
788+
converter: converters.BufferSource,
789+
validator: isOpenSSL32() ? undefined : validateZeroLength('ContextParams.context'),
790+
},
791+
]);
792+
790793
module.exports = {
791794
converters,
792795
requiredArguments,

0 commit comments

Comments
 (0)