mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
crypto: support Ed448 and ML-DSA context parameter in Web Cryptography
PR-URL: https://github.com/nodejs/node/pull/59570 Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
3837993b65
commit
0124e0e0d7
48
deps/ncrypto/ncrypto.cc
vendored
48
deps/ncrypto/ncrypto.cc
vendored
|
|
@ -4288,6 +4288,54 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInit(
|
|||
return ctx;
|
||||
}
|
||||
|
||||
std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::signInitWithContext(
|
||||
const EVPKeyPointer& key,
|
||||
const Digest& digest,
|
||||
const Buffer<const unsigned char>& context_string) {
|
||||
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
|
||||
EVP_PKEY_CTX* ctx = nullptr;
|
||||
|
||||
const OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_octet_string(
|
||||
OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
|
||||
const_cast<unsigned char*>(context_string.data),
|
||||
context_string.len),
|
||||
OSSL_PARAM_END};
|
||||
|
||||
if (!EVP_DigestSignInit_ex(
|
||||
ctx_.get(), &ctx, nullptr, nullptr, nullptr, key.get(), params)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ctx;
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInitWithContext(
|
||||
const EVPKeyPointer& key,
|
||||
const Digest& digest,
|
||||
const Buffer<const unsigned char>& context_string) {
|
||||
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
|
||||
EVP_PKEY_CTX* ctx = nullptr;
|
||||
|
||||
const OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_octet_string(
|
||||
OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
|
||||
const_cast<unsigned char*>(context_string.data),
|
||||
context_string.len),
|
||||
OSSL_PARAM_END};
|
||||
|
||||
if (!EVP_DigestVerifyInit_ex(
|
||||
ctx_.get(), &ctx, nullptr, nullptr, nullptr, key.get(), params)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ctx;
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
DataPointer EVPMDCtxPointer::signOneShot(
|
||||
const Buffer<const unsigned char>& buf) const {
|
||||
if (!ctx_) return {};
|
||||
|
|
|
|||
9
deps/ncrypto/ncrypto.h
vendored
9
deps/ncrypto/ncrypto.h
vendored
|
|
@ -1409,6 +1409,15 @@ class EVPMDCtxPointer final {
|
|||
std::optional<EVP_PKEY_CTX*> verifyInit(const EVPKeyPointer& key,
|
||||
const Digest& digest);
|
||||
|
||||
std::optional<EVP_PKEY_CTX*> signInitWithContext(
|
||||
const EVPKeyPointer& key,
|
||||
const Digest& digest,
|
||||
const Buffer<const unsigned char>& context_string);
|
||||
std::optional<EVP_PKEY_CTX*> verifyInitWithContext(
|
||||
const EVPKeyPointer& key,
|
||||
const Digest& digest,
|
||||
const Buffer<const unsigned char>& context_string);
|
||||
|
||||
DataPointer signOneShot(const Buffer<const unsigned char>& buf) const;
|
||||
DataPointer sign(const Buffer<const unsigned char>& buf) const;
|
||||
bool verify(const Buffer<const unsigned char>& buf,
|
||||
|
|
|
|||
|
|
@ -1342,7 +1342,7 @@ changes:
|
|||
|
||||
<!--lint disable maximum-line-length remark-lint-->
|
||||
|
||||
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams|KmacParams}
|
||||
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|ContextParams|KmacParams}
|
||||
* `key` {CryptoKey}
|
||||
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
|
||||
|
|
@ -1463,7 +1463,7 @@ changes:
|
|||
|
||||
<!--lint disable maximum-line-length remark-lint-->
|
||||
|
||||
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams}
|
||||
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|ContextParams|KmacParams}
|
||||
* `key` {CryptoKey}
|
||||
* `signature` {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||
|
|
@ -1830,20 +1830,23 @@ added: v24.7.0
|
|||
added: v24.7.0
|
||||
-->
|
||||
|
||||
* Type: {string} Must be `'ML-DSA-44'`[^modern-algos], `'ML-DSA-65'`[^modern-algos], or `'ML-DSA-87'`[^modern-algos].
|
||||
* Type: {string} Must be `Ed448`[^secure-curves], `'ML-DSA-44'`[^modern-algos],
|
||||
`'ML-DSA-65'`[^modern-algos], or `'ML-DSA-87'`[^modern-algos].
|
||||
|
||||
#### `contextParams.context`
|
||||
|
||||
<!-- YAML
|
||||
added: v24.7.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/59570
|
||||
description: Non-empty context is now supported.
|
||||
-->
|
||||
|
||||
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
|
||||
|
||||
The `context` member represents the optional context data to associate with
|
||||
the message.
|
||||
The Node.js Web Crypto API implementation only supports zero-length context
|
||||
which is equivalent to not providing context at all.
|
||||
|
||||
### Class: `CShakeParams`
|
||||
|
||||
|
|
@ -2024,37 +2027,6 @@ added: v15.0.0
|
|||
|
||||
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`.
|
||||
|
||||
### Class: `Ed448Params`
|
||||
|
||||
<!-- YAML
|
||||
added: v15.0.0
|
||||
-->
|
||||
|
||||
#### `ed448Params.name`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v18.4.0
|
||||
- v16.17.0
|
||||
-->
|
||||
|
||||
* Type: {string} Must be `'Ed448'`[^secure-curves].
|
||||
|
||||
#### `ed448Params.context`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v18.4.0
|
||||
- v16.17.0
|
||||
-->
|
||||
|
||||
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
|
||||
|
||||
The `context` member represents the optional context data to associate with
|
||||
the message.
|
||||
The Node.js Web Crypto API implementation only supports zero-length context
|
||||
which is equivalent to not providing context at all.
|
||||
|
||||
### Class: `EncapsulatedBits`
|
||||
|
||||
<!-- YAML
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ function eddsaSignVerify(key, data, algorithm, signature) {
|
|||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
algorithm.name === 'Ed448' ? algorithm.context : undefined,
|
||||
signature));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
|
|||
undefined, // Salt length, not used with ECDSA
|
||||
undefined, // PSS Padding, not used with ECDSA
|
||||
kSigEncP1363,
|
||||
undefined,
|
||||
signature));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -303,6 +303,7 @@ function mlDsaSignVerify(key, data, algorithm, signature) {
|
|||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
algorithm.context,
|
||||
signature));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ function rsaSignVerify(key, data, { saltLength }, signature) {
|
|||
saltLength,
|
||||
key[kAlgorithm].name === 'RSA-PSS' ? RSA_PKCS1_PSS_PADDING : undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
signature);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,9 @@ function signOneShot(algorithm, data, key, callback) {
|
|||
algorithm,
|
||||
pssSaltLength,
|
||||
rsaPadding,
|
||||
dsaSigEnc);
|
||||
dsaSigEnc,
|
||||
undefined,
|
||||
undefined);
|
||||
|
||||
if (!callback) {
|
||||
const { 0: err, 1: signature } = job.run();
|
||||
|
|
@ -276,6 +278,7 @@ function verifyOneShot(algorithm, data, key, signature, callback) {
|
|||
pssSaltLength,
|
||||
rsaPadding,
|
||||
dsaSigEnc,
|
||||
undefined,
|
||||
signature);
|
||||
|
||||
if (!callback) {
|
||||
|
|
|
|||
|
|
@ -268,8 +268,8 @@ const kAlgorithmDefinitions = {
|
|||
'generateKey': null,
|
||||
'exportKey': null,
|
||||
'importKey': null,
|
||||
'sign': 'Ed448Params',
|
||||
'verify': 'Ed448Params',
|
||||
'sign': 'ContextParams',
|
||||
'verify': 'ContextParams',
|
||||
},
|
||||
'HKDF': {
|
||||
'importKey': null,
|
||||
|
|
@ -494,7 +494,6 @@ const simpleAlgorithmDictionaries = {
|
|||
salt: 'BufferSource',
|
||||
info: 'BufferSource',
|
||||
},
|
||||
Ed448Params: { context: 'BufferSource' },
|
||||
ContextParams: { context: 'BufferSource' },
|
||||
Pbkdf2Params: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource' },
|
||||
RsaOaepParams: { label: 'BufferSource' },
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const {
|
|||
MathTrunc,
|
||||
Number,
|
||||
NumberIsFinite,
|
||||
NumberParseInt,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
SafeArrayIterator,
|
||||
|
|
@ -304,13 +305,12 @@ function createDictionaryConverter(name, dictionaries) {
|
|||
const context = `'${key}' of '${name}'${
|
||||
opts.context ? ` (${opts.context})` : ''
|
||||
}`;
|
||||
const { converter, validator } = member;
|
||||
const idlMemberValue = converter(esMemberValue, {
|
||||
const idlMemberValue = member.converter(esMemberValue, {
|
||||
__proto__: null,
|
||||
...opts,
|
||||
context,
|
||||
});
|
||||
validator?.(idlMemberValue, esDict);
|
||||
member.validator?.(idlMemberValue, esDict);
|
||||
setOwnProperty(idlDict, key, idlMemberValue);
|
||||
} else if (member.required) {
|
||||
throw makeException(
|
||||
|
|
@ -769,17 +769,25 @@ converters.EcdhKeyDeriveParams = createDictionaryConverter(
|
|||
},
|
||||
]);
|
||||
|
||||
for (const name of ['Ed448Params', 'ContextParams']) {
|
||||
converters[name] = createDictionaryConverter(
|
||||
name, [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'context',
|
||||
converter: converters.BufferSource,
|
||||
validator: validateZeroLength(`${name}.context`),
|
||||
converters.ContextParams = createDictionaryConverter(
|
||||
'ContextParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'context',
|
||||
converter: converters.BufferSource,
|
||||
validator(V, dict) {
|
||||
let { 0: major, 1: minor } = process.versions.openssl.split('.');
|
||||
major = NumberParseInt(major, 10);
|
||||
minor = NumberParseInt(minor, 10);
|
||||
if (major > 3 || (major === 3 && minor >= 2)) {
|
||||
this.validator = undefined;
|
||||
} else {
|
||||
this.validator = validateZeroLength('ContextParams.context');
|
||||
this.validator(V, dict);
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
converters.Argon2Params = createDictionaryConverter(
|
||||
'Argon2Params', [
|
||||
|
|
|
|||
|
|
@ -199,6 +199,10 @@ void CheckThrow(Environment* env, SignBase::Error error) {
|
|||
case SignBase::Error::MalformedSignature:
|
||||
return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Malformed signature");
|
||||
|
||||
case SignBase::Error::ContextUnsupported:
|
||||
return THROW_ERR_CRYPTO_OPERATION_FAILED(
|
||||
env, "Context parameter is unsupported");
|
||||
|
||||
case SignBase::Error::Init:
|
||||
case SignBase::Error::Update:
|
||||
case SignBase::Error::PrivateKey:
|
||||
|
|
@ -231,6 +235,24 @@ void CheckThrow(Environment* env, SignBase::Error error) {
|
|||
bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) {
|
||||
return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363;
|
||||
}
|
||||
|
||||
bool SupportsContextString(const EVPKeyPointer& key) {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x3020000fL
|
||||
return false;
|
||||
#else
|
||||
switch (key.id()) {
|
||||
case EVP_PKEY_ED448:
|
||||
#if OPENSSL_WITH_PQC
|
||||
case EVP_PKEY_ML_DSA_44:
|
||||
case EVP_PKEY_ML_DSA_65:
|
||||
case EVP_PKEY_ML_DSA_87:
|
||||
#endif
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SignBase::Error SignBase::Init(const char* digest) {
|
||||
|
|
@ -534,7 +556,8 @@ SignConfiguration::SignConfiguration(SignConfiguration&& other) noexcept
|
|||
flags(other.flags),
|
||||
padding(other.padding),
|
||||
salt_length(other.salt_length),
|
||||
dsa_encoding(other.dsa_encoding) {}
|
||||
dsa_encoding(other.dsa_encoding),
|
||||
context_string(std::move(other.context_string)) {}
|
||||
|
||||
SignConfiguration& SignConfiguration::operator=(
|
||||
SignConfiguration&& other) noexcept {
|
||||
|
|
@ -548,6 +571,7 @@ void SignConfiguration::MemoryInfo(MemoryTracker* tracker) const {
|
|||
if (job_mode == kCryptoJobAsync) {
|
||||
tracker->TrackFieldWithSize("data", data.size());
|
||||
tracker->TrackFieldWithSize("signature", signature.size());
|
||||
tracker->TrackFieldWithSize("context_string", context_string.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,8 +639,20 @@ Maybe<void> SignTraits::AdditionalConfig(
|
|||
}
|
||||
}
|
||||
|
||||
if (!args[offset + 10]->IsUndefined()) { // Context string
|
||||
ArrayBufferOrViewContents<char> context_string(args[offset + 10]);
|
||||
if (context_string.size() > 255) [[unlikely]] {
|
||||
THROW_ERR_OUT_OF_RANGE(env, "context string must be at most 255 bytes");
|
||||
return Nothing<void>();
|
||||
}
|
||||
params->flags |= SignConfiguration::kHasContextString;
|
||||
params->context_string = mode == kCryptoJobAsync
|
||||
? context_string.ToCopy()
|
||||
: context_string.ToByteSource();
|
||||
}
|
||||
|
||||
if (params->mode == SignConfiguration::Mode::Verify) {
|
||||
ArrayBufferOrViewContents<char> signature(args[offset + 10]);
|
||||
ArrayBufferOrViewContents<char> signature(args[offset + 11]);
|
||||
if (!signature.CheckSizeInt32()) [[unlikely]] {
|
||||
THROW_ERR_OUT_OF_RANGE(env, "signature is too big");
|
||||
return Nothing<void>();
|
||||
|
|
@ -647,12 +683,34 @@ bool SignTraits::DeriveBits(Environment* env,
|
|||
return false;
|
||||
const auto& key = params.key.GetAsymmetricKey();
|
||||
|
||||
bool has_context = (params.flags & SignConfiguration::kHasContextString &&
|
||||
params.context_string.size() > 0);
|
||||
|
||||
if (has_context && !SupportsContextString(key)) {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::ContextUnsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = ([&] {
|
||||
switch (params.mode) {
|
||||
case SignConfiguration::Mode::Sign:
|
||||
return context.signInit(key, params.digest);
|
||||
case SignConfiguration::Mode::Verify:
|
||||
return context.verifyInit(key, params.digest);
|
||||
if (has_context) {
|
||||
ncrypto::Buffer<const unsigned char> context_buf{
|
||||
.data = params.context_string.data<unsigned char>(),
|
||||
.len = params.context_string.size(),
|
||||
};
|
||||
|
||||
switch (params.mode) {
|
||||
case SignConfiguration::Mode::Sign:
|
||||
return context.signInitWithContext(key, params.digest, context_buf);
|
||||
case SignConfiguration::Mode::Verify:
|
||||
return context.verifyInitWithContext(key, params.digest, context_buf);
|
||||
}
|
||||
} else {
|
||||
switch (params.mode) {
|
||||
case SignConfiguration::Mode::Sign:
|
||||
return context.signInit(key, params.digest);
|
||||
case SignConfiguration::Mode::Verify:
|
||||
return context.verifyInit(key, params.digest);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class SignBase : public BaseObject {
|
|||
Update,
|
||||
PrivateKey,
|
||||
PublicKey,
|
||||
MalformedSignature
|
||||
MalformedSignature,
|
||||
ContextUnsupported,
|
||||
};
|
||||
|
||||
SignBase(Environment* env, v8::Local<v8::Object> wrap);
|
||||
|
|
@ -99,7 +100,8 @@ struct SignConfiguration final : public MemoryRetainer {
|
|||
enum Flags {
|
||||
kHasNone = 0,
|
||||
kHasSaltLength = 1,
|
||||
kHasPadding = 2
|
||||
kHasPadding = 2,
|
||||
kHasContextString = 4
|
||||
};
|
||||
|
||||
CryptoJobMode job_mode;
|
||||
|
|
@ -112,6 +114,7 @@ struct SignConfiguration final : public MemoryRetainer {
|
|||
int padding = 0;
|
||||
int salt_length = 0;
|
||||
DSASigEnc dsa_encoding = DSASigEnc::DER;
|
||||
ByteSource context_string;
|
||||
|
||||
SignConfiguration() = default;
|
||||
|
||||
|
|
|
|||
45
test/fixtures/crypto/eddsa.js
vendored
45
test/fixtures/crypto/eddsa.js
vendored
|
|
@ -28,18 +28,23 @@ module.exports = function() {
|
|||
'b219e30a1beea8fe869082d99fc8282f9050d024e59eaf0730ba9db70a', 'hex');
|
||||
|
||||
// For verification tests.
|
||||
// eslint-disable @stylistic/js/max-len
|
||||
const signatures = {
|
||||
'Ed25519': Buffer.from(
|
||||
'3d90de5e5743dfc28225bfadb341b116cbf8a3f1ceedbf4adc350ef5d3471843a418' +
|
||||
'614dcb6e614862614cf7af1496f9340b3c844ea4dceab1d3d155eb7ecc00', 'hex'),
|
||||
'Ed448': Buffer.from(
|
||||
'76897e8c50ac6b1132735c09c55f506c0149d2677c75664f8bc10b826fbd9df0a03c' +
|
||||
'd986bce8339e64c7d1720ea9361784dc73837765ac2980c0dac0814a8bc187d1c9c9' +
|
||||
'07c5dcc07956f85b70930fe42de764177217cb2d52bab7c1debe0ca89ccecbcd63f7' +
|
||||
'025a2a5a572b9d23b0642f00', 'hex')
|
||||
'Ed25519': {
|
||||
// Ed25519 does not support context
|
||||
'0': Buffer.from('3d90de5e5743dfc28225bfadb341b116cbf8a3f1ceedbf4adc350ef5d3471843a418614dcb6e614862614cf7af1496f9340b3c844ea4dceab1d3d155eb7ecc00', 'hex'),
|
||||
},
|
||||
'Ed448': {
|
||||
'0': Buffer.from('76897e8c50ac6b1132735c09c55f506c0149d2677c75664f8bc10b826fbd9df0a03cd986bce8339e64c7d1720ea9361784dc73837765ac2980c0dac0814a8bc187d1c9c907c5dcc07956f85b70930fe42de764177217cb2d52bab7c1debe0ca89ccecbcd63f7025a2a5a572b9d23b0642f00', 'hex'),
|
||||
'32': Buffer.from('0294186f0305dd3a2d5ac86eeb7e73c05d419e84152c2341ae24e55c3889e878f4acb537f3651a50b0b1c26739721b168499337537c92727003480be61fc23f519ed772ebf2977f6bda5259235ded904959227beaf0adfbd28288358854e9abe089dc8075998993b86280b0bd89bdacc3c00', 'hex'),
|
||||
'255': Buffer.from('6dfef748ab53ca856b3ffd84c62ae167c2737dfe4eae89c6c1edc0adc685b73f8170eacd723ec76fb31318ebe47c908722000129b2e9806e8040a4d4d90ac1d1b539199e33553300dcdf4989e4b77c835b53f4ee0d114845ad97047ad0d112e05304b38f5836bbe024a6f700a368d9910100', 'hex'),
|
||||
}
|
||||
}
|
||||
// eslint-disable @stylistic/js/max-len
|
||||
|
||||
|
||||
const algorithms = ['Ed25519'];
|
||||
const contexts = [new Uint8Array(0), new Uint8Array(32), new Uint8Array(255)];
|
||||
|
||||
if (!process.features.openssl_is_boringssl) {
|
||||
algorithms.push('Ed448')
|
||||
|
|
@ -47,13 +52,23 @@ module.exports = function() {
|
|||
common.printSkipMessage(`Skipping unsupported Ed448 test cases`);
|
||||
}
|
||||
|
||||
const vectors = algorithms.map((algorithm) => ({
|
||||
publicKeyBuffer: spki[algorithm],
|
||||
privateKeyBuffer: pkcs8[algorithm],
|
||||
name: algorithm,
|
||||
data,
|
||||
signature: signatures[algorithm],
|
||||
}));
|
||||
const vectors = [];
|
||||
for (const algorithm of algorithms) {
|
||||
for (const context of contexts) {
|
||||
if (algorithm === 'Ed25519' && context.byteLength !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vectors.push({
|
||||
publicKeyBuffer: spki[algorithm],
|
||||
privateKeyBuffer: pkcs8[algorithm],
|
||||
name: algorithm,
|
||||
context: algorithm === 'Ed25519' ? undefined : context,
|
||||
data,
|
||||
signature: signatures[algorithm][context.byteLength],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return vectors;
|
||||
}
|
||||
|
|
|
|||
44
test/fixtures/crypto/ml-dsa.js
vendored
44
test/fixtures/crypto/ml-dsa.js
vendored
File diff suppressed because one or more lines are too long
27
test/fixtures/webcrypto/supports-level-2.mjs
vendored
27
test/fixtures/webcrypto/supports-level-2.mjs
vendored
|
|
@ -5,9 +5,8 @@ const RSA_KEY_GEN = {
|
|||
publicExponent: new Uint8Array([1, 0, 1])
|
||||
};
|
||||
|
||||
const [ECDH, X448, X25519] = await Promise.all([
|
||||
const [ECDH, X25519] = await Promise.all([
|
||||
subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits', 'deriveKey']),
|
||||
subtle.generateKey('X448', false, ['deriveBits', 'deriveKey']),
|
||||
subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
|
||||
]);
|
||||
|
||||
|
|
@ -34,10 +33,6 @@ export const vectors = {
|
|||
|
||||
[true, 'Ed25519'],
|
||||
|
||||
[true, 'Ed448'],
|
||||
[true, { name: 'Ed448', context: Buffer.alloc(0) }],
|
||||
[false, { name: 'Ed448', context: Buffer.alloc(1) }],
|
||||
|
||||
[true, 'RSASSA-PKCS1-v1_5'],
|
||||
|
||||
[true, { name: 'RSA-PSS', saltLength: 32 }],
|
||||
|
|
@ -64,9 +59,7 @@ export const vectors = {
|
|||
[false, 'HKDF'],
|
||||
[false, 'PBKDF2'],
|
||||
[true, 'X25519'],
|
||||
[true, 'X448'],
|
||||
[true, 'Ed25519'],
|
||||
[true, 'Ed448'],
|
||||
[true, { name: 'HMAC', hash: 'SHA-256' }],
|
||||
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
|
||||
[false, { name: 'HMAC', hash: 'SHA-256', length: 25 }],
|
||||
|
|
@ -116,15 +109,6 @@ export const vectors = {
|
|||
[true,
|
||||
{ name: 'X25519', public: X25519.publicKey },
|
||||
'HKDF'],
|
||||
[true,
|
||||
{ name: 'X448', public: X448.publicKey },
|
||||
{ name: 'AES-CBC', length: 128 }],
|
||||
[true,
|
||||
{ name: 'X448', public: X448.publicKey },
|
||||
{ name: 'HMAC', hash: 'SHA-256' }],
|
||||
[true,
|
||||
{ name: 'X448', public: X448.publicKey },
|
||||
'HKDF'],
|
||||
[true,
|
||||
{ name: 'ECDH', public: ECDH.publicKey },
|
||||
{ name: 'AES-CBC', length: 128 }],
|
||||
|
|
@ -159,27 +143,18 @@ export const vectors = {
|
|||
|
||||
[true,
|
||||
{ name: 'ECDH', public: ECDH.publicKey }],
|
||||
[false, { name: 'ECDH', public: X448.publicKey }],
|
||||
[false, { name: 'ECDH', public: ECDH.privateKey }],
|
||||
[false, 'ECDH'],
|
||||
|
||||
[true, { name: 'X25519', public: X25519.publicKey }],
|
||||
[false, { name: 'X25519', public: X448.publicKey }],
|
||||
[false, { name: 'X25519', public: X25519.privateKey }],
|
||||
[false, 'X25519'],
|
||||
|
||||
[true, { name: 'X448', public: X448.publicKey }],
|
||||
[false, { name: 'X448', public: X25519.publicKey }],
|
||||
[false, { name: 'X448', public: X448.privateKey }],
|
||||
[false, 'X448'],
|
||||
],
|
||||
'importKey': [
|
||||
[false, 'SHA-1'],
|
||||
[false, 'Invalid'],
|
||||
[true, 'X25519'],
|
||||
[true, 'X448'],
|
||||
[true, 'Ed25519'],
|
||||
[true, 'Ed448'],
|
||||
[true, { name: 'HMAC', hash: 'SHA-256' }],
|
||||
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
|
||||
[false, { name: 'HMAC', hash: 'SHA-256', length: 25 }],
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ export const vectors = {
|
|||
[pqc, 'ML-DSA-44'],
|
||||
[pqc, 'ML-DSA-65'],
|
||||
[pqc, 'ML-DSA-87'],
|
||||
[pqc, { name: 'ML-DSA-44', context: Buffer.alloc(0) }],
|
||||
[pqc, { name: 'ML-DSA-65', context: Buffer.alloc(0) }],
|
||||
[pqc, { name: 'ML-DSA-87', context: Buffer.alloc(0) }],
|
||||
[pqc, { name: 'ML-DSA-44', context: Buffer.alloc(32) }],
|
||||
[pqc, { name: 'ML-DSA-65', context: Buffer.alloc(32) }],
|
||||
[pqc, { name: 'ML-DSA-87', context: Buffer.alloc(32) }],
|
||||
[false, 'Argon2d'],
|
||||
[false, 'Argon2i'],
|
||||
[false, 'Argon2id'],
|
||||
|
|
|
|||
|
|
@ -1,14 +1,25 @@
|
|||
import { hasOpenSSL } from '../../common/crypto.js'
|
||||
|
||||
const supportsContext = hasOpenSSL(3, 2);
|
||||
|
||||
const { subtle } = globalThis.crypto;
|
||||
|
||||
const boringSSL = process.features.openssl_is_boringssl;
|
||||
|
||||
const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
|
||||
let X448;
|
||||
let Ed448;
|
||||
if (!boringSSL) {
|
||||
X448 = await subtle.generateKey('X448', false, ['deriveBits', 'deriveKey'])
|
||||
Ed448 = await subtle.generateKey('Ed448', false, ['sign', 'verify'])
|
||||
}
|
||||
|
||||
export const vectors = {
|
||||
'sign': [
|
||||
[!boringSSL, 'Ed448'],
|
||||
[!boringSSL, { name: 'Ed448', context: Buffer.alloc(0) }],
|
||||
[!boringSSL && supportsContext, { name: 'Ed448', context: Buffer.alloc(32) }],
|
||||
],
|
||||
'generateKey': [
|
||||
[!boringSSL, 'X448'],
|
||||
[!boringSSL, 'Ed448'],
|
||||
|
|
|
|||
|
|
@ -5,12 +5,18 @@ const common = require('../common');
|
|||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const { hasOpenSSL } = require('../common/crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
const { subtle } = globalThis.crypto;
|
||||
|
||||
const vectors = require('../fixtures/crypto/eddsa')();
|
||||
|
||||
const supportsContext = hasOpenSSL(3, 2);
|
||||
|
||||
async function testVerify({ name,
|
||||
context,
|
||||
publicKeyBuffer,
|
||||
privateKeyBuffer,
|
||||
signature,
|
||||
|
|
@ -63,43 +69,62 @@ async function testVerify({ name,
|
|||
['sign']),
|
||||
]);
|
||||
|
||||
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, signature, data));
|
||||
if (context?.byteLength !== undefined) {
|
||||
if (supportsContext) {
|
||||
assert(!(await subtle.verify({ name, context: crypto.randomBytes(30) }, publicKey, signature, data)));
|
||||
}
|
||||
if (context.byteLength === 0) {
|
||||
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||
}
|
||||
}
|
||||
|
||||
// Test verification with altered buffers
|
||||
const copy = Buffer.from(data);
|
||||
const sigcopy = Buffer.from(signature);
|
||||
const p = subtle.verify({ name }, publicKey, sigcopy, copy);
|
||||
const p = subtle.verify({ name, context }, publicKey, sigcopy, copy);
|
||||
copy[0] = 255 - copy[0];
|
||||
sigcopy[0] = 255 - sigcopy[0];
|
||||
assert(await p);
|
||||
|
||||
// Test failure when using wrong key
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, privateKey, signature, data), {
|
||||
subtle.verify({ name, context }, privateKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, noVerifyPublicKey, signature, data), {
|
||||
subtle.verify({ name, context }, noVerifyPublicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
// Test failure when using the wrong algorithms
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, hmacKey, signature, data), {
|
||||
subtle.verify({ name, context }, hmacKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, rsaKeys.publicKey, signature, data), {
|
||||
subtle.verify({ name, context }, rsaKeys.publicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, ecKeys.publicKey, signature, data), {
|
||||
subtle.verify({ name, context }, ecKeys.publicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
if (name === 'Ed448' && supportsContext) {
|
||||
// Test failure when too long context
|
||||
await assert.rejects(
|
||||
subtle.verify({ name, context: new Uint8Array(256) }, publicKey, signature, data), (err) => {
|
||||
assert.strictEqual(err.name, 'OperationError');
|
||||
assert.strictEqual(err.cause.code, 'ERR_OUT_OF_RANGE');
|
||||
assert.strictEqual(err.cause.message, 'context string must be at most 255 bytes');
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Test failure when signature is altered
|
||||
{
|
||||
const copy = Buffer.from(signature);
|
||||
|
|
@ -120,11 +145,12 @@ async function testVerify({ name,
|
|||
{
|
||||
const copy = Buffer.from(data);
|
||||
copy[0] = 255 - copy[0];
|
||||
assert(!(await subtle.verify({ name }, publicKey, signature, copy)));
|
||||
assert(!(await subtle.verify({ name, context }, publicKey, signature, copy)));
|
||||
}
|
||||
}
|
||||
|
||||
async function testSign({ name,
|
||||
context,
|
||||
publicKeyBuffer,
|
||||
privateKeyBuffer,
|
||||
signature,
|
||||
|
|
@ -171,46 +197,66 @@ async function testSign({ name,
|
|||
]);
|
||||
|
||||
{
|
||||
const sig = await subtle.sign({ name }, privateKey, data);
|
||||
const sig = await subtle.sign({ name, context }, privateKey, data);
|
||||
assert.strictEqual(sig.byteLength, signature.byteLength);
|
||||
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, sig, data));
|
||||
if (context?.byteLength !== undefined) {
|
||||
if (supportsContext) {
|
||||
assert(!(await subtle.verify({ name, context: crypto.randomBytes(30) }, publicKey, signature, data)));
|
||||
}
|
||||
if (context.byteLength === 0) {
|
||||
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const copy = Buffer.from(data);
|
||||
const p = subtle.sign({ name }, privateKey, copy);
|
||||
const p = subtle.sign({ name, context }, privateKey, copy);
|
||||
copy[0] = 255 - copy[0];
|
||||
const sig = await p;
|
||||
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, sig, data));
|
||||
}
|
||||
|
||||
// Test failure when using wrong key
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, publicKey, data), {
|
||||
subtle.sign({ name, context }, publicKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
// Test failure when using the wrong algorithms
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, hmacKey, data), {
|
||||
subtle.sign({ name, context }, hmacKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, rsaKeys.privateKey, data), {
|
||||
subtle.sign({ name, context }, rsaKeys.privateKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, ecKeys.privateKey, data), {
|
||||
subtle.sign({ name, context }, ecKeys.privateKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
if (name === 'Ed448' && supportsContext) {
|
||||
// Test failure when too long context
|
||||
await assert.rejects(
|
||||
subtle.sign({ name, context: new Uint8Array(256) }, privateKey, data), (err) => {
|
||||
assert.strictEqual(err.name, 'OperationError');
|
||||
assert.strictEqual(err.cause.code, 'ERR_OUT_OF_RANGE');
|
||||
assert.strictEqual(err.cause.message, 'context string must be at most 255 bytes');
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(async function() {
|
||||
const variations = [];
|
||||
|
||||
vectors.forEach((vector) => {
|
||||
if (!supportsContext && vector.context?.byteLength) return;
|
||||
variations.push(testVerify(vector));
|
||||
variations.push(testSign(vector));
|
||||
});
|
||||
|
|
@ -218,35 +264,16 @@ async function testSign({ name,
|
|||
await Promise.all(variations);
|
||||
})().then(common.mustCall());
|
||||
|
||||
// Ed448 context
|
||||
if (!process.features.openssl_is_boringssl) {
|
||||
const vector = vectors.find(({ name }) => name === 'Ed448');
|
||||
Promise.all([
|
||||
subtle.importKey(
|
||||
'pkcs8',
|
||||
vector.privateKeyBuffer,
|
||||
{ name: 'Ed448' },
|
||||
false,
|
||||
['sign']),
|
||||
subtle.importKey(
|
||||
'spki',
|
||||
vector.publicKeyBuffer,
|
||||
{ name: 'Ed448' },
|
||||
false,
|
||||
['verify']),
|
||||
]).then(async ([privateKey, publicKey]) => {
|
||||
const sig = await subtle.sign({ name: 'Ed448', context: Buffer.alloc(0) }, privateKey, vector.data);
|
||||
assert.deepStrictEqual(Buffer.from(sig), vector.signature);
|
||||
assert.strictEqual(
|
||||
await subtle.verify({ name: 'Ed448', context: Buffer.alloc(0) }, publicKey, sig, vector.data), true);
|
||||
if (!supportsContext) {
|
||||
assert.rejects(async () => {
|
||||
const kp = await subtle.generateKey('Ed448', false, ['sign', 'verify']);
|
||||
const data = crypto.randomBytes(32);
|
||||
await subtle.sign({ name: 'Ed448', context: new Uint8Array(32) }, kp.privateKey, data);
|
||||
}, { name: /OperationError|NotSupportedError/ }).then(common.mustCall());
|
||||
|
||||
await assert.rejects(subtle.sign({ name: 'Ed448', context: Buffer.alloc(1) }, privateKey, vector.data), {
|
||||
message: /Non zero-length Ed448Params\.context is not supported/
|
||||
});
|
||||
await assert.rejects(subtle.verify({ name: 'Ed448', context: Buffer.alloc(1) }, publicKey, sig, vector.data), {
|
||||
message: /Non zero-length Ed448Params\.context is not supported/
|
||||
});
|
||||
}).then(common.mustCall());
|
||||
} else {
|
||||
common.printSkipMessage('Skipping unsupported Ed448 test case');
|
||||
assert.rejects(async () => {
|
||||
const kp = await subtle.generateKey('Ed448', false, ['sign', 'verify']);
|
||||
const data = crypto.randomBytes(32);
|
||||
await subtle.verify({ name: 'Ed448', context: new Uint8Array(32) }, kp.publicKey, data, data);
|
||||
}, { name: /OperationError|NotSupportedError/ }).then(common.mustCall());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const { subtle } = globalThis.crypto;
|
|||
const vectors = require('../fixtures/crypto/ml-dsa')();
|
||||
|
||||
async function testVerify({ name,
|
||||
context,
|
||||
publicKeyPem,
|
||||
privateKeyPem,
|
||||
signature,
|
||||
|
|
@ -57,54 +58,67 @@ async function testVerify({ name,
|
|||
['sign']),
|
||||
]);
|
||||
|
||||
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, signature, data));
|
||||
assert(!(await subtle.verify({ name, context: crypto.randomBytes(30) }, publicKey, signature, data)));
|
||||
if (context.byteLength === 0) {
|
||||
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||
}
|
||||
|
||||
// Test verification with altered buffers
|
||||
const copy = Buffer.from(data);
|
||||
const sigcopy = Buffer.from(signature);
|
||||
const p = subtle.verify({ name }, publicKey, sigcopy, copy);
|
||||
const p = subtle.verify({ name, context }, publicKey, sigcopy, copy);
|
||||
copy[0] = 255 - copy[0];
|
||||
sigcopy[0] = 255 - sigcopy[0];
|
||||
assert(await p);
|
||||
|
||||
// Test failure when using wrong key
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, privateKey, signature, data), {
|
||||
subtle.verify({ name, context }, privateKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, noVerifyPublicKey, signature, data), {
|
||||
subtle.verify({ name, context }, noVerifyPublicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
// Test failure when using the wrong algorithms
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, hmacKey, signature, data), {
|
||||
subtle.verify({ name, context }, hmacKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, rsaKeys.publicKey, signature, data), {
|
||||
subtle.verify({ name, context }, rsaKeys.publicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name }, ecKeys.publicKey, signature, data), {
|
||||
subtle.verify({ name, context }, ecKeys.publicKey, signature, data), {
|
||||
message: /Unable to use this key to verify/
|
||||
});
|
||||
|
||||
// Test failure when too long context
|
||||
await assert.rejects(
|
||||
subtle.verify({ name, context: new Uint8Array(256) }, publicKey, signature, data), (err) => {
|
||||
assert.strictEqual(err.name, 'OperationError');
|
||||
assert.strictEqual(err.cause.code, 'ERR_OUT_OF_RANGE');
|
||||
assert.strictEqual(err.cause.message, 'context string must be at most 255 bytes');
|
||||
return true;
|
||||
});
|
||||
|
||||
// Test failure when signature is altered
|
||||
{
|
||||
const copy = Buffer.from(signature);
|
||||
copy[0] = 255 - copy[0];
|
||||
assert(!(await subtle.verify(
|
||||
{ name },
|
||||
{ name, context },
|
||||
publicKey,
|
||||
copy,
|
||||
data)));
|
||||
assert(!(await subtle.verify(
|
||||
{ name },
|
||||
{ name, context },
|
||||
publicKey,
|
||||
copy.slice(1),
|
||||
data)));
|
||||
|
|
@ -114,11 +128,12 @@ async function testVerify({ name,
|
|||
{
|
||||
const copy = Buffer.from(data);
|
||||
copy[0] = 255 - copy[0];
|
||||
assert(!(await subtle.verify({ name }, publicKey, signature, copy)));
|
||||
assert(!(await subtle.verify({ name, context }, publicKey, signature, copy)));
|
||||
}
|
||||
}
|
||||
|
||||
async function testSign({ name,
|
||||
context,
|
||||
publicKeyPem,
|
||||
privateKeyPem,
|
||||
signature,
|
||||
|
|
@ -157,40 +172,49 @@ async function testSign({ name,
|
|||
]);
|
||||
|
||||
{
|
||||
const sig = await subtle.sign({ name }, privateKey, data);
|
||||
const sig = await subtle.sign({ name, context }, privateKey, data);
|
||||
assert.strictEqual(sig.byteLength, signature.byteLength);
|
||||
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, sig, data));
|
||||
}
|
||||
|
||||
{
|
||||
const copy = Buffer.from(data);
|
||||
const p = subtle.sign({ name }, privateKey, copy);
|
||||
const p = subtle.sign({ name, context }, privateKey, copy);
|
||||
copy[0] = 255 - copy[0];
|
||||
const sig = await p;
|
||||
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||
assert(await subtle.verify({ name, context }, publicKey, sig, data));
|
||||
}
|
||||
|
||||
// Test failure when using wrong key
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, publicKey, data), {
|
||||
subtle.sign({ name, context }, publicKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
// Test failure when using the wrong algorithms
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, hmacKey, data), {
|
||||
subtle.sign({ name, context }, hmacKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, rsaKeys.privateKey, data), {
|
||||
subtle.sign({ name, context }, rsaKeys.privateKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.sign({ name }, ecKeys.privateKey, data), {
|
||||
subtle.sign({ name, context }, ecKeys.privateKey, data), {
|
||||
message: /Unable to use this key to sign/
|
||||
});
|
||||
|
||||
// Test failure when too long context
|
||||
await assert.rejects(
|
||||
subtle.sign({ name, context: new Uint8Array(256) }, privateKey, data), (err) => {
|
||||
assert.strictEqual(err.name, 'OperationError');
|
||||
assert.strictEqual(err.cause.code, 'ERR_OUT_OF_RANGE');
|
||||
assert.strictEqual(err.cause.message, 'context string must be at most 255 bytes');
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
(async function() {
|
||||
|
|
@ -203,26 +227,3 @@ async function testSign({ name,
|
|||
|
||||
await Promise.all(variations);
|
||||
})().then(common.mustCall());
|
||||
|
||||
// ContextParams context not supported
|
||||
{
|
||||
const vector = vectors[0];
|
||||
const name = vector.name;
|
||||
const publicKey = crypto.createPublicKey(vector.publicKeyPem)
|
||||
.toCryptoKey(vector.name, false, ['verify']);
|
||||
const privateKey = crypto.createPrivateKey(vector.privateKeyPem)
|
||||
.toCryptoKey(vector.name, false, ['sign']);
|
||||
|
||||
(async () => {
|
||||
const sig = await subtle.sign({ name, context: Buffer.alloc(0) }, privateKey, vector.data);
|
||||
assert.strictEqual(
|
||||
await subtle.verify({ name, context: Buffer.alloc(0) }, publicKey, sig, vector.data), true);
|
||||
|
||||
await assert.rejects(subtle.sign({ name, context: Buffer.alloc(1) }, privateKey, vector.data), {
|
||||
message: /Non zero-length ContextParams\.context is not supported/
|
||||
});
|
||||
await assert.rejects(subtle.verify({ name, context: Buffer.alloc(1) }, publicKey, sig, vector.data), {
|
||||
message: /Non zero-length ContextParams\.context is not supported/
|
||||
});
|
||||
})().then(common.mustCall());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -507,12 +507,12 @@ const opts = { prefix, context };
|
|||
}).then(common.mustCall());
|
||||
}
|
||||
|
||||
// Ed448Params
|
||||
// ContextParams
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'Ed448', context: new Uint8Array() },
|
||||
{ name: 'Ed448' },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.Ed448Params({ ...good, filtered: 'out' }, opts), good);
|
||||
assert.deepStrictEqual(converters.ContextParams({ ...good, filtered: 'out' }, opts), good);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ const customTypesMap = {
|
|||
'HmacImportParams': 'webcrypto.html#class-hmacimportparams',
|
||||
'EcdsaParams': 'webcrypto.html#class-ecdsaparams',
|
||||
'RsaPssParams': 'webcrypto.html#class-rsapssparams',
|
||||
'Ed448Params': 'webcrypto.html#class-ed448params',
|
||||
'ContextParams': 'webcrypto.html#class-contextparams',
|
||||
'CShakeParams': 'webcrypto.html#class-cshakeparams',
|
||||
'KmacImportParams': 'webcrypto.html#class-kmacimportparams',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user