GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — tls: add certificateCompression option · nodejs/node@ee29465

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • deps/openssl
    • openssl.gyp
  • doc/api
    • tls.md
  • lib
    • internal/tls
      • secure-context.js
      • wrap.js
    • tls.js
  • src/crypto
    • crypto_context.cc
    • crypto_context.h
    • crypto_tls.cc
    • crypto_util.h
  • test/parallel
    • test-tls-certificate-compression.js
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎deps/openssl/openssl.gyp‎

Copy file name to clipboardExpand all lines: deps/openssl/openssl.gyp
+9-5Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@
1919
{
2020
'target_name': 'openssl',
2121
'type': '<(library)',
22-
'dependencies': [
23-
'../zlib/zlib.gyp:zlib',
24-
'../brotli/brotli.gyp:brotli',
25-
'../zstd/zstd.gyp:zstd',
26-
],
2722
'includes': ['./openssl_common.gypi'],
2823
'defines': [
2924
# Compile out hardware engines. Most are stubs that dynamically load
@@ -54,6 +49,15 @@
5449
'MODULESDIR="<(modules_dir)"',
5550
]
5651
}],
52+
['node_shared_zlib=="false"', {
53+
'dependencies': [ '../zlib/zlib.gyp:zlib' ],
54+
}],
55+
['node_shared_brotli=="false"', {
56+
'dependencies': [ '../brotli/brotli.gyp:brotli' ],
57+
}],
58+
['node_shared_zstd=="false"', {
59+
'dependencies': [ '../zstd/zstd.gyp:zstd' ],
60+
}],
5761
],
5862
'direct_dependent_settings': {
5963
'include_dirs': [ 'openssl/include', 'openssl/crypto/include']
Collapse file

‎doc/api/tls.md‎

Copy file name to clipboardExpand all lines: doc/api/tls.md
+29Lines changed: 29 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,9 @@ argument.
18951895
<!-- YAML
18961896
added: v0.11.13
18971897
changes:
1898+
- version: REPLACEME
1899+
pr-url: https://github.com/nodejs/node/pull/62217
1900+
description: The `certificateCompression` option has been added.
18981901
- version:
18991902
- v22.9.0
19001903
- v20.18.0
@@ -1988,6 +1991,12 @@ changes:
19881991
the same order as their private keys in `key`. If the intermediate
19891992
certificates are not provided, the peer will not be able to validate the
19901993
certificate, and the handshake will fail.
1994+
* `certificateCompression` {string\[]} An array of supported certificate
1995+
compression algorithm names, in preference order. Supported values are
1996+
`'zlib'`, `'brotli'`, and `'zstd'`. When set, enables TLS certificate
1997+
compression ([RFC 8879][]) which compresses certificates during the TLS
1998+
handshake, reducing handshake size. Only effective with TLSv1.3.
1999+
**Default:** `[]` (disabled).
19912000
* `sigalgs` {string} Colon-separated list of supported signature algorithms.
19922001
The list can contain digest algorithms (`SHA256`, `MD5` etc.), public key
19932002
algorithms (`RSA-PSS`, `ECDSA` etc.), combination of both (e.g
@@ -2378,6 +2387,25 @@ TLSv1.2 and below.
23782387
console.log(tls.getCiphers()); // ['aes128-gcm-sha256', 'aes128-sha', ...]
23792388
```
23802389

2390+
## `tls.getCertificateCompressionAlgorithms()`
2391+
2392+
<!-- YAML
2393+
added: REPLACEME
2394+
-->
2395+
2396+
* Returns: {string\[]}
2397+
2398+
Returns an array with the names of the RFC 8879 certificate compression
2399+
algorithms supported by the current OpenSSL build, suitable for use in the
2400+
`certificateCompression` option of [`tls.createSecureContext()`][]. Possible
2401+
values include `'zlib'`, `'brotli'`, and `'zstd'`.
2402+
2403+
The array is empty when certificate compression is unavailable.
2404+
2405+
```js
2406+
console.log(tls.getCertificateCompressionAlgorithms()); // ['zlib', 'brotli', 'zstd']
2407+
```
2408+
23812409
## `tls.rootCertificates`
23822410

23832411
<!-- YAML
@@ -2469,6 +2497,7 @@ added: v0.11.3
24692497
[RFC 4279]: https://tools.ietf.org/html/rfc4279
24702498
[RFC 5077]: https://tools.ietf.org/html/rfc5077
24712499
[RFC 5929]: https://tools.ietf.org/html/rfc5929
2500+
[RFC 8879]: https://tools.ietf.org/html/rfc8879
24722501
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.1/man7/ssl.html#Dealing-with-Protocol-Methods
24732502
[Session Resumption]: #session-resumption
24742503
[Stream]: stream.md#stream
Collapse file

‎lib/internal/tls/secure-context.js‎

Copy file name to clipboardExpand all lines: lib/internal/tls/secure-context.js
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ function configSecureContext(context, options = kEmptyObject, name = 'options')
133133
allowPartialTrustChain,
134134
ca,
135135
cert,
136+
certificateCompression,
136137
ciphers = getDefaultCiphers(),
137138
clientCertEngine,
138139
crl,
@@ -210,6 +211,43 @@ function configSecureContext(context, options = kEmptyObject, name = 'options')
210211
}
211212
}
212213

214+
if (certificateCompression != null) {
215+
if (!ArrayIsArray(certificateCompression)) {
216+
throw new ERR_INVALID_ARG_TYPE(
217+
`${name}.certificateCompression`, 'Array', certificateCompression);
218+
}
219+
220+
if (certificateCompression.length > 0) {
221+
// Pack length + algorithm IDs into a single Uint32 for a cheap
222+
// JS->C++ crossing. Layout:
223+
// bits 0-7 : length (1..3)
224+
// bits 8-15: alg id at position 0
225+
// bits 16-23: alg id at position 1
226+
// bits 24-31: alg id at position 2
227+
// IDs match OpenSSL's TLSEXT_comp_cert_zlib (1), _brotli (2), _zstd (3)
228+
if (certificateCompression.length > 3) {
229+
throw new ERR_INVALID_ARG_VALUE(
230+
`${name}.certificateCompression`, certificateCompression,
231+
'can specify at most 3 algorithms');
232+
}
233+
let packed = certificateCompression.length;
234+
for (let i = 0; i < certificateCompression.length; i++) {
235+
const algoName = certificateCompression[i];
236+
let id;
237+
if (algoName === 'zlib') id = 1;
238+
else if (algoName === 'brotli') id = 2;
239+
else if (algoName === 'zstd') id = 3;
240+
else {
241+
throw new ERR_INVALID_ARG_VALUE(
242+
`${name}.certificateCompression[${i}]`, algoName,
243+
"must be 'zlib', 'brotli', or 'zstd'");
244+
}
245+
packed |= id << (8 * (i + 1));
246+
}
247+
context.setCertificateCompression(packed);
248+
}
249+
}
250+
213251
if (sigalgs !== undefined && sigalgs !== null) {
214252
validateString(sigalgs, `${name}.sigalgs`);
215253

Collapse file

‎lib/internal/tls/wrap.js‎

Copy file name to clipboardExpand all lines: lib/internal/tls/wrap.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,7 @@ Server.prototype.setSecureContext = function(options) {
16201620

16211621
this.privateKeyIdentifier = options.privateKeyIdentifier;
16221622
this.privateKeyEngine = options.privateKeyEngine;
1623+
this.certificateCompression = options.certificateCompression;
16231624

16241625
this._sharedCreds = tls.createSecureContext({
16251626
pfx: this.pfx,
@@ -1643,6 +1644,7 @@ Server.prototype.setSecureContext = function(options) {
16431644
sessionTimeout: this.sessionTimeout,
16441645
privateKeyIdentifier: this.privateKeyIdentifier,
16451646
privateKeyEngine: this.privateKeyEngine,
1647+
certificateCompression: this.certificateCompression,
16461648
});
16471649
};
16481650

Collapse file

‎lib/tls.js‎

Copy file name to clipboardExpand all lines: lib/tls.js
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const {
4747
resetRootCertStore,
4848
getUserRootCertificates,
4949
getSSLCiphers,
50+
getCertificateCompressionAlgorithms,
5051
startLoadingCertificatesOffThread,
5152
} = internalBinding('crypto');
5253

@@ -113,6 +114,10 @@ exports.getCiphers = internalUtil.cachedResult(
113114
() => internalUtil.filterDuplicateStrings(getSSLCiphers(), true),
114115
);
115116

117+
exports.getCertificateCompressionAlgorithms = internalUtil.cachedResult(
118+
() => getCertificateCompressionAlgorithms(),
119+
);
120+
116121
let bundledRootCertificates;
117122
function cacheBundledRootCertificates() {
118123
bundledRootCertificates ||= ObjectFreeze(getBundledRootCertificates());
Collapse file

‎src/crypto/crypto_context.cc‎

Copy file name to clipboardExpand all lines: src/crypto/crypto_context.cc
+112-1Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
#include "util.h"
1313
#include "v8.h"
1414

15-
#include <openssl/x509.h>
15+
#include <openssl/comp.h>
1616
#include <openssl/pkcs12.h>
1717
#include <openssl/rand.h>
18+
#include <openssl/x509.h>
1819
#ifndef OPENSSL_NO_ENGINE
1920
#include <openssl/engine.h>
2021
#endif // !OPENSSL_NO_ENGINE
@@ -1378,6 +1379,8 @@ Local<FunctionTemplate> SecureContext::GetConstructorTemplate(
13781379
SetProtoMethod(isolate, tmpl, "setOptions", SetOptions);
13791380
SetProtoMethod(isolate, tmpl, "setSessionIdContext", SetSessionIdContext);
13801381
SetProtoMethod(isolate, tmpl, "setSessionTimeout", SetSessionTimeout);
1382+
SetProtoMethod(
1383+
isolate, tmpl, "setCertificateCompression", SetCertificateCompression);
13811384
SetProtoMethod(isolate, tmpl, "close", Close);
13821385
SetProtoMethod(isolate, tmpl, "loadPKCS12", LoadPKCS12);
13831386
SetProtoMethod(isolate, tmpl, "setTicketKeys", SetTicketKeys);
@@ -1442,6 +1445,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
14421445
target,
14431446
"startLoadingCertificatesOffThread",
14441447
StartLoadingCertificatesOffThread);
1448+
SetMethodNoSideEffect(context,
1449+
target,
1450+
"getCertificateCompressionAlgorithms",
1451+
GetCertificateCompressionAlgorithms);
14451452
}
14461453

14471454
void SecureContext::RegisterExternalReferences(
@@ -1466,6 +1473,7 @@ void SecureContext::RegisterExternalReferences(
14661473
registry->Register(SetOptions);
14671474
registry->Register(SetSessionIdContext);
14681475
registry->Register(SetSessionTimeout);
1476+
registry->Register(SetCertificateCompression);
14691477
registry->Register(Close);
14701478
registry->Register(LoadPKCS12);
14711479
registry->Register(SetTicketKeys);
@@ -1487,6 +1495,7 @@ void SecureContext::RegisterExternalReferences(
14871495
registry->Register(ResetRootCertStore);
14881496
registry->Register(GetUserRootCertificates);
14891497
registry->Register(StartLoadingCertificatesOffThread);
1498+
registry->Register(GetCertificateCompressionAlgorithms);
14901499
}
14911500

14921501
SecureContext* SecureContext::Create(Environment* env) {
@@ -1624,6 +1633,14 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
16241633
env->external_memory_accounter()->Increase(env->isolate(), kExternalSize);
16251634
SSL_CTX_set_app_data(sc->ctx_.get(), sc);
16261635

1636+
// OpenSSL populates cert_comp_prefs with all available algorithms by
1637+
// default when compression libraries are linked. Clear them so that
1638+
// certificate compression (RFC 8879) is always opt-in for now, via
1639+
// the certificateCompression option.
1640+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
1641+
SSL_CTX_set1_cert_comp_preference(sc->ctx_.get(), nullptr, 0);
1642+
#endif
1643+
16271644
// Disable SSLv2 in the case when method == TLS_method() and the
16281645
// cipher list contains SSLv2 ciphers (not the default, should be rare.)
16291646
// The bundled OpenSSL doesn't have SSLv2 support but the system OpenSSL may.
@@ -2131,6 +2148,100 @@ void SecureContext::SetSessionTimeout(const FunctionCallbackInfo<Value>& args) {
21312148
SSL_CTX_set_timeout(sc->ctx_.get(), sessionTimeout);
21322149
}
21332150

2151+
void SecureContext::SetCertificateCompression(
2152+
const FunctionCallbackInfo<Value>& args) {
2153+
SecureContext* sc;
2154+
ASSIGN_OR_RETURN_UNWRAP(&sc, args.This());
2155+
Environment* env = sc->env();
2156+
2157+
CHECK_GE(args.Length(), 1);
2158+
CHECK(args[0]->IsUint32());
2159+
2160+
// Cert compression requires TLS 1.3:
2161+
long max_proto = // NOLINT(runtime/int)
2162+
SSL_CTX_get_max_proto_version(sc->ctx_.get());
2163+
if (max_proto != 0 && max_proto < TLS1_3_VERSION) {
2164+
return THROW_ERR_INVALID_ARG_VALUE(
2165+
env,
2166+
"certificateCompression requires a TLS protocol range that includes "
2167+
"TLSv1.3");
2168+
}
2169+
2170+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
2171+
// JS packs (length | alg0<<8 | alg1<<16 | alg2<<24) into a single Uint32.
2172+
// IDs match TLSEXT_comp_cert_zlib (1), _brotli (2), _zstd (3).
2173+
uint32_t packed = args[0].As<v8::Uint32>()->Value();
2174+
size_t len = packed & 0xff;
2175+
2176+
// TLSEXT_comp_cert_limit is the limit for a zero-terminated algs array,
2177+
// total number of available algs is one fewer.
2178+
constexpr size_t kMaxCompAlgs = TLSEXT_comp_cert_limit - 1;
2179+
if (len == 0 || len > kMaxCompAlgs) {
2180+
return THROW_ERR_INVALID_ARG_VALUE(
2181+
env,
2182+
"certificateCompression must specify fewer than %d algorithms",
2183+
static_cast<int>(kMaxCompAlgs));
2184+
}
2185+
2186+
int algs[kMaxCompAlgs];
2187+
for (size_t i = 0; i < len; i++) {
2188+
algs[i] = (packed >> (8 * (i + 1))) & 0xff;
2189+
}
2190+
if (!SSL_CTX_set1_cert_comp_preference(
2191+
sc->ctx_.get(), algs, static_cast<size_t>(len))) {
2192+
return THROW_ERR_CRYPTO_OPERATION_FAILED(
2193+
env, "Failed to set certificate compression preference");
2194+
}
2195+
2196+
// Pre-compress the loaded certificate(s) for all supported algorithms.
2197+
// Returns 0 when no certificate is loaded (e.g. client-only context) or
2198+
// when compression did not reduce size - both are non-fatal.
2199+
constexpr int kCompressAllAlgs = 0;
2200+
SSL_CTX_compress_certs(sc->ctx_.get(), kCompressAllAlgs);
2201+
2202+
// Store preferences for propagation during SNI context switches.
2203+
memcpy(sc->cert_comp_prefs_, algs, sizeof(int) * len);
2204+
sc->cert_comp_prefs_len_ = len;
2205+
2206+
// Cache pre-compressed cert data for SNI context switches.
2207+
// setSniContext uses SSL_use_certificate which doesn't carry comp_cert data,
2208+
// so we extract it here and re-apply via SSL_set1_compressed_cert later.
2209+
sc->compressed_certs_.clear();
2210+
for (size_t i = 0; i < len; i++) {
2211+
unsigned char* data = nullptr;
2212+
size_t orig_len = 0;
2213+
size_t comp_len =
2214+
SSL_CTX_get1_compressed_cert(sc->ctx_.get(), algs[i], &data, &orig_len);
2215+
ncrypto::DataPointer comp(data, comp_len);
2216+
if (comp_len > 0 && data != nullptr) {
2217+
sc->compressed_certs_.push_back(
2218+
{algs[i],
2219+
std::vector<unsigned char>(data, data + comp_len),
2220+
orig_len});
2221+
}
2222+
}
2223+
#else
2224+
return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(
2225+
env, "Certificate compression is not supported by this OpenSSL build");
2226+
#endif
2227+
}
2228+
2229+
void SecureContext::GetCertificateCompressionAlgorithms(
2230+
const FunctionCallbackInfo<Value>& args) {
2231+
Environment* env = Environment::GetCurrent(args);
2232+
LocalVector<Value> algs(env->isolate());
2233+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
2234+
if (BIO_f_zlib() != nullptr)
2235+
algs.push_back(FIXED_ONE_BYTE_STRING(env->isolate(), "zlib"));
2236+
if (BIO_f_brotli() != nullptr)
2237+
algs.push_back(FIXED_ONE_BYTE_STRING(env->isolate(), "brotli"));
2238+
if (BIO_f_zstd() != nullptr)
2239+
algs.push_back(FIXED_ONE_BYTE_STRING(env->isolate(), "zstd"));
2240+
#endif
2241+
args.GetReturnValue().Set(
2242+
Array::New(env->isolate(), algs.data(), algs.size()));
2243+
}
2244+
21342245
void SecureContext::Close(const FunctionCallbackInfo<Value>& args) {
21352246
SecureContext* sc;
21362247
ASSIGN_OR_RETURN_UNWRAP(&sc, args.This());
Collapse file

‎src/crypto/crypto_context.h‎

Copy file name to clipboardExpand all lines: src/crypto/crypto_context.h
+35Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include "memory_tracker.h"
1111
#include "v8.h"
1212

13+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
14+
#include <vector>
15+
#endif
16+
1317
namespace node {
1418
namespace crypto {
1519
// A maxVersion of 0 means "any", but OpenSSL may support TLS versions that
@@ -47,6 +51,27 @@ class SecureContext final : public BaseObject {
4751
// the SecureContext.
4852
ncrypto::SSLCtxPointer& ctx() { return ctx_; }
4953

54+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
55+
bool HasCertCompression() const {
56+
return cert_comp_prefs_len_ > 0;
57+
}
58+
int* CertCompPrefs() {
59+
return cert_comp_prefs_;
60+
}
61+
size_t CertCompPrefsLen() const {
62+
return cert_comp_prefs_len_;
63+
}
64+
65+
struct CompressedCertData {
66+
int algorithm;
67+
std::vector<unsigned char> data;
68+
size_t orig_length;
69+
};
70+
const std::vector<CompressedCertData>& CompressedCerts() const {
71+
return compressed_certs_;
72+
}
73+
#endif
74+
5075
ncrypto::SSLPointer CreateSSL();
5176

5277
void SetGetSessionCallback(GetSessionCb cb);
@@ -106,6 +131,10 @@ class SecureContext final : public BaseObject {
106131
const v8::FunctionCallbackInfo<v8::Value>& args);
107132
static void SetSessionTimeout(
108133
const v8::FunctionCallbackInfo<v8::Value>& args);
134+
static void SetCertificateCompression(
135+
const v8::FunctionCallbackInfo<v8::Value>& args);
136+
static void GetCertificateCompressionAlgorithms(
137+
const v8::FunctionCallbackInfo<v8::Value>& args);
109138
static void SetMinProto(const v8::FunctionCallbackInfo<v8::Value>& args);
110139
static void SetMaxProto(const v8::FunctionCallbackInfo<v8::Value>& args);
111140
static void GetMinProto(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -156,6 +185,12 @@ class SecureContext final : public BaseObject {
156185
unsigned char ticket_key_name_[16];
157186
unsigned char ticket_key_aes_[16];
158187
unsigned char ticket_key_hmac_[16];
188+
189+
#ifdef NODE_OPENSSL_HAS_CERT_COMP
190+
int cert_comp_prefs_[TLSEXT_comp_cert_limit] = {};
191+
size_t cert_comp_prefs_len_ = 0;
192+
std::vector<CompressedCertData> compressed_certs_;
193+
#endif
159194
};
160195

161196
int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.