| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 96f19a1 commit 729274e
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -666,6 +666,10 @@ function prepareAsymmetricKey(key, ctx, name = 'key') { | |||
| 666 | 666 | return { data, format: kKeyFormatJWK }; | |
| 667 | 667 | } else if (format === 'raw-public' || format === 'raw-private' || | |
| 668 | 668 | format === 'raw-seed') { | |
| 669 | + if ((ctx === kConsumePrivate || ctx === kCreatePrivate) && | ||
| 670 | + format === 'raw-public') { | ||
| 671 | + throw new ERR_INVALID_ARG_VALUE(`${name}.format`, format); | ||
| 672 | + } | ||
| 669 | 673 | if (!isArrayBufferView(data) && !isAnyArrayBuffer(data)) { | |
| 670 | 674 | throw new ERR_INVALID_ARG_TYPE( | |
| 671 | 675 | `${name}.key`, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,8 +13,8 @@ const { | |||
| 13 | 13 | KEMDecapsulateJob, | |
| 14 | 14 | KEMEncapsulateJob, | |
| 15 | 15 | kKeyFormatDER, | |
| 16 | - kKeyFormatRawPrivate, | ||
| 17 | 16 | kKeyFormatRawPublic, | |
| 17 | + kKeyFormatRawSeed, | ||
| 18 | 18 | kWebCryptoKeyFormatPKCS8, | |
| 19 | 19 | kWebCryptoKeyFormatRaw, | |
| 20 | 20 | kWebCryptoKeyFormatSPKI, | |
@@ -178,7 +178,7 @@ function mlKemImportKey( | |||
| 178 | 178 | case 'raw-seed': { | |
| 179 | 179 | const isPublic = format === 'raw-public'; | |
| 180 | 180 | verifyAcceptableMlKemKeyUse(name, isPublic, usagesSet); | |
| 181 | - handle = importRawKey(isPublic, keyData, isPublic ? kKeyFormatRawPublic : kKeyFormatRawPrivate, name); | ||
| 181 | + handle = importRawKey(isPublic, keyData, isPublic ? kKeyFormatRawPublic : kKeyFormatRawSeed, name); | ||
| 182 | 182 | break; | |
| 183 | 183 | } | |
| 184 | 184 | case 'jwk': { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -336,6 +336,85 @@ int GetNidFromName(const char* name) { | |||
| 336 | 336 | } | |
| 337 | 337 | return NID_undef; | |
| 338 | 338 | } | |
| 339 | + | ||
| 340 | + bool IsUnavailablePqcKeyType(Environment* env, Local<String> key_type) { | ||
| 341 | + return key_type->StringEquals(env->crypto_ml_dsa_44_string()) || | ||
| 342 | + key_type->StringEquals(env->crypto_ml_dsa_65_string()) || | ||
| 343 | + key_type->StringEquals(env->crypto_ml_dsa_87_string()) || | ||
| 344 | + key_type->StringEquals(env->crypto_ml_kem_512_string()) || | ||
| 345 | + key_type->StringEquals(env->crypto_ml_kem_768_string()) || | ||
| 346 | + key_type->StringEquals(env->crypto_ml_kem_1024_string()) || | ||
| 347 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_128f_string()) || | ||
| 348 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_128s_string()) || | ||
| 349 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_192f_string()) || | ||
| 350 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_192s_string()) || | ||
| 351 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_256f_string()) || | ||
| 352 | + key_type->StringEquals(env->crypto_slh_dsa_sha2_256s_string()) || | ||
| 353 | + key_type->StringEquals(env->crypto_slh_dsa_shake_128f_string()) || | ||
| 354 | + key_type->StringEquals(env->crypto_slh_dsa_shake_128s_string()) || | ||
| 355 | + key_type->StringEquals(env->crypto_slh_dsa_shake_192f_string()) || | ||
| 356 | + key_type->StringEquals(env->crypto_slh_dsa_shake_192s_string()) || | ||
| 357 | + key_type->StringEquals(env->crypto_slh_dsa_shake_256f_string()) || | ||
| 358 | + key_type->StringEquals(env->crypto_slh_dsa_shake_256s_string()); | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + bool IsUnsupportedRawKeyType(Environment* env, Local<String> key_type) { | ||
| 362 | + return key_type->StringEquals(env->crypto_rsa_string()) || | ||
| 363 | + key_type->StringEquals(env->crypto_rsa_pss_string()) || | ||
| 364 | + key_type->StringEquals(env->crypto_dsa_string()) || | ||
| 365 | + key_type->StringEquals(env->crypto_dh_string()); | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + void ValidateRawKeyImportFormat(Environment* env, | ||
| 369 | + Local<String> key_type, | ||
| 370 | + const char* key_type_name, | ||
| 371 | + int id, | ||
| 372 | + EVPKeyPointer::PKFormatType format) { | ||
| 373 | + auto validate_raw_format = | ||
| 374 | + [&](EVPKeyPointer::PKFormatType expected_private_format) { | ||
| 375 | + if (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC || | ||
| 376 | + format == expected_private_format) { | ||
| 377 | + return; | ||
| 378 | + } | ||
| 379 | + THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); | ||
| 380 | + }; | ||
| 381 | + | ||
| 382 | + if (key_type->StringEquals(env->crypto_ec_string())) { | ||
| 383 | + return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + switch (id) { | ||
| 387 | + case EVP_PKEY_X25519: | ||
| 388 | + case EVP_PKEY_X448: | ||
| 389 | + case EVP_PKEY_ED25519: | ||
| 390 | + case EVP_PKEY_ED448: | ||
| 391 | + return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); | ||
| 392 | + default: | ||
| 393 | + break; | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + #if OPENSSL_WITH_PQC | ||
| 397 | + if (IsPqcSeedKeyId(id)) { | ||
| 398 | + return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_SEED); | ||
| 399 | + } | ||
| 400 | + if (IsPqcRawPrivateKeyId(id)) { | ||
| 401 | + return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); | ||
| 402 | + } | ||
| 403 | + #endif | ||
| 404 | + | ||
| 405 | + if (IsUnavailablePqcKeyType(env, key_type)) { | ||
| 406 | + THROW_ERR_INVALID_ARG_VALUE(env, "Unsupported key type"); | ||
| 407 | + return; | ||
| 408 | + } | ||
| 409 | + | ||
| 410 | + if (IsUnsupportedRawKeyType(env, key_type)) { | ||
| 411 | + THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); | ||
| 412 | + return; | ||
| 413 | + } | ||
| 414 | + | ||
| 415 | + THROW_ERR_INVALID_ARG_VALUE( | ||
| 416 | + env, "Invalid asymmetricKeyType: %s", key_type_name); | ||
| 417 | + } | ||
| 339 | 418 | } // namespace | |
| 340 | 419 | ||
| 341 | 420 | bool KeyObjectData::ToEncodedPublicKey( | |
@@ -585,6 +664,12 @@ static KeyObjectData ImportRawKey(Environment* env, | |||
| 585 | 664 | } | |
| 586 | 665 | }; | |
| 587 | 666 | ||
| 667 | + const int id = GetNidFromName(key_type_name); | ||
| 668 | + ValidateRawKeyImportFormat(env, key_type, key_type_name, id, format); | ||
| 669 | + if (env->isolate()->HasPendingException()) { | ||
| 670 | + return {}; | ||
| 671 | + } | ||
| 672 | + | ||
| 588 | 673 | // EC keys | |
| 589 | 674 | if (key_type->StringEquals(env->crypto_ec_string())) { | |
| 590 | 675 | int curve_nid = ncrypto::Ec::GetCurveIdFromName(named_curve); | |
@@ -642,8 +727,6 @@ static KeyObjectData ImportRawKey(Environment* env, | |||
| 642 | 727 | return KeyObjectData::CreateAsymmetric(target_type, std::move(pkey)); | |
| 643 | 728 | } | |
| 644 | 729 | ||
| 645 | - int id = GetNidFromName(key_type_name); | ||
| 646 | - | ||
| 647 | 730 | typedef EVPKeyPointer (*new_key_fn)( | |
| 648 | 731 | int, const ncrypto::Buffer<const unsigned char>&); | |
| 649 | 732 | new_key_fn fn = nullptr; | |
@@ -698,40 +781,6 @@ static KeyObjectData ImportRawKey(Environment* env, | |||
| 698 | 781 | return KeyObjectData::CreateAsymmetric(target_type, std::move(pkey)); | |
| 699 | 782 | } | |
| 700 | 783 | ||
| 701 | - if (key_type->StringEquals(env->crypto_rsa_string()) || | ||
| 702 | - key_type->StringEquals(env->crypto_rsa_pss_string()) || | ||
| 703 | - key_type->StringEquals(env->crypto_dsa_string()) || | ||
| 704 | - key_type->StringEquals(env->crypto_dh_string())) { | ||
| 705 | - THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); | ||
| 706 | - return {}; | ||
| 707 | - } | ||
| 708 | - | ||
| 709 | - #if !OPENSSL_WITH_PQC | ||
| 710 | - if (key_type->StringEquals(env->crypto_ml_dsa_44_string()) || | ||
| 711 | - key_type->StringEquals(env->crypto_ml_dsa_65_string()) || | ||
| 712 | - key_type->StringEquals(env->crypto_ml_dsa_87_string()) || | ||
| 713 | - key_type->StringEquals(env->crypto_ml_kem_512_string()) || | ||
| 714 | - key_type->StringEquals(env->crypto_ml_kem_768_string()) || | ||
| 715 | - key_type->StringEquals(env->crypto_ml_kem_1024_string()) || | ||
| 716 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_128f_string()) || | ||
| 717 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_128s_string()) || | ||
| 718 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_192f_string()) || | ||
| 719 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_192s_string()) || | ||
| 720 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_256f_string()) || | ||
| 721 | - key_type->StringEquals(env->crypto_slh_dsa_sha2_256s_string()) || | ||
| 722 | - key_type->StringEquals(env->crypto_slh_dsa_shake_128f_string()) || | ||
| 723 | - key_type->StringEquals(env->crypto_slh_dsa_shake_128s_string()) || | ||
| 724 | - key_type->StringEquals(env->crypto_slh_dsa_shake_192f_string()) || | ||
| 725 | - key_type->StringEquals(env->crypto_slh_dsa_shake_192s_string()) || | ||
| 726 | - key_type->StringEquals(env->crypto_slh_dsa_shake_256f_string()) || | ||
| 727 | - key_type->StringEquals(env->crypto_slh_dsa_shake_256s_string())) { | ||
| 728 | - THROW_ERR_INVALID_ARG_VALUE(env, "Unsupported key type"); | ||
| 729 | - return {}; | ||
| 730 | - } | ||
| 731 | - #endif | ||
| 732 | - | ||
| 733 | - THROW_ERR_INVALID_ARG_VALUE( | ||
| 734 | - env, "Invalid asymmetricKeyType: %s", key_type_name); | ||
| 735 | 784 | return {}; | |
| 736 | 785 | } | |
| 737 | 786 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -175,6 +175,16 @@ KeyObjectData ImportJWKPqcKey(Environment* env, Local<Object> jwk) { | |||
| 175 | 175 | ||
| 176 | 176 | return KeyObjectData::CreateAsymmetric(type, std::move(pkey)); | |
| 177 | 177 | } | |
| 178 | + | ||
| 179 | + bool IsPqcRawPrivateKeyId(int id) { | ||
| 180 | + const PqcAlgorithm* alg = FindPqcAlgorithmById(id); | ||
| 181 | + return alg != nullptr && !alg->use_seed; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + bool IsPqcSeedKeyId(int id) { | ||
| 185 | + const PqcAlgorithm* alg = FindPqcAlgorithmById(id); | ||
| 186 | + return alg != nullptr && alg->use_seed; | ||
| 187 | + } | ||
| 178 | 188 | #endif | |
| 179 | 189 | } // namespace crypto | |
| 180 | 190 | } // namespace node | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,13 @@ bool ExportJwkPqcKey(Environment* env, | |||
| 15 | 15 | v8::Local<v8::Object> target); | |
| 16 | 16 | ||
| 17 | 17 | KeyObjectData ImportJWKPqcKey(Environment* env, v8::Local<v8::Object> jwk); | |
| 18 | + | ||
| 19 | + // Returns true for PQC algorithms that support raw private key export/import. | ||
| 20 | + bool IsPqcRawPrivateKeyId(int id); | ||
| 21 | + // Returns true for PQC algorithms that carry the private key as a seed | ||
| 22 | + // (ML-DSA, ML-KEM). Returns false for algorithms that use the expanded | ||
| 23 | + // private key (SLH-DSA), or for non-PQC ids. | ||
| 24 | + bool IsPqcSeedKeyId(int id); | ||
| 18 | 25 | #endif | |
| 19 | 26 | } // namespace crypto | |
| 20 | 27 | } // namespace node | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,47 @@ const { hasOpenSSL } = require('../common/crypto'); | |||
| 59 | 59 | } | |
| 60 | 60 | } | |
| 61 | 61 | ||
| 62 | + // Raw public keys cannot be imported as private keys. | ||
| 63 | + { | ||
| 64 | + const rawPublicKeys = [ | ||
| 65 | + ['ec', 'ec_p256_public.pem', { namedCurve: 'P-256' }], | ||
| 66 | + ['ed25519', 'ed25519_public.pem'], | ||
| 67 | + ['x25519', 'x25519_public.pem'], | ||
| 68 | + ]; | ||
| 69 | + | ||
| 70 | + if (!process.features.openssl_is_boringssl) { | ||
| 71 | + rawPublicKeys.push( | ||
| 72 | + ['ed448', 'ed448_public.pem'], | ||
| 73 | + ['x448', 'x448_public.pem'], | ||
| 74 | + ); | ||
| 75 | + } else { | ||
| 76 | + common.printSkipMessage('Skipping unsupported ed448/x448 test cases'); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) { | ||
| 80 | + rawPublicKeys.push( | ||
| 81 | + ['ml-dsa-44', 'ml_dsa_44_public.pem'], | ||
| 82 | + ['ml-kem-768', 'ml_kem_768_public.pem'], | ||
| 83 | + ); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + if (hasOpenSSL(3, 5)) { | ||
| 87 | + rawPublicKeys.push( | ||
| 88 | + ['slh-dsa-sha2-128f', 'slh_dsa_sha2_128f_public.pem'], | ||
| 89 | + ); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + for (const [asymmetricKeyType, fixture, options = {}] of rawPublicKeys) { | ||
| 93 | + const publicKey = crypto.createPublicKey(fixtures.readKey(fixture, 'ascii')); | ||
| 94 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 95 | + key: publicKey.export({ format: 'raw-public' }), | ||
| 96 | + format: 'raw-public', | ||
| 97 | + asymmetricKeyType, | ||
| 98 | + ...options, | ||
| 99 | + }), { code: 'ERR_INVALID_ARG_VALUE' }); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 62 | 103 | // Raw seed imports do not support strings. | |
| 63 | 104 | if (hasOpenSSL(3, 5)) { | |
| 64 | 105 | const privKeyObj = crypto.createPrivateKey( | |
@@ -113,7 +154,11 @@ if (hasOpenSSL(3, 5)) { | |||
| 113 | 154 | assert.throws(() => privKeyObj.export({ format: 'raw-private' }), | |
| 114 | 155 | { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
| 115 | 156 | ||
| 116 | - for (const format of ['raw-public', 'raw-private', 'raw-seed']) { | ||
| 157 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 158 | + key: Buffer.alloc(32), format: 'raw-public', asymmetricKeyType: 'dh', | ||
| 159 | + }), { code: 'ERR_INVALID_ARG_VALUE' }); | ||
| 160 | + | ||
| 161 | + for (const format of ['raw-private', 'raw-seed']) { | ||
| 117 | 162 | assert.throws(() => crypto.createPrivateKey({ | |
| 118 | 163 | key: Buffer.alloc(32), format, asymmetricKeyType: 'dh', | |
| 119 | 164 | }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
@@ -274,6 +319,12 @@ if (hasOpenSSL(3, 5)) { | |||
| 274 | 319 | fixtures.readKey('ec_p256_private.pem', 'ascii')); | |
| 275 | 320 | assert.throws(() => ecPriv.export({ format: 'raw-seed' }), | |
| 276 | 321 | { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
| 322 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 323 | + key: ecPriv.export({ format: 'raw-private' }), | ||
| 324 | + format: 'raw-seed', | ||
| 325 | + asymmetricKeyType: 'ec', | ||
| 326 | + namedCurve: 'P-256', | ||
| 327 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 277 | 328 | ||
| 278 | 329 | if (process.features.openssl_is_boringssl) { | |
| 279 | 330 | common.printSkipMessage('Skipping unsupported ed448/x448 test cases'); | |
@@ -285,13 +336,23 @@ if (hasOpenSSL(3, 5)) { | |||
| 285 | 336 | fixtures.readKey(`${type}_private.pem`, 'ascii')); | |
| 286 | 337 | assert.throws(() => priv.export({ format: 'raw-seed' }), | |
| 287 | 338 | { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
| 339 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 340 | + key: priv.export({ format: 'raw-private' }), | ||
| 341 | + format: 'raw-seed', | ||
| 342 | + asymmetricKeyType: type, | ||
| 343 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 288 | 344 | } | |
| 289 | 345 | ||
| 290 | 346 | if (hasOpenSSL(3, 5)) { | |
| 291 | 347 | const slhPriv = crypto.createPrivateKey( | |
| 292 | 348 | fixtures.readKey('slh_dsa_sha2_128f_private.pem', 'ascii')); | |
| 293 | 349 | assert.throws(() => slhPriv.export({ format: 'raw-seed' }), | |
| 294 | 350 | { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
| 351 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 352 | + key: slhPriv.export({ format: 'raw-private' }), | ||
| 353 | + format: 'raw-seed', | ||
| 354 | + asymmetricKeyType: 'slh-dsa-sha2-128f', | ||
| 355 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 295 | 356 | } | |
| 296 | 357 | } | |
| 297 | 358 | ||
@@ -302,6 +363,11 @@ if (hasOpenSSL(3, 5)) { | |||
| 302 | 363 | fixtures.readKey(`${type.replaceAll('-', '_')}_private.pem`, 'ascii')); | |
| 303 | 364 | assert.throws(() => priv.export({ format: 'raw-private' }), | |
| 304 | 365 | { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | |
| 366 | + assert.throws(() => crypto.createPrivateKey({ | ||
| 367 | + key: priv.export({ format: 'raw-seed' }), | ||
| 368 | + format: 'raw-private', | ||
| 369 | + asymmetricKeyType: type, | ||
| 370 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 305 | 371 | } | |
| 306 | 372 | } | |
| 307 | 373 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -91,6 +91,12 @@ for (const asymmetricKeyType of [ | |||
| 91 | 91 | key: rawPriv, format: 'raw-private', asymmetricKeyType, | |
| 92 | 92 | }); | |
| 93 | 93 | assert.strictEqual(importedPriv.equals(key), true); | |
| 94 | + assert.throws(() => createPrivateKey({ | ||
| 95 | + key: rawPriv, format: 'raw-seed', asymmetricKeyType, | ||
| 96 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 97 | + assert.throws(() => createPublicKey({ | ||
| 98 | + key: rawPriv, format: 'raw-seed', asymmetricKeyType, | ||
| 99 | + }), { code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' }); | ||
| 94 | 100 | } | |
| 95 | 101 | ||
| 96 | 102 | if (!hasOpenSSL(3, 5)) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments