Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 56e2505

Browse filesBrowse files
panvasxa
authored andcommitted
crypto: wire ML-DSA and ML-KEM for use when using BoringSSL
Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #63255 Backport-PR-URL: #63563 Refs: electron/electron#36256 Refs: electron/electron#41720 Refs: electron/electron#51127 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 3bac77f commit 56e2505
Copy full SHA for 56e2505

37 files changed

+825-506Lines changed: 825 additions & 506 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎benchmark/crypto/create-keyobject.js‎

Copy file name to clipboardExpand all lines: benchmark/crypto/create-keyobject.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const keyFixtures = {
2626

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

3133
const bench = common.createBenchmark(main, {
Collapse file

‎benchmark/crypto/kem.js‎

Copy file name to clipboardExpand all lines: benchmark/crypto/kem.js
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ if (hasOpenSSL(3, 5)) {
2424
keyFixtures['ml-kem-512'] = readKeyPair('ml_kem_512_public', 'ml_kem_512_private');
2525
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private');
2626
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private');
27+
} else if (process.features.openssl_is_boringssl) {
28+
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private_seed_only');
29+
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private_seed_only');
2730
}
2831
if (hasOpenSSL(3, 2)) {
2932
keyFixtures['p-256'] = readKeyPair('ec_p256_public', 'ec_p256_private');
Collapse file

‎benchmark/crypto/oneshot-sign.js‎

Copy file name to clipboardExpand all lines: benchmark/crypto/oneshot-sign.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const keyFixtures = {
1919

2020
if (hasOpenSSL(3, 5)) {
2121
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private');
22+
} else if (process.features.openssl_is_boringssl) {
23+
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private_seed_only');
2224
}
2325

2426
const data = crypto.randomBytes(256);
Collapse file

‎benchmark/crypto/oneshot-verify.js‎

Copy file name to clipboardExpand all lines: benchmark/crypto/oneshot-verify.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const keyFixtures = {
2626

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

3133
const data = crypto.randomBytes(256);
Collapse file

‎deps/ncrypto/ncrypto.cc‎

Copy file name to clipboardExpand all lines: deps/ncrypto/ncrypto.cc
+116-39Lines changed: 116 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ constexpr static PQCMapping pqc_mappings[] = {
3434
{"ML-DSA-44", EVP_PKEY_ML_DSA_44},
3535
{"ML-DSA-65", EVP_PKEY_ML_DSA_65},
3636
{"ML-DSA-87", EVP_PKEY_ML_DSA_87},
37-
{"ML-KEM-512", EVP_PKEY_ML_KEM_512},
3837
{"ML-KEM-768", EVP_PKEY_ML_KEM_768},
3938
{"ML-KEM-1024", EVP_PKEY_ML_KEM_1024},
39+
40+
#if OPENSSL_WITH_PQC_ML_KEM_512
41+
{"ML-KEM-512", EVP_PKEY_ML_KEM_512},
42+
#endif
43+
#if OPENSSL_WITH_PQC_SLH_DSA
4044
{"SLH-DSA-SHA2-128f", EVP_PKEY_SLH_DSA_SHA2_128F},
4145
{"SLH-DSA-SHA2-128s", EVP_PKEY_SLH_DSA_SHA2_128S},
4246
{"SLH-DSA-SHA2-192f", EVP_PKEY_SLH_DSA_SHA2_192F},
@@ -49,6 +53,7 @@ constexpr static PQCMapping pqc_mappings[] = {
4953
{"SLH-DSA-SHAKE-192s", EVP_PKEY_SLH_DSA_SHAKE_192S},
5054
{"SLH-DSA-SHAKE-256f", EVP_PKEY_SLH_DSA_SHAKE_256F},
5155
{"SLH-DSA-SHAKE-256s", EVP_PKEY_SLH_DSA_SHAKE_256S},
56+
#endif
5257
};
5358

5459
#endif
@@ -2095,27 +2100,99 @@ EVPKeyPointer EVPKeyPointer::NewRawPrivate(
20952100
}
20962101

20972102
#if OPENSSL_WITH_PQC
2098-
EVPKeyPointer EVPKeyPointer::NewRawSeed(
2099-
int id, const Buffer<const unsigned char>& data) {
2100-
if (id == 0) return {};
2103+
namespace {
2104+
constexpr size_t kPqcMlDsaSeedSize = 32;
2105+
constexpr size_t kPqcMlKemSeedSize = 64;
2106+
2107+
size_t GetPqcSeedSize(int id) {
2108+
switch (id) {
2109+
case EVP_PKEY_ML_DSA_44:
2110+
case EVP_PKEY_ML_DSA_65:
2111+
case EVP_PKEY_ML_DSA_87:
2112+
return kPqcMlDsaSeedSize;
2113+
#if OPENSSL_WITH_PQC_ML_KEM_512
2114+
case EVP_PKEY_ML_KEM_512:
2115+
#endif
2116+
case EVP_PKEY_ML_KEM_768:
2117+
case EVP_PKEY_ML_KEM_1024:
2118+
return kPqcMlKemSeedSize;
2119+
default:
2120+
unreachable();
2121+
}
2122+
}
2123+
2124+
#if OPENSSL_WITH_BORINGSSL_PQC
2125+
const EVP_PKEY_ALG* GetPqcSeedAlg(int id) {
2126+
switch (id) {
2127+
case EVP_PKEY_ML_DSA_44:
2128+
return EVP_pkey_ml_dsa_44();
2129+
case EVP_PKEY_ML_DSA_65:
2130+
return EVP_pkey_ml_dsa_65();
2131+
case EVP_PKEY_ML_DSA_87:
2132+
return EVP_pkey_ml_dsa_87();
2133+
case EVP_PKEY_ML_KEM_768:
2134+
return EVP_pkey_ml_kem_768();
2135+
case EVP_PKEY_ML_KEM_1024:
2136+
return EVP_pkey_ml_kem_1024();
2137+
default:
2138+
unreachable();
2139+
}
2140+
}
2141+
#else
2142+
const char* GetPqcSeedParamName(int id) {
2143+
switch (id) {
2144+
case EVP_PKEY_ML_DSA_44:
2145+
case EVP_PKEY_ML_DSA_65:
2146+
case EVP_PKEY_ML_DSA_87:
2147+
return OSSL_PKEY_PARAM_ML_DSA_SEED;
2148+
case EVP_PKEY_ML_KEM_512:
2149+
case EVP_PKEY_ML_KEM_768:
2150+
case EVP_PKEY_ML_KEM_1024:
2151+
return OSSL_PKEY_PARAM_ML_KEM_SEED;
2152+
default:
2153+
unreachable();
2154+
}
2155+
}
2156+
#endif
21012157

2158+
EVPKeyPointer NewPqcKeyFromSeed(int id,
2159+
const Buffer<const unsigned char>& data) {
2160+
#if OPENSSL_WITH_BORINGSSL_PQC
2161+
return EVPKeyPointer(
2162+
EVP_PKEY_from_private_seed(GetPqcSeedAlg(id), data.data, data.len));
2163+
#else
21022164
OSSL_PARAM params[] = {
2103-
OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED,
2165+
OSSL_PARAM_construct_octet_string(GetPqcSeedParamName(id),
21042166
const_cast<unsigned char*>(data.data),
21052167
data.len),
21062168
OSSL_PARAM_END};
21072169

2108-
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(id, nullptr);
2109-
if (ctx == nullptr) return {};
2170+
auto ctx = EVPKeyCtxPointer::NewFromID(id);
2171+
if (!ctx) return {};
21102172

21112173
EVP_PKEY* pkey = nullptr;
2112-
if (ctx == nullptr || EVP_PKEY_fromdata_init(ctx) <= 0 ||
2113-
EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
2114-
EVP_PKEY_CTX_free(ctx);
2174+
if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
2175+
EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
21152176
return {};
21162177
}
2117-
21182178
return EVPKeyPointer(pkey);
2179+
#endif
2180+
}
2181+
2182+
bool GetPqcSeed(EVP_PKEY* pkey, int id, const Buffer<unsigned char>& out) {
2183+
size_t len = out.len;
2184+
#if OPENSSL_WITH_BORINGSSL_PQC
2185+
return EVP_PKEY_get_private_seed(pkey, out.data, &len) == 1;
2186+
#else
2187+
return EVP_PKEY_get_octet_string_param(
2188+
pkey, GetPqcSeedParamName(id), out.data, out.len, &len) == 1;
2189+
#endif
2190+
}
2191+
} // namespace
2192+
2193+
EVPKeyPointer EVPKeyPointer::NewRawSeed(
2194+
int id, const Buffer<const unsigned char>& data) {
2195+
return NewPqcKeyFromSeed(id, data);
21192196
}
21202197
#endif
21212198

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

2246-
// Determine seed length and parameter name based on key type
2247-
size_t seed_len;
2248-
const char* param_name;
2249-
2250-
switch (id()) {
2251-
case EVP_PKEY_ML_DSA_44:
2252-
case EVP_PKEY_ML_DSA_65:
2253-
case EVP_PKEY_ML_DSA_87:
2254-
seed_len = 32; // ML-DSA uses 32-byte seeds
2255-
param_name = OSSL_PKEY_PARAM_ML_DSA_SEED;
2256-
break;
2257-
case EVP_PKEY_ML_KEM_512:
2258-
case EVP_PKEY_ML_KEM_768:
2259-
case EVP_PKEY_ML_KEM_1024:
2260-
seed_len = 64; // ML-KEM uses 64-byte seeds
2261-
param_name = OSSL_PKEY_PARAM_ML_KEM_SEED;
2262-
break;
2263-
default:
2264-
unreachable();
2265-
}
2323+
const size_t seed_len = GetPqcSeedSize(id());
22662324

22672325
if (auto data = DataPointer::Alloc(seed_len)) {
22682326
const Buffer<unsigned char> buf = data;
2269-
size_t len = data.size();
2270-
2271-
if (EVP_PKEY_get_octet_string_param(
2272-
get(), param_name, buf.data, len, &seed_len) != 1)
2273-
return {};
2327+
if (!GetPqcSeed(get(), id(), buf)) return {};
22742328
return data;
22752329
}
22762330
return {};
@@ -2312,6 +2366,7 @@ EVPKeyPointer::operator const EC_KEY*() const {
23122366
}
23132367

23142368
namespace {
2369+
23152370
EVPKeyPointer::ParseKeyResult TryParsePublicKeyInner(const BIOPointer& bp,
23162371
const char* name,
23172372
auto&& parse) {
@@ -2739,6 +2794,7 @@ bool EVPKeyPointer::isOneShotVariant() const {
27392794
case EVP_PKEY_ML_DSA_44:
27402795
case EVP_PKEY_ML_DSA_65:
27412796
case EVP_PKEY_ML_DSA_87:
2797+
#if OPENSSL_WITH_PQC_SLH_DSA
27422798
case EVP_PKEY_SLH_DSA_SHA2_128F:
27432799
case EVP_PKEY_SLH_DSA_SHA2_128S:
27442800
case EVP_PKEY_SLH_DSA_SHA2_192F:
@@ -2751,6 +2807,7 @@ bool EVPKeyPointer::isOneShotVariant() const {
27512807
case EVP_PKEY_SLH_DSA_SHAKE_192S:
27522808
case EVP_PKEY_SLH_DSA_SHAKE_256F:
27532809
case EVP_PKEY_SLH_DSA_SHAKE_256S:
2810+
#endif
27542811
#endif
27552812
return true;
27562813
default:
@@ -4401,7 +4458,17 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::signInitWithContext(
44014458
const EVPKeyPointer& key,
44024459
const Digest& digest,
44034460
const Buffer<const unsigned char>& context_string) {
4404-
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
4461+
#ifdef OPENSSL_IS_BORINGSSL
4462+
EVP_PKEY_CTX* ctx = nullptr;
4463+
if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) {
4464+
return std::nullopt;
4465+
}
4466+
if (EVP_PKEY_CTX_set1_signature_context_string(
4467+
ctx, context_string.data, context_string.len) <= 0) {
4468+
return std::nullopt;
4469+
}
4470+
return ctx;
4471+
#elif defined(OSSL_SIGNATURE_PARAM_CONTEXT_STRING)
44054472
EVP_PKEY_CTX* ctx = nullptr;
44064473

44074474
#ifdef OSSL_SIGNATURE_PARAM_INSTANCE
@@ -4446,7 +4513,17 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInitWithContext(
44464513
const EVPKeyPointer& key,
44474514
const Digest& digest,
44484515
const Buffer<const unsigned char>& context_string) {
4449-
#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
4516+
#ifdef OPENSSL_IS_BORINGSSL
4517+
EVP_PKEY_CTX* ctx = nullptr;
4518+
if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) {
4519+
return std::nullopt;
4520+
}
4521+
if (EVP_PKEY_CTX_set1_signature_context_string(
4522+
ctx, context_string.data, context_string.len) <= 0) {
4523+
return std::nullopt;
4524+
}
4525+
return ctx;
4526+
#elif defined(OSSL_SIGNATURE_PARAM_CONTEXT_STRING)
44504527
EVP_PKEY_CTX* ctx = nullptr;
44514528

44524529
#ifdef OSSL_SIGNATURE_PARAM_INSTANCE
Collapse file

‎deps/ncrypto/ncrypto.h‎

Copy file name to clipboardExpand all lines: deps/ncrypto/ncrypto.h
+24-8Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
#define OPENSSL_WITH_ARGON2 0
5959
#endif
6060

61-
#if OPENSSL_VERSION_PREREQ(3, 0)
61+
#if OPENSSL_VERSION_PREREQ(3, 0) || defined(OPENSSL_IS_BORINGSSL)
6262
#define OPENSSL_WITH_KEM 1
6363
#else
6464
#define OPENSSL_WITH_KEM 0
@@ -70,7 +70,7 @@
7070
#define OPENSSL_WITH_KMAC 0
7171
#endif
7272

73-
#if OPENSSL_VERSION_PREREQ(3, 2)
73+
#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_PREREQ(3, 2)
7474
#define OPENSSL_WITH_SIGNATURE_CONTEXT_STRING 1
7575
#else
7676
#define OPENSSL_WITH_SIGNATURE_CONTEXT_STRING 0
@@ -82,24 +82,40 @@
8282
#define OPENSSL_WITH_OPENSSL_DHKEM 0
8383
#endif
8484

85-
#if OPENSSL_WITH_KEM && !OPENSSL_VERSION_PREREQ(3, 5)
85+
#if OPENSSL_WITH_KEM && !defined(OPENSSL_IS_BORINGSSL) && \
86+
!OPENSSL_VERSION_PREREQ(3, 5)
8687
#define OPENSSL_WITH_KEM_OPERATION_PARAM 1
8788
#else
8889
#define OPENSSL_WITH_KEM_OPERATION_PARAM 0
8990
#endif
9091

91-
// Define OPENSSL_WITH_PQC for post-quantum cryptography support.
92-
#if OPENSSL_VERSION_PREREQ(3, 5)
93-
#define OPENSSL_WITH_PQC 1
92+
// Post-quantum cryptography support. Keep these explicit so code can
93+
// distinguish provider API shape from the available algorithm set.
94+
#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_PREREQ(3, 5)
95+
#define OPENSSL_WITH_OPENSSL_PQC 1
9496
#else
95-
#define OPENSSL_WITH_PQC 0
97+
#define OPENSSL_WITH_OPENSSL_PQC 0
9698
#endif
9799

98-
#if OPENSSL_WITH_PQC
100+
#ifdef OPENSSL_IS_BORINGSSL
101+
#define OPENSSL_WITH_BORINGSSL_PQC 1
102+
#else
103+
#define OPENSSL_WITH_BORINGSSL_PQC 0
104+
#endif
105+
106+
#define OPENSSL_WITH_PQC \
107+
(OPENSSL_WITH_OPENSSL_PQC || OPENSSL_WITH_BORINGSSL_PQC)
108+
#define OPENSSL_WITH_PQC_ML_KEM_512 OPENSSL_WITH_OPENSSL_PQC
109+
#define OPENSSL_WITH_PQC_SLH_DSA OPENSSL_WITH_OPENSSL_PQC
110+
111+
#if OPENSSL_WITH_OPENSSL_PQC
99112
#define EVP_PKEY_ML_KEM_512 NID_ML_KEM_512
100113
#define EVP_PKEY_ML_KEM_768 NID_ML_KEM_768
101114
#define EVP_PKEY_ML_KEM_1024 NID_ML_KEM_1024
102115
#include <openssl/core_names.h>
116+
#elif OPENSSL_WITH_BORINGSSL_PQC
117+
#define EVP_PKEY_ML_KEM_768 NID_ML_KEM_768
118+
#define EVP_PKEY_ML_KEM_1024 NID_ML_KEM_1024
103119
#endif
104120

105121
#if OPENSSL_VERSION_PREREQ(3, 0)
Collapse file

‎lib/internal/crypto/webidl.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/webidl.js
+10-6Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -763,14 +763,18 @@ converters.ContextParams = createDictionaryConverter(
763763
key: 'context',
764764
converter: converters.BufferSource,
765765
validator(V, dict) {
766-
let { 0: major, 1: minor } = process.versions.openssl.split('.');
767-
major = NumberParseInt(major, 10);
768-
minor = NumberParseInt(minor, 10);
769-
if (major > 3 || (major === 3 && minor >= 2)) {
766+
if (process.features.openssl_is_boringssl) {
770767
this.validator = undefined;
771768
} else {
772-
this.validator = validateZeroLength('ContextParams.context');
773-
this.validator(V, dict);
769+
let { 0: major, 1: minor } = process.versions.openssl.split('.');
770+
major = NumberParseInt(major, 10);
771+
minor = NumberParseInt(minor, 10);
772+
if (major > 3 || (major === 3 && minor >= 2)) {
773+
this.validator = undefined;
774+
} else {
775+
this.validator = validateZeroLength('ContextParams.context');
776+
this.validator(V, dict);
777+
}
774778
}
775779
},
776780
},

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.