Skip to content

Commit 5b592b7

Browse files
panvaaduh95
authored andcommitted
crypto: avoid EC raw export reconstruction
Read private scalars and matching uncompressed provider encodings directly. Assisted-by: GitHub Copilot Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65908 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent ee956ae commit 5b592b7

4 files changed

Lines changed: 95 additions & 52 deletions

File tree

‎benchmark/crypto/ec-raw-export.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const { generateKeyPairSync } = require('crypto');
5+
6+
const bench = common.createBenchmark(main, {
7+
namedCurve: ['prime256v1', 'secp384r1', 'secp521r1'],
8+
format: ['raw-private', 'raw-public'],
9+
type: ['uncompressed', 'compressed'],
10+
n: [10000],
11+
}, {
12+
combinationFilter: ({ format, type }) => format === 'raw-public' || type === 'uncompressed',
13+
});
14+
15+
function main({ namedCurve, format, type, n }) {
16+
const pair = generateKeyPairSync('ec', { namedCurve });
17+
const key = format === 'raw-private' ? pair.privateKey : pair.publicKey;
18+
const options = format === 'raw-public' ? { format, type } : { format };
19+
bench.start();
20+
for (let index = 0; index < n; index++)
21+
key.export(options);
22+
bench.end(n);
23+
}

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6916,6 +6916,51 @@ int Ec::getCurve() const {
69166916
return EC_GROUP_get_curve_name(getGroup());
69176917
}
69186918

6919+
DataPointer Ec::TryExportPublic(const EVPKeyPointer& key,
6920+
point_conversion_form_t form) {
6921+
if (form != POINT_CONVERSION_UNCOMPRESSED) return {};
6922+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
6923+
{
6924+
MarkPopErrorOnReturn pop_errors;
6925+
size_t length = 0;
6926+
if (EVP_PKEY_get_octet_string_param(
6927+
key.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &length) == 1) {
6928+
auto bytes = DataPointer::Alloc(length);
6929+
if (bytes && length != 0 &&
6930+
EVP_PKEY_get_octet_string_param(key.get(),
6931+
OSSL_PKEY_PARAM_PUB_KEY,
6932+
bytes.get<unsigned char>(),
6933+
length,
6934+
&length) == 1 &&
6935+
(bytes.get<unsigned char>()[0] & ~1) == form) {
6936+
return bytes.resize(length);
6937+
}
6938+
}
6939+
}
6940+
#endif
6941+
return {};
6942+
}
6943+
6944+
DataPointer Ec::ExportPrivate(const EVPKeyPointer& key) {
6945+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
6946+
{
6947+
MarkPopErrorOnReturn pop_errors;
6948+
BignumPointer priv;
6949+
BignumPointer order;
6950+
if (GetPKeyBnParam(key.get(), OSSL_PKEY_PARAM_PRIV_KEY, &priv) &&
6951+
GetPKeyBnParam(key.get(), OSSL_PKEY_PARAM_EC_ORDER, &order)) {
6952+
return priv.encodePadded(order.byteLength());
6953+
}
6954+
}
6955+
#endif
6956+
ECKeyPointer ec(key);
6957+
if (!ec || ec.getPrivateKey() == nullptr) return {};
6958+
auto order = BignumPointer::New();
6959+
if (!order || !EC_GROUP_get_order(ec.getGroup(), order.get(), nullptr))
6960+
return {};
6961+
return BignumPointer::EncodePadded(ec.getPrivateKey(), order.byteLength());
6962+
}
6963+
69196964
bool Ec::GetKeyComponents(const EVPKeyPointer& key,
69206965
BignumPointer* x,
69216966
BignumPointer* y,

‎deps/ncrypto/ncrypto.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ class Ec final {
813813

814814
static int GetCurveIdFromName(const char* name);
815815
static int GetCurveId(const EVPKeyPointer& key);
816+
static DataPointer TryExportPublic(const EVPKeyPointer& key,
817+
point_conversion_form_t form);
818+
static DataPointer ExportPrivate(const EVPKeyPointer& key);
816819
static bool GetKeyComponents(const EVPKeyPointer& key,
817820
BignumPointer* x,
818821
BignumPointer* y,

‎src/crypto/crypto_keys.cc‎

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -387,21 +387,24 @@ bool KeyObjectData::ToEncodedPublicKey(
387387
Mutex::ScopedLock lock(mutex());
388388
const auto& pkey = GetAsymmetricKey();
389389
if (pkey.id() == EVP_PKEY_EC) {
390+
auto form = static_cast<point_conversion_form_t>(config.ec_point_form);
391+
auto bytes = ncrypto::Ec::TryExportPublic(pkey, form);
392+
if (bytes)
393+
return Buffer::Copy(env, bytes.get<const char>(), bytes.size())
394+
.ToLocal(out);
390395
ECKeyPointer ec_key(pkey);
391396
if (!ec_key) {
392397
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
393398
return false;
394399
}
395-
// A provider-backed key need not expose its public point.
396400
if (ec_key.getPublicKey() == nullptr) {
397401
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
398402
"Failed to export EC public key");
399403
return false;
400404
}
401-
auto form = static_cast<point_conversion_form_t>(config.ec_point_form);
402-
const auto group = ec_key.getGroup();
403-
const auto point = ec_key.getPublicKey();
404-
return ECPointToBuffer(env, group, point, form).ToLocal(out);
405+
return ECPointToBuffer(
406+
env, ec_key.getGroup(), ec_key.getPublicKey(), form)
407+
.ToLocal(out);
405408
}
406409
const int id = pkey.id();
407410
bool is_raw_supported = id == EVP_PKEY_ED25519 || id == EVP_PKEY_ED448 ||
@@ -442,25 +445,7 @@ bool KeyObjectData::ToEncodedPrivateKey(
442445
Mutex::ScopedLock lock(mutex());
443446
const auto& pkey = GetAsymmetricKey();
444447
if (pkey.id() == EVP_PKEY_EC) {
445-
ECKeyPointer ec_key(pkey);
446-
if (!ec_key) {
447-
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
448-
return false;
449-
}
450-
const BIGNUM* private_key = ec_key.getPrivateKey();
451-
if (private_key == nullptr) {
452-
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
453-
"Failed to export EC private key");
454-
return false;
455-
}
456-
const auto group = ec_key.getGroup();
457-
auto order = BignumPointer::New();
458-
if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) {
459-
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
460-
"Failed to export EC private key");
461-
return false;
462-
}
463-
auto buf = BignumPointer::EncodePadded(private_key, order.byteLength());
448+
auto buf = ncrypto::Ec::ExportPrivate(pkey);
464449
if (!buf) {
465450
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
466451
"Failed to export EC private key");
@@ -1582,24 +1567,27 @@ void KeyObjectHandle::ExportECPublicRaw(
15821567
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
15831568
}
15841569

1570+
CHECK(args[0]->IsInt32());
1571+
auto form =
1572+
static_cast<point_conversion_form_t>(args[0].As<Int32>()->Value());
1573+
1574+
auto bytes = ncrypto::Ec::TryExportPublic(m_pkey, form);
1575+
if (bytes) {
1576+
args.GetReturnValue().Set(
1577+
Buffer::Copy(env, bytes.get<const char>(), bytes.size())
1578+
.FromMaybe(Local<Value>()));
1579+
return;
1580+
}
15851581
ECKeyPointer ec_key(m_pkey);
15861582
if (!ec_key) return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
1587-
// A provider-backed key need not expose its public point.
15881583
if (ec_key.getPublicKey() == nullptr) {
15891584
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
15901585
"Failed to export EC public key");
15911586
}
1592-
1593-
CHECK(args[0]->IsInt32());
1594-
auto form =
1595-
static_cast<point_conversion_form_t>(args[0].As<Int32>()->Value());
1596-
1597-
const auto group = ec_key.getGroup();
1598-
const auto point = ec_key.getPublicKey();
1599-
16001587
Local<Object> buf;
1601-
if (!ECPointToBuffer(env, group, point, form).ToLocal(&buf)) return;
1602-
1588+
if (!ECPointToBuffer(env, ec_key.getGroup(), ec_key.getPublicKey(), form)
1589+
.ToLocal(&buf))
1590+
return;
16031591
args.GetReturnValue().Set(buf);
16041592
}
16051593

@@ -1618,23 +1606,7 @@ void KeyObjectHandle::ExportECPrivateRaw(
16181606
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
16191607
}
16201608

1621-
ECKeyPointer ec_key(m_pkey);
1622-
if (!ec_key) return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
1623-
1624-
const BIGNUM* private_key = ec_key.getPrivateKey();
1625-
if (private_key == nullptr) {
1626-
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
1627-
"Failed to export EC private key");
1628-
}
1629-
1630-
const auto group = ec_key.getGroup();
1631-
auto order = BignumPointer::New();
1632-
if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) {
1633-
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
1634-
"Failed to export EC private key");
1635-
}
1636-
1637-
auto buf = BignumPointer::EncodePadded(private_key, order.byteLength());
1609+
auto buf = ncrypto::Ec::ExportPrivate(m_pkey);
16381610
if (!buf) {
16391611
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
16401612
"Failed to export EC private key");

0 commit comments

Comments
 (0)