Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: https://github.com/nodejs/node/pull/63255 Refs: https://github.com/electron/electron/issues/36256 Refs: https://github.com/electron/electron/issues/41720 Refs: https://github.com/electron/electron/pull/51127 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
580 lines
21 KiB
JavaScript
580 lines
21 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
if (!common.hasCrypto)
|
|
common.skip('missing crypto');
|
|
|
|
const assert = require('assert');
|
|
const crypto = require('crypto');
|
|
const fixtures = require('../common/fixtures');
|
|
const { hasOpenSSL } = require('../common/crypto');
|
|
|
|
// EC: NIST and OpenSSL curve names are both recognized for raw-public and raw-private
|
|
{
|
|
const pubKeyObj = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p256_public.pem', 'ascii'));
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey('ec_p256_private.pem', 'ascii'));
|
|
|
|
const rawPub = pubKeyObj.export({ format: 'raw-public' });
|
|
const rawPriv = privKeyObj.export({ format: 'raw-private' });
|
|
|
|
for (const namedCurve of ['P-256', 'prime256v1']) {
|
|
const importedPub = crypto.createPublicKey({
|
|
key: rawPub, format: 'raw-public', asymmetricKeyType: 'ec', namedCurve,
|
|
});
|
|
assert.strictEqual(importedPub.equals(pubKeyObj), true);
|
|
|
|
const importedPriv = crypto.createPrivateKey({
|
|
key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ec', namedCurve,
|
|
});
|
|
assert.strictEqual(importedPriv.equals(privKeyObj), true);
|
|
}
|
|
}
|
|
|
|
// Raw key imports do not support strings.
|
|
{
|
|
const pubKeyObj = crypto.createPublicKey(
|
|
fixtures.readKey('ed25519_public.pem', 'ascii'));
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey('ed25519_private.pem', 'ascii'));
|
|
|
|
const rawPub = pubKeyObj.export({ format: 'raw-public' });
|
|
const rawPriv = privKeyObj.export({ format: 'raw-private' });
|
|
|
|
for (const encoding of ['hex', 'base64', 'utf8', 'latin1', 'ascii']) {
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: rawPub.toString(encoding),
|
|
encoding,
|
|
format: 'raw-public',
|
|
asymmetricKeyType: 'ed25519',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: rawPriv.toString(encoding),
|
|
encoding,
|
|
format: 'raw-private',
|
|
asymmetricKeyType: 'ed25519',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
}
|
|
}
|
|
|
|
// Raw public keys cannot be imported as private keys.
|
|
{
|
|
const rawPublicKeys = [
|
|
['ec', 'ec_p256_public.pem', { namedCurve: 'P-256' }],
|
|
['ed25519', 'ed25519_public.pem'],
|
|
['x25519', 'x25519_public.pem'],
|
|
];
|
|
|
|
if (!process.features.openssl_is_boringssl) {
|
|
rawPublicKeys.push(
|
|
['ed448', 'ed448_public.pem'],
|
|
['x448', 'x448_public.pem'],
|
|
);
|
|
} else {
|
|
common.printSkipMessage('Skipping unsupported ed448/x448 test cases');
|
|
}
|
|
|
|
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
|
|
rawPublicKeys.push(
|
|
['ml-dsa-44', 'ml_dsa_44_public.pem'],
|
|
['ml-kem-768', 'ml_kem_768_public.pem'],
|
|
);
|
|
}
|
|
|
|
if (hasOpenSSL(3, 5)) {
|
|
rawPublicKeys.push(
|
|
['slh-dsa-sha2-128f', 'slh_dsa_sha2_128f_public.pem'],
|
|
);
|
|
}
|
|
|
|
for (const [asymmetricKeyType, fixture, options = {}] of rawPublicKeys) {
|
|
const publicKey = crypto.createPublicKey(fixtures.readKey(fixture, 'ascii'));
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: publicKey.export({ format: 'raw-public' }),
|
|
format: 'raw-public',
|
|
asymmetricKeyType,
|
|
...options,
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
}
|
|
|
|
// Raw seed imports do not support strings.
|
|
if (hasOpenSSL(3, 5)) {
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey('ml_dsa_44_private.pem', 'ascii'));
|
|
|
|
const rawSeed = privKeyObj.export({ format: 'raw-seed' });
|
|
|
|
for (const encoding of ['hex', 'base64']) {
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: rawSeed.toString(encoding),
|
|
encoding,
|
|
format: 'raw-seed',
|
|
asymmetricKeyType: 'ml-dsa-44',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
}
|
|
}
|
|
|
|
// Key types that don't support raw-* formats
|
|
{
|
|
const unsupportedKeyTypes = [
|
|
['rsa', 'rsa_public_2048.pem', 'rsa_private_2048.pem'],
|
|
];
|
|
if (!process.features.openssl_is_boringssl) {
|
|
unsupportedKeyTypes.push(['dsa', 'dsa_public.pem', 'dsa_private.pem']);
|
|
} else {
|
|
common.printSkipMessage('Skipping unsupported dsa test case');
|
|
}
|
|
for (const [type, pub, priv] of unsupportedKeyTypes) {
|
|
const pubKeyObj = crypto.createPublicKey(
|
|
fixtures.readKey(pub, 'ascii'));
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey(priv, 'ascii'));
|
|
|
|
assert.throws(() => pubKeyObj.export({ format: 'raw-public' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => privKeyObj.export({ format: 'raw-private' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => privKeyObj.export({ format: 'raw-seed' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
|
|
for (const format of ['raw-public', 'raw-private', 'raw-seed']) {
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(32), format, asymmetricKeyType: type,
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
}
|
|
|
|
// DH keys also don't support raw formats
|
|
if (!process.features.openssl_is_boringssl) {
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey('dh_private.pem', 'ascii'));
|
|
assert.throws(() => privKeyObj.export({ format: 'raw-private' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: Buffer.alloc(32), format: 'raw-public', asymmetricKeyType: 'dh',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
for (const format of ['raw-private', 'raw-seed']) {
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: Buffer.alloc(32), format, asymmetricKeyType: 'dh',
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
} else {
|
|
common.printSkipMessage('Skipping unsupported dh test case');
|
|
}
|
|
}
|
|
|
|
// PQC import throws when PQC is not supported
|
|
if (!hasOpenSSL(3, 5)) {
|
|
const unsupported = process.features.openssl_is_boringssl ?
|
|
// BoringSSL supports ML-DSA and ML-KEM-{768,1024}, but not ML-KEM-512 or SLH-DSA.
|
|
['ml-kem-512', 'slh-dsa-sha2-128f', 'slh-dsa-shake-128f'] :
|
|
[
|
|
'ml-dsa-44', 'ml-dsa-65', 'ml-dsa-87',
|
|
'ml-kem-512', 'ml-kem-768', 'ml-kem-1024',
|
|
'slh-dsa-sha2-128f', 'slh-dsa-shake-128f',
|
|
];
|
|
for (const asymmetricKeyType of unsupported) {
|
|
for (const format of ['raw-public', 'raw-private', 'raw-seed']) {
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(32), format, asymmetricKeyType,
|
|
}), {
|
|
code: 'ERR_INVALID_ARG_VALUE',
|
|
message: /Invalid asymmetricKeyType|Unsupported key type/
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// EC: P-256 and P-384 keys cannot be imported as private/public of the other type
|
|
{
|
|
const p256Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p256_public.pem', 'ascii'));
|
|
const p384Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p384_public.pem', 'ascii'));
|
|
const p256Priv = crypto.createPrivateKey(
|
|
fixtures.readKey('ec_p256_private.pem', 'ascii'));
|
|
const p384Priv = crypto.createPrivateKey(
|
|
fixtures.readKey('ec_p384_private.pem', 'ascii'));
|
|
|
|
const p256RawPub = p256Pub.export({ format: 'raw-public' });
|
|
const p384RawPub = p384Pub.export({ format: 'raw-public' });
|
|
const p256RawPriv = p256Priv.export({ format: 'raw-private' });
|
|
const p384RawPriv = p384Priv.export({ format: 'raw-private' });
|
|
|
|
// P-256 public imported as P-384
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: p256RawPub, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-384',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// P-384 public imported as P-256
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: p384RawPub, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// P-256 private imported as P-384
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: p256RawPriv, format: 'raw-private',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-384',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// P-384 private imported as P-256
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: p384RawPriv, format: 'raw-private',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// ML-KEM: public keys of different type cannot be imported as the other type
|
|
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
|
|
const mlKem768Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ml_kem_768_public.pem', 'ascii'));
|
|
const mlKem1024Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ml_kem_1024_public.pem', 'ascii'));
|
|
|
|
const mlKem768RawPub = mlKem768Pub.export({ format: 'raw-public' });
|
|
const mlKem1024RawPub = mlKem1024Pub.export({ format: 'raw-public' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: mlKem768RawPub, format: 'raw-public', asymmetricKeyType: 'ml-kem-1024',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: mlKem1024RawPub, format: 'raw-public', asymmetricKeyType: 'ml-kem-768',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// ML-DSA: -44 and -65 public keys cannot be imported as the other type
|
|
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
|
|
const mlDsa44Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ml_dsa_44_public.pem', 'ascii'));
|
|
const mlDsa65Pub = crypto.createPublicKey(
|
|
fixtures.readKey('ml_dsa_65_public.pem', 'ascii'));
|
|
|
|
const mlDsa44RawPub = mlDsa44Pub.export({ format: 'raw-public' });
|
|
const mlDsa65RawPub = mlDsa65Pub.export({ format: 'raw-public' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: mlDsa44RawPub, format: 'raw-public', asymmetricKeyType: 'ml-dsa-65',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: mlDsa65RawPub, format: 'raw-public', asymmetricKeyType: 'ml-dsa-44',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// SLH-DSA: mismatched key types with different sizes are rejected
|
|
if (hasOpenSSL(3, 5)) {
|
|
const slh128fPub = crypto.createPublicKey(
|
|
fixtures.readKey('slh_dsa_sha2_128f_public.pem', 'ascii'));
|
|
const slh192fPub = crypto.createPublicKey(
|
|
fixtures.readKey('slh_dsa_sha2_192f_public.pem', 'ascii'));
|
|
const slh128fPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('slh_dsa_sha2_128f_private.pem', 'ascii'));
|
|
const slh192fPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('slh_dsa_sha2_192f_private.pem', 'ascii'));
|
|
|
|
const rawPub128f = slh128fPub.export({ format: 'raw-public' });
|
|
const rawPub192f = slh192fPub.export({ format: 'raw-public' });
|
|
const rawPriv128f = slh128fPriv.export({ format: 'raw-private' });
|
|
const rawPriv192f = slh192fPriv.export({ format: 'raw-private' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: rawPub128f, format: 'raw-public',
|
|
asymmetricKeyType: 'slh-dsa-sha2-192f',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: rawPub192f, format: 'raw-public',
|
|
asymmetricKeyType: 'slh-dsa-sha2-128f',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: rawPriv128f, format: 'raw-private',
|
|
asymmetricKeyType: 'slh-dsa-sha2-192f',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: rawPriv192f, format: 'raw-private',
|
|
asymmetricKeyType: 'slh-dsa-sha2-128f',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// Passphrase cannot be used with raw formats
|
|
{
|
|
const privKeyObj = crypto.createPrivateKey(
|
|
fixtures.readKey('ed25519_private.pem', 'ascii'));
|
|
|
|
assert.throws(() => privKeyObj.export({
|
|
format: 'raw-private', passphrase: 'test',
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
|
|
assert.throws(() => privKeyObj.export({
|
|
format: 'raw-seed', passphrase: 'test',
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
|
|
// raw-seed export is rejected for key types that do not support seeds
|
|
{
|
|
const ecPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('ec_p256_private.pem', 'ascii'));
|
|
assert.throws(() => ecPriv.export({ format: 'raw-seed' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: ecPriv.export({ format: 'raw-private' }),
|
|
format: 'raw-seed',
|
|
asymmetricKeyType: 'ec',
|
|
namedCurve: 'P-256',
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
|
|
if (process.features.openssl_is_boringssl) {
|
|
common.printSkipMessage('Skipping unsupported ed448/x448 test cases');
|
|
}
|
|
for (const type of process.features.openssl_is_boringssl ?
|
|
['ed25519', 'x25519'] :
|
|
['ed25519', 'ed448', 'x25519', 'x448']) {
|
|
const priv = crypto.createPrivateKey(
|
|
fixtures.readKey(`${type}_private.pem`, 'ascii'));
|
|
assert.throws(() => priv.export({ format: 'raw-seed' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: priv.export({ format: 'raw-private' }),
|
|
format: 'raw-seed',
|
|
asymmetricKeyType: type,
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
|
|
if (hasOpenSSL(3, 5)) {
|
|
const slhPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('slh_dsa_sha2_128f_private.pem', 'ascii'));
|
|
assert.throws(() => slhPriv.export({ format: 'raw-seed' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: slhPriv.export({ format: 'raw-private' }),
|
|
format: 'raw-seed',
|
|
asymmetricKeyType: 'slh-dsa-sha2-128f',
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
}
|
|
|
|
// raw-private cannot be used for ml-kem and ml-dsa
|
|
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
|
|
for (const type of ['ml-kem-768', 'ml-dsa-44']) {
|
|
const priv = crypto.createPrivateKey(
|
|
fixtures.readKey(`${type.replaceAll('-', '_')}_private_seed_only.pem`, 'ascii'));
|
|
assert.throws(() => priv.export({ format: 'raw-private' }),
|
|
{ code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: priv.export({ format: 'raw-seed' }),
|
|
format: 'raw-private',
|
|
asymmetricKeyType: type,
|
|
}), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' });
|
|
}
|
|
}
|
|
|
|
// EC: defaults to uncompressed, can be switched to compressed, both can be imported
|
|
{
|
|
const pubKeyObj = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p256_public.pem', 'ascii'));
|
|
|
|
const defaultExport = pubKeyObj.export({ format: 'raw-public' });
|
|
const compressed = pubKeyObj.export({ format: 'raw-public', type: 'compressed' });
|
|
const uncompressed = pubKeyObj.export({ format: 'raw-public', type: 'uncompressed' });
|
|
|
|
// Default is uncompressed
|
|
assert.deepStrictEqual(defaultExport, uncompressed);
|
|
|
|
// Compressed starts with 0x02 or 0x03 and is 33 bytes for P-256
|
|
assert.strictEqual(compressed.byteLength, 33);
|
|
assert(compressed[0] === 0x02 || compressed[0] === 0x03);
|
|
|
|
// Uncompressed starts with 0x04 and is 65 bytes for P-256
|
|
assert.strictEqual(uncompressed.byteLength, 65);
|
|
assert.strictEqual(uncompressed[0], 0x04);
|
|
|
|
// Both can be imported
|
|
const fromCompressed = crypto.createPublicKey({
|
|
key: compressed, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
});
|
|
assert.strictEqual(fromCompressed.equals(pubKeyObj), true);
|
|
|
|
const fromUncompressed = crypto.createPublicKey({
|
|
key: uncompressed, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
});
|
|
assert.strictEqual(fromUncompressed.equals(pubKeyObj), true);
|
|
}
|
|
|
|
// Compressed and uncompressed are the only recognized options
|
|
{
|
|
const pubKeyObj = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p256_public.pem', 'ascii'));
|
|
|
|
assert.throws(() => pubKeyObj.export({ format: 'raw-public', type: 'hybrid' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
assert.throws(() => pubKeyObj.export({ format: 'raw-public', type: 'invalid' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// None of the raw types can be used with symmetric key objects
|
|
{
|
|
const secretKey = crypto.createSecretKey(Buffer.alloc(32));
|
|
|
|
for (const format of ['raw-public', 'raw-private', 'raw-seed']) {
|
|
assert.throws(() => secretKey.export({ format }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
}
|
|
|
|
// Private key objects created from raw-seed or raw-private can be passed to createPublicKey()
|
|
{
|
|
// Ed25519 raw-private -> createPublicKey
|
|
const edPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('ed25519_private.pem', 'ascii'));
|
|
const edPub = crypto.createPublicKey(
|
|
fixtures.readKey('ed25519_public.pem', 'ascii'));
|
|
const rawPriv = edPriv.export({ format: 'raw-private' });
|
|
const importedPriv = crypto.createPrivateKey({
|
|
key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ed25519',
|
|
});
|
|
const derivedPub = crypto.createPublicKey(importedPriv);
|
|
assert.strictEqual(derivedPub.equals(edPub), true);
|
|
// Private key must not be extractable from the derived public key.
|
|
assert.throws(() => derivedPub.export({ format: 'pem', type: 'pkcs8' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
assert.throws(() => derivedPub.export({ format: 'der', type: 'pkcs8' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// EC raw-private -> createPublicKey
|
|
const ecPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('ec_p256_private.pem', 'ascii'));
|
|
const ecPub = crypto.createPublicKey(
|
|
fixtures.readKey('ec_p256_public.pem', 'ascii'));
|
|
const ecRawPriv = ecPriv.export({ format: 'raw-private' });
|
|
const ecImportedPriv = crypto.createPrivateKey({
|
|
key: ecRawPriv, format: 'raw-private',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
});
|
|
const ecDerivedPub = crypto.createPublicKey(ecImportedPriv);
|
|
assert.strictEqual(ecDerivedPub.equals(ecPub), true);
|
|
// Private key must not be extractable from the derived public key.
|
|
assert.throws(() => ecDerivedPub.export({ format: 'pem', type: 'pkcs8' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
assert.throws(() => ecDerivedPub.export({ format: 'pem', type: 'sec1' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// PQC raw-seed -> createPublicKey
|
|
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
|
|
const mlDsaPriv = crypto.createPrivateKey(
|
|
fixtures.readKey('ml_dsa_44_private_seed_only.pem', 'ascii'));
|
|
const mlDsaPub = crypto.createPublicKey(
|
|
fixtures.readKey('ml_dsa_44_public.pem', 'ascii'));
|
|
const mlDsaRawSeed = mlDsaPriv.export({ format: 'raw-seed' });
|
|
const mlDsaImportedPriv = crypto.createPrivateKey({
|
|
key: mlDsaRawSeed, format: 'raw-seed', asymmetricKeyType: 'ml-dsa-44',
|
|
});
|
|
const mlDsaDerivedPub = crypto.createPublicKey(mlDsaImportedPriv);
|
|
assert.strictEqual(mlDsaDerivedPub.equals(mlDsaPub), true);
|
|
// Private key must not be extractable from the derived public key.
|
|
assert.throws(() => mlDsaDerivedPub.export({ format: 'pem', type: 'pkcs8' }),
|
|
{ code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
}
|
|
|
|
// raw-public EC keys that are garbage/not on curve are rejected
|
|
{
|
|
const garbage = Buffer.alloc(33, 0xff);
|
|
garbage[0] = 0x02; // Valid compressed prefix but invalid point
|
|
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: garbage, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
// Totally random garbage
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(10, 0xab), format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// Unrecognized namedCurve values are rejected
|
|
{
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(33), format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'not-a-curve',
|
|
}), { code: 'ERR_CRYPTO_INVALID_CURVE' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: Buffer.alloc(32), format: 'raw-private',
|
|
asymmetricKeyType: 'ec', namedCurve: 'not-a-curve',
|
|
}), { code: 'ERR_CRYPTO_INVALID_CURVE' });
|
|
}
|
|
|
|
// x25519, ed25519, x448, and ed448 cannot be used as 'ec' namedCurve values
|
|
{
|
|
if (process.features.openssl_is_boringssl) {
|
|
common.printSkipMessage('Skipping unsupported ed448/x448 test cases');
|
|
}
|
|
for (const type of process.features.openssl_is_boringssl ?
|
|
['ed25519', 'x25519'] :
|
|
['ed25519', 'x25519', 'ed448', 'x448']) {
|
|
const priv = crypto.createPrivateKey(
|
|
fixtures.readKey(`${type}_private.pem`, 'ascii'));
|
|
const pub = crypto.createPublicKey(
|
|
fixtures.readKey(`${type}_public.pem`, 'ascii'));
|
|
|
|
const rawPub = pub.export({ format: 'raw-public' });
|
|
const rawPriv = priv.export({ format: 'raw-private' });
|
|
|
|
// Try to import as EC - must fail
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: rawPub, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: type,
|
|
}), { code: 'ERR_CRYPTO_INVALID_CURVE' });
|
|
|
|
assert.throws(() => crypto.createPrivateKey({
|
|
key: rawPriv, format: 'raw-private',
|
|
asymmetricKeyType: 'ec', namedCurve: type,
|
|
}), { code: 'ERR_CRYPTO_INVALID_CURVE' });
|
|
}
|
|
}
|
|
|
|
// Missing asymmetricKeyType option
|
|
{
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(32), format: 'raw-public',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
}
|
|
|
|
// Unknown asymmetricKeyType value
|
|
{
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(32), format: 'raw-public',
|
|
asymmetricKeyType: 'unknown',
|
|
}), { code: 'ERR_INVALID_ARG_VALUE' });
|
|
}
|
|
|
|
// Non-buffer key data
|
|
{
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: 12345, format: 'raw-public',
|
|
asymmetricKeyType: 'ec', namedCurve: 'P-256',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
}
|
|
|
|
// Missing namedCurve for EC
|
|
{
|
|
assert.throws(() => crypto.createPublicKey({
|
|
key: Buffer.alloc(33), format: 'raw-public',
|
|
asymmetricKeyType: 'ec',
|
|
}), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
}
|