node/test/parallel/test-crypto-sign-verify.js
Antoine du Hamel 99e0d0d218
test: add escapePOSIXShell util
PR-URL: https://github.com/nodejs/node/pull/55125
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
2024-09-29 20:44:52 +00:00

812 lines
27 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const fs = require('fs');
const exec = require('child_process').exec;
const crypto = require('crypto');
const fixtures = require('../common/fixtures');
// Test certificates
const certPem = fixtures.readKey('rsa_cert.crt');
const keyPem = fixtures.readKey('rsa_private.pem');
const keySize = 2048;
{
const Sign = crypto.Sign;
const instance = Sign('SHA256');
assert(instance instanceof Sign, 'Sign is expected to return a new ' +
'instance when called without `new`');
}
{
const Verify = crypto.Verify;
const instance = Verify('SHA256');
assert(instance instanceof Verify, 'Verify is expected to return a new ' +
'instance when called without `new`');
}
// Test handling of exceptional conditions
{
const library = {
configurable: true,
set() {
throw new Error('bye, bye, library');
}
};
Object.defineProperty(Object.prototype, 'library', library);
assert.throws(() => {
crypto.createSign('sha1').sign(
`-----BEGIN RSA PRIVATE KEY-----
AAAAAAAAAAAA
-----END RSA PRIVATE KEY-----`);
}, { message: 'bye, bye, library' });
delete Object.prototype.library;
const errorStack = {
configurable: true,
set() {
throw new Error('bye, bye, error stack');
}
};
Object.defineProperty(Object.prototype, 'opensslErrorStack', errorStack);
assert.throws(() => {
crypto.createSign('SHA1')
.update('Test123')
.sign({
key: keyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
});
}, { message: common.hasOpenSSL3 ?
'error:1C8000A5:Provider routines::illegal or unsupported padding mode' :
'bye, bye, error stack' });
delete Object.prototype.opensslErrorStack;
}
assert.throws(
() => crypto.createVerify('SHA256').verify({
key: certPem,
padding: null,
}, ''),
{
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: "The property 'options.padding' is invalid. Received null",
});
assert.throws(
() => crypto.createVerify('SHA256').verify({
key: certPem,
saltLength: null,
}, ''),
{
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: "The property 'options.saltLength' is invalid. Received null",
});
// Test signing and verifying
{
const s1 = crypto.createSign('SHA1')
.update('Test123')
.sign(keyPem, 'base64');
let s1stream = crypto.createSign('SHA1');
s1stream.end('Test123');
s1stream = s1stream.sign(keyPem, 'base64');
assert.strictEqual(s1, s1stream, `${s1} should equal ${s1stream}`);
const verified = crypto.createVerify('SHA1')
.update('Test')
.update('123')
.verify(certPem, s1, 'base64');
assert.strictEqual(verified, true);
}
{
const s2 = crypto.createSign('SHA256')
.update('Test123')
.sign(keyPem, 'latin1');
let s2stream = crypto.createSign('SHA256');
s2stream.end('Test123');
s2stream = s2stream.sign(keyPem, 'latin1');
assert.strictEqual(s2, s2stream, `${s2} should equal ${s2stream}`);
let verified = crypto.createVerify('SHA256')
.update('Test')
.update('123')
.verify(certPem, s2, 'latin1');
assert.strictEqual(verified, true);
const verStream = crypto.createVerify('SHA256');
verStream.write('Tes');
verStream.write('t12');
verStream.end('3');
verified = verStream.verify(certPem, s2, 'latin1');
assert.strictEqual(verified, true);
}
{
const s3 = crypto.createSign('SHA1')
.update('Test123')
.sign(keyPem, 'buffer');
let verified = crypto.createVerify('SHA1')
.update('Test')
.update('123')
.verify(certPem, s3);
assert.strictEqual(verified, true);
const verStream = crypto.createVerify('SHA1');
verStream.write('Tes');
verStream.write('t12');
verStream.end('3');
verified = verStream.verify(certPem, s3);
assert.strictEqual(verified, true);
}
// Special tests for RSA_PKCS1_PSS_PADDING
{
function testPSS(algo, hLen) {
// Maximum permissible salt length
const max = keySize / 8 - hLen - 2;
function getEffectiveSaltLength(saltLength) {
switch (saltLength) {
case crypto.constants.RSA_PSS_SALTLEN_DIGEST:
return hLen;
case crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN:
return max;
default:
return saltLength;
}
}
const signSaltLengths = [
crypto.constants.RSA_PSS_SALTLEN_DIGEST,
getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST),
crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN,
getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN),
0, 16, 32, 64, 128,
];
const verifySaltLengths = [
crypto.constants.RSA_PSS_SALTLEN_DIGEST,
getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST),
getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN),
0, 16, 32, 64, 128,
];
const errMessage = /^Error:.*data too large for key size$/;
const data = Buffer.from('Test123');
signSaltLengths.forEach((signSaltLength) => {
if (signSaltLength > max) {
// If the salt length is too big, an Error should be thrown
assert.throws(() => {
crypto.createSign(algo)
.update(data)
.sign({
key: keyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: signSaltLength
});
}, errMessage);
assert.throws(() => {
crypto.sign(algo, data, {
key: keyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: signSaltLength
});
}, errMessage);
} else {
// Otherwise, a valid signature should be generated
const s4 = crypto.createSign(algo)
.update(data)
.sign({
key: keyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: signSaltLength
});
const s4_2 = crypto.sign(algo, data, {
key: keyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: signSaltLength
});
[s4, s4_2].forEach((sig) => {
let verified;
verifySaltLengths.forEach((verifySaltLength) => {
// Verification should succeed if and only if the salt length is
// correct
verified = crypto.createVerify(algo)
.update(data)
.verify({
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: verifySaltLength
}, sig);
assert.strictEqual(verified, crypto.verify(algo, data, {
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: verifySaltLength
}, sig));
const saltLengthCorrect = getEffectiveSaltLength(signSaltLength) ===
getEffectiveSaltLength(verifySaltLength);
assert.strictEqual(verified, saltLengthCorrect);
});
// Verification using RSA_PSS_SALTLEN_AUTO should always work
verified = crypto.createVerify(algo)
.update(data)
.verify({
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
}, sig);
assert.strictEqual(verified, true);
assert.strictEqual(verified, crypto.verify(algo, data, {
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
}, sig));
// Verifying an incorrect message should never work
const wrongData = Buffer.from('Test1234');
verified = crypto.createVerify(algo)
.update(wrongData)
.verify({
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
}, sig);
assert.strictEqual(verified, false);
assert.strictEqual(verified, crypto.verify(algo, wrongData, {
key: certPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
}, sig));
});
}
});
}
testPSS('SHA1', 20);
testPSS('SHA256', 32);
}
// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories:
// https://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm
{
// We only test verification as we cannot specify explicit salts when signing
function testVerify(cert, vector) {
const verified = crypto.createVerify('SHA1')
.update(Buffer.from(vector.message, 'hex'))
.verify({
key: cert,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: vector.salt.length / 2
}, vector.signature, 'hex');
assert.strictEqual(verified, true);
}
const examples = JSON.parse(fixtures.readSync('pss-vectors.json', 'utf8'));
for (const key in examples) {
const example = examples[key];
const publicKey = example.publicKey.join('\n');
example.tests.forEach((test) => testVerify(publicKey, test));
}
}
// Test exceptions for invalid `padding` and `saltLength` values
{
[null, NaN, 'boom', {}, [], true, false]
.forEach((invalidValue) => {
assert.throws(() => {
crypto.createSign('SHA256')
.update('Test123')
.sign({
key: keyPem,
padding: invalidValue
});
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError'
});
assert.throws(() => {
crypto.createSign('SHA256')
.update('Test123')
.sign({
key: keyPem,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: invalidValue
});
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError'
});
});
assert.throws(() => {
crypto.createSign('SHA1')
.update('Test123')
.sign({
key: keyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
});
}, common.hasOpenSSL3 ? {
code: 'ERR_OSSL_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE',
message: /illegal or unsupported padding mode/,
} : {
code: 'ERR_OSSL_RSA_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE',
message: /illegal or unsupported padding mode/,
opensslErrorStack: [
'error:06089093:digital envelope routines:EVP_PKEY_CTX_ctrl:' +
'command not supported',
],
});
}
// Test throws exception when key options is null
{
assert.throws(() => {
crypto.createSign('SHA1').update('Test123').sign(null, 'base64');
}, {
code: 'ERR_CRYPTO_SIGN_KEY_REQUIRED',
name: 'Error'
});
}
{
const sign = crypto.createSign('SHA1');
const verify = crypto.createVerify('SHA1');
[1, [], {}, undefined, null, true, Infinity].forEach((input) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "algorithm" argument must be of type string.' +
`${common.invalidArgTypeHelper(input)}`
};
assert.throws(() => crypto.createSign(input), errObj);
assert.throws(() => crypto.createVerify(input), errObj);
errObj.message = 'The "data" argument must be of type string or an ' +
'instance of Buffer, TypedArray, or DataView.' +
common.invalidArgTypeHelper(input);
assert.throws(() => sign.update(input), errObj);
assert.throws(() => verify.update(input), errObj);
assert.throws(() => sign._write(input, 'utf8', () => {}), errObj);
assert.throws(() => verify._write(input, 'utf8', () => {}), errObj);
});
[
Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array,
].forEach((clazz) => {
// These should all just work
sign.update(new clazz());
verify.update(new clazz());
});
[1, {}, [], Infinity].forEach((input) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
};
assert.throws(() => sign.sign(input), errObj);
assert.throws(() => verify.verify(input), errObj);
assert.throws(() => verify.verify('test', input), errObj);
});
}
{
assert.throws(
() => crypto.createSign('sha8'),
/Invalid digest/);
assert.throws(
() => crypto.sign('sha8', Buffer.alloc(1), keyPem),
/Invalid digest/);
}
[
{ private: fixtures.readKey('ed25519_private.pem', 'ascii'),
public: fixtures.readKey('ed25519_public.pem', 'ascii'),
algo: null,
sigLen: 64 },
{ private: fixtures.readKey('ed448_private.pem', 'ascii'),
public: fixtures.readKey('ed448_public.pem', 'ascii'),
algo: null,
sigLen: 114 },
{ private: fixtures.readKey('rsa_private_2048.pem', 'ascii'),
public: fixtures.readKey('rsa_public_2048.pem', 'ascii'),
algo: 'sha1',
sigLen: 256 },
].forEach((pair) => {
const algo = pair.algo;
{
const data = Buffer.from('Hello world');
const sig = crypto.sign(algo, data, pair.private);
assert.strictEqual(sig.length, pair.sigLen);
assert.strictEqual(crypto.verify(algo, data, pair.private, sig),
true);
assert.strictEqual(crypto.verify(algo, data, pair.public, sig),
true);
}
{
const data = Buffer.from('Hello world');
const privKeyObj = crypto.createPrivateKey(pair.private);
const pubKeyObj = crypto.createPublicKey(pair.public);
const sig = crypto.sign(algo, data, privKeyObj);
assert.strictEqual(sig.length, pair.sigLen);
assert.strictEqual(crypto.verify(algo, data, privKeyObj, sig), true);
assert.strictEqual(crypto.verify(algo, data, pubKeyObj, sig), true);
}
{
const data = Buffer.from('Hello world');
const otherData = Buffer.from('Goodbye world');
const otherSig = crypto.sign(algo, otherData, pair.private);
assert.strictEqual(crypto.verify(algo, data, pair.private, otherSig),
false);
}
[
Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array,
].forEach((clazz) => {
const data = new clazz();
const sig = crypto.sign(algo, data, pair.private);
assert.strictEqual(crypto.verify(algo, data, pair.private, sig),
true);
});
});
[1, {}, [], true, Infinity].forEach((input) => {
const data = Buffer.alloc(1);
const sig = Buffer.alloc(1);
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
};
assert.throws(() => crypto.sign(null, input, 'asdf'), errObj);
assert.throws(() => crypto.verify(null, input, 'asdf', sig), errObj);
assert.throws(() => crypto.sign(null, data, input), errObj);
assert.throws(() => crypto.verify(null, data, input, sig), errObj);
errObj.message = 'The "signature" argument must be an instance of ' +
'Buffer, TypedArray, or DataView.' +
common.invalidArgTypeHelper(input);
assert.throws(() => crypto.verify(null, data, 'test', input), errObj);
});
{
const data = Buffer.from('Hello world');
const keys = [['ec-key.pem', 64], ['dsa_private_1025.pem', 40]];
for (const [file, length] of keys) {
const privKey = fixtures.readKey(file);
[
crypto.createSign('sha1').update(data).sign(privKey),
crypto.sign('sha1', data, privKey),
crypto.sign('sha1', data, { key: privKey, dsaEncoding: 'der' }),
].forEach((sig) => {
// Signature length variability due to DER encoding
assert(sig.length >= length + 4 && sig.length <= length + 8);
assert.strictEqual(
crypto.createVerify('sha1').update(data).verify(privKey, sig),
true
);
assert.strictEqual(crypto.verify('sha1', data, privKey, sig), true);
});
// Test (EC)DSA signature conversion.
const opts = { key: privKey, dsaEncoding: 'ieee-p1363' };
let sig = crypto.sign('sha1', data, opts);
// Unlike DER signatures, IEEE P1363 signatures have a predictable length.
assert.strictEqual(sig.length, length);
assert.strictEqual(crypto.verify('sha1', data, opts, sig), true);
assert.strictEqual(crypto.createVerify('sha1')
.update(data)
.verify(opts, sig), true);
// Test invalid signature lengths.
for (const i of [-2, -1, 1, 2, 4, 8]) {
sig = crypto.randomBytes(length + i);
let result;
try {
result = crypto.verify('sha1', data, opts, sig);
} catch (err) {
assert.match(err.message, /asn1 encoding/);
assert.strictEqual(err.library, 'asn1 encoding routines');
continue;
}
assert.strictEqual(result, false);
}
}
// Test verifying externally signed messages.
const extSig = Buffer.from('494c18ab5c8a62a72aea5041966902bcfa229821af2bf65' +
'0b5b4870d1fe6aebeaed9460c62210693b5b0a300033823' +
'33d9529c8abd8c5948940af944828be16c', 'hex');
for (const ok of [true, false]) {
assert.strictEqual(
crypto.verify('sha256', data, {
key: fixtures.readKey('ec-key.pem'),
dsaEncoding: 'ieee-p1363'
}, extSig),
ok
);
assert.strictEqual(
crypto.createVerify('sha256').update(data).verify({
key: fixtures.readKey('ec-key.pem'),
dsaEncoding: 'ieee-p1363'
}, extSig),
ok
);
extSig[Math.floor(Math.random() * extSig.length)] ^= 1;
}
// Non-(EC)DSA keys should ignore the option.
const sig = crypto.sign('sha1', data, {
key: keyPem,
dsaEncoding: 'ieee-p1363'
});
assert.strictEqual(crypto.verify('sha1', data, certPem, sig), true);
assert.strictEqual(
crypto.verify('sha1', data, {
key: certPem,
dsaEncoding: 'ieee-p1363'
}, sig),
true
);
assert.strictEqual(
crypto.verify('sha1', data, {
key: certPem,
dsaEncoding: 'der'
}, sig),
true
);
for (const dsaEncoding of ['foo', null, {}, 5, true, NaN]) {
assert.throws(() => {
crypto.sign('sha1', data, {
key: certPem,
dsaEncoding
});
}, {
code: 'ERR_INVALID_ARG_VALUE'
});
}
}
// RSA-PSS Sign test by verifying with 'openssl dgst -verify'
// Note: this particular test *must* be the last in this file as it will exit
// early if no openssl binary is found
{
if (!common.opensslCli)
common.skip('node compiled without OpenSSL CLI.');
const pubfile = fixtures.path('keys', 'rsa_public_2048.pem');
const privkey = fixtures.readKey('rsa_private_2048.pem');
const msg = 'Test123';
const s5 = crypto.createSign('SHA256')
.update(msg)
.sign({
key: privkey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING
});
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const sigfile = tmpdir.resolve('s5.sig');
fs.writeFileSync(sigfile, s5);
const msgfile = tmpdir.resolve('s5.msg');
fs.writeFileSync(msgfile, msg);
exec(...common.escapePOSIXShell`"${
common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${
sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${msgfile
}"`, common.mustCall((err, stdout, stderr) => {
assert(stdout.includes('Verified OK'));
}));
}
{
// Test RSA-PSS.
{
// This key pair does not restrict the message digest algorithm or salt
// length.
const publicPem = fixtures.readKey('rsa_pss_public_2048.pem');
const privatePem = fixtures.readKey('rsa_pss_private_2048.pem');
const publicKey = crypto.createPublicKey(publicPem);
const privateKey = crypto.createPrivateKey(privatePem);
for (const key of [privatePem, privateKey]) {
// Any algorithm should work.
for (const algo of ['sha1', 'sha256']) {
// Any salt length should work.
for (const saltLength of [undefined, 8, 10, 12, 16, 18, 20]) {
const signature = crypto.sign(algo, 'foo', { key, saltLength });
for (const pkey of [key, publicKey, publicPem]) {
const okay = crypto.verify(
algo,
'foo',
{ key: pkey, saltLength },
signature
);
assert.ok(okay);
}
}
}
}
}
{
// This key pair enforces sha256 as the message digest and the MGF1
// message digest and a salt length of at least 16 bytes.
const publicPem =
fixtures.readKey('rsa_pss_public_2048_sha256_sha256_16.pem');
const privatePem =
fixtures.readKey('rsa_pss_private_2048_sha256_sha256_16.pem');
const publicKey = crypto.createPublicKey(publicPem);
const privateKey = crypto.createPrivateKey(privatePem);
for (const key of [privatePem, privateKey]) {
// Signing with anything other than sha256 should fail.
assert.throws(() => {
crypto.sign('sha1', 'foo', key);
}, /digest not allowed/);
// Signing with salt lengths less than 16 bytes should fail.
for (const saltLength of [8, 10, 12]) {
assert.throws(() => {
crypto.sign('sha256', 'foo', { key, saltLength });
}, /pss saltlen too small/);
}
// Signing with sha256 and appropriate salt lengths should work.
for (const saltLength of [undefined, 16, 18, 20]) {
const signature = crypto.sign('sha256', 'foo', { key, saltLength });
for (const pkey of [key, publicKey, publicPem]) {
const okay = crypto.verify(
'sha256',
'foo',
{ key: pkey, saltLength },
signature
);
assert.ok(okay);
}
}
}
}
{
// This key enforces sha512 as the message digest and sha256 as the MGF1
// message digest.
const publicPem =
fixtures.readKey('rsa_pss_public_2048_sha512_sha256_20.pem');
const privatePem =
fixtures.readKey('rsa_pss_private_2048_sha512_sha256_20.pem');
const publicKey = crypto.createPublicKey(publicPem);
const privateKey = crypto.createPrivateKey(privatePem);
// Node.js usually uses the same hash function for the message and for MGF1.
// However, when a different MGF1 message digest algorithm has been
// specified as part of the key, it should automatically switch to that.
// This behavior is required by sections 3.1 and 3.3 of RFC4055.
for (const key of [privatePem, privateKey]) {
// sha256 matches the MGF1 hash function and should be used internally,
// but it should not be permitted as the main message digest algorithm.
for (const algo of ['sha1', 'sha256']) {
assert.throws(() => {
crypto.sign(algo, 'foo', key);
}, /digest not allowed/);
}
// sha512 should produce a valid signature.
const signature = crypto.sign('sha512', 'foo', key);
for (const pkey of [key, publicKey, publicPem]) {
const okay = crypto.verify('sha512', 'foo', pkey, signature);
assert.ok(okay);
}
}
}
}
// The sign function should not swallow OpenSSL errors.
// Regression test for https://github.com/nodejs/node/issues/40794.
{
assert.throws(() => {
const { privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 512
});
crypto.sign('sha512', 'message', privateKey);
}, {
code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY',
message: /digest too big for rsa key/
});
}
{
// This should not cause a crash: https://github.com/nodejs/node/issues/44471
for (const key of ['', 'foo', null, undefined, true, Boolean]) {
assert.throws(() => {
crypto.verify('sha256', 'foo', { key, format: 'jwk' }, Buffer.alloc(0));
}, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ });
assert.throws(() => {
crypto.createVerify('sha256').verify({ key, format: 'jwk' }, Buffer.alloc(0));
}, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ });
assert.throws(() => {
crypto.sign('sha256', 'foo', { key, format: 'jwk' });
}, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ });
assert.throws(() => {
crypto.createSign('sha256').sign({ key, format: 'jwk' });
}, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ });
}
}
{
// Ed25519 and Ed448 must use the one-shot methods
const keys = [{ privateKey: fixtures.readKey('ed25519_private.pem', 'ascii'),
publicKey: fixtures.readKey('ed25519_public.pem', 'ascii') },
{ privateKey: fixtures.readKey('ed448_private.pem', 'ascii'),
publicKey: fixtures.readKey('ed448_public.pem', 'ascii') }];
for (const { publicKey, privateKey } of keys) {
assert.throws(() => {
crypto.createSign('SHA256').update('Test123').sign(privateKey);
}, { code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION', message: 'Unsupported crypto operation' });
assert.throws(() => {
crypto.createVerify('SHA256').update('Test123').verify(privateKey, 'sig');
}, { code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION', message: 'Unsupported crypto operation' });
assert.throws(() => {
crypto.createVerify('SHA256').update('Test123').verify(publicKey, 'sig');
}, { code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION', message: 'Unsupported crypto operation' });
}
}
{
// Dh, x25519 and x448 should not be used for signing/verifying
// https://github.com/nodejs/node/issues/53742
for (const algo of ['dh', 'x25519', 'x448']) {
const privateKey = fixtures.readKey(`${algo}_private.pem`, 'ascii');
const publicKey = fixtures.readKey(`${algo}_public.pem`, 'ascii');
assert.throws(() => {
crypto.createSign('SHA256').update('Test123').sign(privateKey);
}, { code: 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE', message: /operation not supported for this keytype/ });
assert.throws(() => {
crypto.createVerify('SHA256').update('Test123').verify(privateKey, 'sig');
}, { code: 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE', message: /operation not supported for this keytype/ });
assert.throws(() => {
crypto.createVerify('SHA256').update('Test123').verify(publicKey, 'sig');
}, { code: 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE', message: /operation not supported for this keytype/ });
}
}