Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benchmark/crypto/create-keyobject.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private_seed_only');
}

const bench = common.createBenchmark(main, {
Expand Down
3 changes: 3 additions & 0 deletions benchmark/crypto/kem.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ if (hasOpenSSL(3, 5)) {
keyFixtures['ml-kem-512'] = readKeyPair('ml_kem_512_public', 'ml_kem_512_private');
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private');
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private_seed_only');
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private_seed_only');
}
if (hasOpenSSL(3, 2)) {
keyFixtures['p-256'] = readKeyPair('ec_p256_public', 'ec_p256_private');
Expand Down
2 changes: 2 additions & 0 deletions benchmark/crypto/oneshot-sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private_seed_only');
}

const data = crypto.randomBytes(256);
Expand Down
2 changes: 2 additions & 0 deletions benchmark/crypto/oneshot-verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private_seed_only');
}

const data = crypto.randomBytes(256);
Expand Down
177 changes: 126 additions & 51 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <openssl/core_names.h>
#include <openssl/params.h>
#include <openssl/provider.h>
#if OPENSSL_VERSION_NUMBER >= 0x30200000L
#if OPENSSL_WITH_ARGON2
#include <openssl/thread.h>
#endif
#endif
Expand All @@ -34,9 +34,13 @@ constexpr static PQCMapping pqc_mappings[] = {
{"ML-DSA-44", EVP_PKEY_ML_DSA_44},
{"ML-DSA-65", EVP_PKEY_ML_DSA_65},
{"ML-DSA-87", EVP_PKEY_ML_DSA_87},
{"ML-KEM-512", EVP_PKEY_ML_KEM_512},
{"ML-KEM-768", EVP_PKEY_ML_KEM_768},
{"ML-KEM-1024", EVP_PKEY_ML_KEM_1024},

#if OPENSSL_WITH_PQC_ML_KEM_512
{"ML-KEM-512", EVP_PKEY_ML_KEM_512},
#endif
#if OPENSSL_WITH_PQC_SLH_DSA
{"SLH-DSA-SHA2-128f", EVP_PKEY_SLH_DSA_SHA2_128F},
{"SLH-DSA-SHA2-128s", EVP_PKEY_SLH_DSA_SHA2_128S},
{"SLH-DSA-SHA2-192f", EVP_PKEY_SLH_DSA_SHA2_192F},
Expand All @@ -49,6 +53,7 @@ constexpr static PQCMapping pqc_mappings[] = {
{"SLH-DSA-SHAKE-192s", EVP_PKEY_SLH_DSA_SHAKE_192S},
{"SLH-DSA-SHAKE-256f", EVP_PKEY_SLH_DSA_SHAKE_256F},
{"SLH-DSA-SHAKE-256s", EVP_PKEY_SLH_DSA_SHAKE_256S},
#endif
};

#endif
Expand Down Expand Up @@ -1955,8 +1960,7 @@ DataPointer pbkdf2(const Digest& md,
return {};
}

#if OPENSSL_VERSION_NUMBER >= 0x30200000L
#ifndef OPENSSL_NO_ARGON2
#if OPENSSL_WITH_ARGON2
DataPointer argon2(const Buffer<const char>& pass,
const Buffer<const unsigned char>& salt,
uint32_t lanes,
Expand Down Expand Up @@ -2049,7 +2053,6 @@ DataPointer argon2(const Buffer<const char>& pass,
return {};
}
#endif
#endif

// ============================================================================

Expand Down Expand Up @@ -2097,27 +2100,99 @@ EVPKeyPointer EVPKeyPointer::NewRawPrivate(
}

#if OPENSSL_WITH_PQC
EVPKeyPointer EVPKeyPointer::NewRawSeed(
int id, const Buffer<const unsigned char>& data) {
if (id == 0) return {};
namespace {
constexpr size_t kPqcMlDsaSeedSize = 32;
constexpr size_t kPqcMlKemSeedSize = 64;

size_t GetPqcSeedSize(int id) {
switch (id) {
case EVP_PKEY_ML_DSA_44:
case EVP_PKEY_ML_DSA_65:
case EVP_PKEY_ML_DSA_87:
return kPqcMlDsaSeedSize;
#if OPENSSL_WITH_PQC_ML_KEM_512
case EVP_PKEY_ML_KEM_512:
#endif
case EVP_PKEY_ML_KEM_768:
case EVP_PKEY_ML_KEM_1024:
return kPqcMlKemSeedSize;
default:
unreachable();
}
}

#if OPENSSL_WITH_BORINGSSL_PQC
const EVP_PKEY_ALG* GetPqcSeedAlg(int id) {
switch (id) {
case EVP_PKEY_ML_DSA_44:
return EVP_pkey_ml_dsa_44();
case EVP_PKEY_ML_DSA_65:
return EVP_pkey_ml_dsa_65();
case EVP_PKEY_ML_DSA_87:
return EVP_pkey_ml_dsa_87();
case EVP_PKEY_ML_KEM_768:
return EVP_pkey_ml_kem_768();
case EVP_PKEY_ML_KEM_1024:
return EVP_pkey_ml_kem_1024();
default:
unreachable();
}
}
#else
const char* GetPqcSeedParamName(int id) {
switch (id) {
case EVP_PKEY_ML_DSA_44:
case EVP_PKEY_ML_DSA_65:
case EVP_PKEY_ML_DSA_87:
return OSSL_PKEY_PARAM_ML_DSA_SEED;
case EVP_PKEY_ML_KEM_512:
case EVP_PKEY_ML_KEM_768:
case EVP_PKEY_ML_KEM_1024:
return OSSL_PKEY_PARAM_ML_KEM_SEED;
default:
unreachable();
}
}
#endif

EVPKeyPointer NewPqcKeyFromSeed(int id,
const Buffer<const unsigned char>& data) {
#if OPENSSL_WITH_BORINGSSL_PQC
return EVPKeyPointer(
EVP_PKEY_from_private_seed(GetPqcSeedAlg(id), data.data, data.len));
#else
OSSL_PARAM params[] = {
OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED,
OSSL_PARAM_construct_octet_string(GetPqcSeedParamName(id),
const_cast<unsigned char*>(data.data),
data.len),
OSSL_PARAM_END};

EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(id, nullptr);
if (ctx == nullptr) return {};
auto ctx = EVPKeyCtxPointer::NewFromID(id);
if (!ctx) return {};

EVP_PKEY* pkey = nullptr;
if (ctx == nullptr || EVP_PKEY_fromdata_init(ctx) <= 0 ||
EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
EVP_PKEY_CTX_free(ctx);
if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
return {};
}

return EVPKeyPointer(pkey);
#endif
}

bool GetPqcSeed(EVP_PKEY* pkey, int id, const Buffer<unsigned char>& out) {
size_t len = out.len;
#if OPENSSL_WITH_BORINGSSL_PQC
return EVP_PKEY_get_private_seed(pkey, out.data, &len) == 1;
#else
return EVP_PKEY_get_octet_string_param(
pkey, GetPqcSeedParamName(id), out.data, out.len, &len) == 1;
#endif
}
} // namespace

EVPKeyPointer EVPKeyPointer::NewRawSeed(
int id, const Buffer<const unsigned char>& data) {
return NewPqcKeyFromSeed(id, data);
}
#endif

Expand Down Expand Up @@ -2167,7 +2242,7 @@ EVP_PKEY* EVPKeyPointer::release() {
int EVPKeyPointer::id(const EVP_PKEY* key) {
if (key == nullptr) return 0;
int type = EVP_PKEY_id(key);
#if OPENSSL_WITH_PQC
#if OPENSSL_WITH_OPENSSL_PQC
// EVP_PKEY_id returns -1 when EVP_PKEY_* is only implemented in a provider
// which is the case for all post-quantum NIST algorithms
// one suggested way would be to use a chain of `EVP_PKEY_is_a`
Expand Down Expand Up @@ -2245,34 +2320,11 @@ DataPointer EVPKeyPointer::rawPublicKey() const {
DataPointer EVPKeyPointer::rawSeed() const {
if (!pkey_) return {};

// Determine seed length and parameter name based on key type
size_t seed_len;
const char* param_name;

switch (id()) {
case EVP_PKEY_ML_DSA_44:
case EVP_PKEY_ML_DSA_65:
case EVP_PKEY_ML_DSA_87:
seed_len = 32; // ML-DSA uses 32-byte seeds
param_name = OSSL_PKEY_PARAM_ML_DSA_SEED;
break;
case EVP_PKEY_ML_KEM_512:
case EVP_PKEY_ML_KEM_768:
case EVP_PKEY_ML_KEM_1024:
seed_len = 64; // ML-KEM uses 64-byte seeds
param_name = OSSL_PKEY_PARAM_ML_KEM_SEED;
break;
default:
unreachable();
}
const size_t seed_len = GetPqcSeedSize(id());

if (auto data = DataPointer::Alloc(seed_len)) {
const Buffer<unsigned char> buf = data;
size_t len = data.size();

if (EVP_PKEY_get_octet_string_param(
get(), param_name, buf.data, len, &seed_len) != 1)
return {};
if (!GetPqcSeed(get(), id(), buf)) return {};
return data;
}
return {};
Expand Down Expand Up @@ -2314,6 +2366,7 @@ EVPKeyPointer::operator const EC_KEY*() const {
}

namespace {

EVPKeyPointer::ParseKeyResult TryParsePublicKeyInner(const BIOPointer& bp,
const char* name,
auto&& parse) {
Expand Down Expand Up @@ -2741,6 +2794,7 @@ bool EVPKeyPointer::isOneShotVariant() const {
case EVP_PKEY_ML_DSA_44:
case EVP_PKEY_ML_DSA_65:
case EVP_PKEY_ML_DSA_87:
#if OPENSSL_WITH_PQC_SLH_DSA
case EVP_PKEY_SLH_DSA_SHA2_128F:
case EVP_PKEY_SLH_DSA_SHA2_128S:
case EVP_PKEY_SLH_DSA_SHA2_192F:
Expand All @@ -2753,6 +2807,7 @@ bool EVPKeyPointer::isOneShotVariant() const {
case EVP_PKEY_SLH_DSA_SHAKE_192S:
case EVP_PKEY_SLH_DSA_SHAKE_256F:
case EVP_PKEY_SLH_DSA_SHAKE_256S:
#endif
#endif
return true;
default:
Expand Down Expand Up @@ -4403,7 +4458,17 @@ 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
#ifdef OPENSSL_IS_BORINGSSL
EVP_PKEY_CTX* ctx = nullptr;
if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) {
return std::nullopt;
}
if (EVP_PKEY_CTX_set1_signature_context_string(
ctx, context_string.data, context_string.len) <= 0) {
return std::nullopt;
}
return ctx;
#elif defined(OSSL_SIGNATURE_PARAM_CONTEXT_STRING)
EVP_PKEY_CTX* ctx = nullptr;

#ifdef OSSL_SIGNATURE_PARAM_INSTANCE
Expand Down Expand Up @@ -4448,7 +4513,17 @@ 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
#ifdef OPENSSL_IS_BORINGSSL
EVP_PKEY_CTX* ctx = nullptr;
if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) {
return std::nullopt;
}
if (EVP_PKEY_CTX_set1_signature_context_string(
ctx, context_string.data, context_string.len) <= 0) {
return std::nullopt;
}
return ctx;
#elif defined(OSSL_SIGNATURE_PARAM_CONTEXT_STRING)
EVP_PKEY_CTX* ctx = nullptr;

#ifdef OSSL_SIGNATURE_PARAM_INSTANCE
Expand Down Expand Up @@ -4614,7 +4689,7 @@ HMACCtxPointer HMACCtxPointer::New() {
return HMACCtxPointer(HMAC_CTX_new());
}

#if OPENSSL_VERSION_MAJOR >= 3
#if OPENSSL_WITH_KMAC
EVPMacPointer::EVPMacPointer(EVP_MAC* mac) : mac_(mac) {}

EVPMacPointer::EVPMacPointer(EVPMacPointer&& other) noexcept
Expand Down Expand Up @@ -4702,7 +4777,7 @@ EVPMacCtxPointer EVPMacCtxPointer::New(EVP_MAC* mac) {
if (!mac) return EVPMacCtxPointer();
return EVPMacCtxPointer(EVP_MAC_CTX_new(mac));
}
#endif // OPENSSL_VERSION_MAJOR >= 3
#endif // OPENSSL_WITH_KMAC

DataPointer hashDigest(const Buffer<const unsigned char>& buf,
const EVP_MD* md) {
Expand Down Expand Up @@ -4849,16 +4924,16 @@ const Digest Digest::FromName(const char* name) {

// ============================================================================
// KEM Implementation
#if OPENSSL_VERSION_MAJOR >= 3
#if !OPENSSL_VERSION_PREREQ(3, 5)
#if OPENSSL_WITH_KEM
#if OPENSSL_WITH_KEM_OPERATION_PARAM
bool KEM::SetOperationParameter(EVP_PKEY_CTX* ctx, const EVPKeyPointer& key) {
const char* operation = nullptr;

switch (EVP_PKEY_id(key.get())) {
case EVP_PKEY_RSA:
operation = OSSL_KEM_PARAM_OPERATION_RSASVE;
break;
#if OPENSSL_VERSION_PREREQ(3, 2)
#if OPENSSL_WITH_OPENSSL_DHKEM
case EVP_PKEY_EC:
case EVP_PKEY_X25519:
case EVP_PKEY_X448:
Expand Down Expand Up @@ -4895,7 +4970,7 @@ std::optional<KEM::EncapsulateResult> KEM::Encapsulate(
return std::nullopt;
}

#if !OPENSSL_VERSION_PREREQ(3, 5)
#if OPENSSL_WITH_KEM_OPERATION_PARAM
if (!SetOperationParameter(ctx.get(), public_key)) {
return std::nullopt;
}
Expand Down Expand Up @@ -4936,7 +5011,7 @@ DataPointer KEM::Decapsulate(const EVPKeyPointer& private_key,
return {};
}

#if !OPENSSL_VERSION_PREREQ(3, 5)
#if OPENSSL_WITH_KEM_OPERATION_PARAM
if (!SetOperationParameter(ctx.get(), private_key)) {
return {};
}
Expand Down Expand Up @@ -4966,6 +5041,6 @@ DataPointer KEM::Decapsulate(const EVPKeyPointer& private_key,
return shared_key;
}

#endif // OPENSSL_VERSION_MAJOR >= 3
#endif // OPENSSL_WITH_KEM

} // namespace ncrypto
Loading
Loading