Skip to content

Commit 7242415

Browse files
authored
feat(boring): Add add_cert_compression_alg support (#44)
1 parent dbb5874 commit 7242415

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ antidote = "1.0.0"
3939
linked_hash_set = "0.1"
4040
openssl-macros = "0.1.1"
4141
autocfg = "1.3.0"
42+
brotli = "7"
43+
flate2 = "1"
44+
zstd = "0.13"

boring/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,18 @@ kx-client-pq-preferred = ["kx-safe-default", "kx-client-pq-supported"]
6464
# Implies "kx-safe-default".
6565
kx-client-nist-required = ["kx-safe-default"]
6666

67+
# Certificate compression
68+
cert-compression = ["flate2", "brotli", "zstd"]
69+
6770
[dependencies]
6871
bitflags = { workspace = true }
6972
foreign-types = { workspace = true }
7073
openssl-macros = { workspace = true }
7174
libc = { workspace = true }
7275
boring-sys = { workspace = true }
76+
brotli = { workspace = true, optional = true }
77+
flate2 = { workspace = true, optional = true }
78+
zstd = { workspace = true, optional = true }
7379

7480
[dev-dependencies]
7581
hex = { workspace = true }

boring/src/ssl/cert_compression.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use boring_sys as ffi;
2+
use std::{io::Read, slice};
3+
4+
/// IANA assigned identifier of compression algorithm.
5+
/// See https://www.rfc-editor.org/rfc/rfc8879.html#name-compression-algorithms
6+
#[repr(u16)]
7+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8+
pub enum CertCompressionAlgorithm {
9+
/// The Brotli compression algorithm.
10+
Brotli = ffi::TLSEXT_cert_compression_brotli as _,
11+
/// The zlib compression algorithm.
12+
Zlib = ffi::TLSEXT_cert_compression_zlib as _,
13+
/// The Zstandard compression algorithm.
14+
Zstd = ffi::TLSEXT_cert_compression_zstd as _,
15+
}
16+
17+
impl CertCompressionAlgorithm {
18+
/// Returns the compression function for the algorithm.
19+
pub(crate) fn compression_fn(&self) -> ffi::ssl_cert_compression_func_t {
20+
match &self {
21+
Self::Brotli => Some(brotli_compressor),
22+
Self::Zlib => Some(zlib_compressor),
23+
Self::Zstd => Some(zstd_compressor),
24+
}
25+
}
26+
27+
/// Returns the decompression function for the algorithm.
28+
pub(crate) fn decompression_fn(&self) -> ffi::ssl_cert_decompression_func_t {
29+
match &self {
30+
Self::Brotli => Some(brotli_decompressor),
31+
Self::Zlib => Some(zlib_decompressor),
32+
Self::Zstd => Some(zstd_decompressor),
33+
}
34+
}
35+
}
36+
37+
extern "C" fn brotli_compressor(
38+
_ssl: *mut ffi::SSL,
39+
buffer: *mut ffi::CBB,
40+
in_: *const u8,
41+
in_len: usize,
42+
) -> ::std::os::raw::c_int {
43+
let mut uncompressed = unsafe { slice::from_raw_parts(in_, in_len) };
44+
let mut compressed = Vec::new();
45+
46+
let params = brotli::enc::encode::BrotliEncoderInitParams();
47+
48+
if brotli::BrotliCompress(&mut uncompressed, &mut compressed, &params).is_err() {
49+
return 0;
50+
}
51+
52+
unsafe { ffi::CBB_add_bytes(buffer, compressed.as_ptr(), compressed.len()) }
53+
}
54+
55+
extern "C" fn zlib_compressor(
56+
_ssl: *mut ffi::SSL,
57+
out: *mut ffi::CBB,
58+
in_: *const u8,
59+
in_len: usize,
60+
) -> ::std::os::raw::c_int {
61+
let mut uncompressed = unsafe { slice::from_raw_parts(in_, in_len) };
62+
let mut compressed = Vec::new();
63+
64+
let params = flate2::Compression::default();
65+
66+
let mut encoder = flate2::bufread::ZlibEncoder::new(&mut uncompressed, params);
67+
if encoder.read_to_end(&mut compressed).is_err() {
68+
return 0;
69+
}
70+
71+
unsafe { ffi::CBB_add_bytes(out, compressed.as_ptr(), compressed.len()) }
72+
}
73+
74+
extern "C" fn zstd_compressor(
75+
_ssl: *mut ffi::SSL,
76+
out: *mut ffi::CBB,
77+
in_: *const u8,
78+
in_len: usize,
79+
) -> ::std::os::raw::c_int {
80+
let mut uncompressed = unsafe { slice::from_raw_parts(in_, in_len) };
81+
82+
let compressed = if let Ok(compressed) = zstd::encode_all(&mut uncompressed, 3) {
83+
compressed
84+
} else {
85+
return 0;
86+
};
87+
88+
unsafe { ffi::CBB_add_bytes(out, compressed.as_ptr(), compressed.len()) }
89+
}
90+
91+
extern "C" fn brotli_decompressor(
92+
_ssl: *mut ffi::SSL,
93+
buffer: *mut *mut ffi::CRYPTO_BUFFER,
94+
uncompressed_len: usize,
95+
in_: *const u8,
96+
in_len: usize,
97+
) -> ::std::os::raw::c_int {
98+
let compressed = unsafe { slice::from_raw_parts(in_, in_len) };
99+
let mut uncompressed = Vec::with_capacity(uncompressed_len);
100+
101+
if brotli::BrotliDecompress(&mut &compressed[..], &mut uncompressed).is_err() {
102+
return 0;
103+
}
104+
105+
if uncompressed.len() != uncompressed_len {
106+
return 0;
107+
}
108+
109+
unsafe {
110+
*buffer = ffi::CRYPTO_BUFFER_new(
111+
uncompressed.as_ptr(),
112+
uncompressed_len,
113+
std::ptr::null_mut(),
114+
);
115+
116+
if buffer.is_null() {
117+
return 0;
118+
}
119+
}
120+
121+
1
122+
}
123+
124+
extern "C" fn zlib_decompressor(
125+
_ssl: *mut ffi::SSL,
126+
buffer: *mut *mut ffi::CRYPTO_BUFFER,
127+
uncompressed_len: usize,
128+
in_: *const u8,
129+
in_len: usize,
130+
) -> ::std::os::raw::c_int {
131+
let mut compressed = unsafe { slice::from_raw_parts(in_, in_len) };
132+
let mut uncompressed = Vec::with_capacity(uncompressed_len);
133+
134+
let mut decoder = flate2::bufread::ZlibDecoder::new(&mut compressed);
135+
if decoder.read_to_end(&mut uncompressed).is_err() {
136+
return 0;
137+
}
138+
139+
if uncompressed.len() != uncompressed_len {
140+
return 0;
141+
}
142+
143+
unsafe {
144+
*buffer = ffi::CRYPTO_BUFFER_new(
145+
uncompressed.as_ptr(),
146+
uncompressed_len,
147+
std::ptr::null_mut(),
148+
);
149+
150+
if buffer.is_null() {
151+
return 0;
152+
}
153+
}
154+
155+
1
156+
}
157+
158+
extern "C" fn zstd_decompressor(
159+
_ssl: *mut ffi::SSL,
160+
buffer: *mut *mut ffi::CRYPTO_BUFFER,
161+
uncompressed_len: usize,
162+
in_: *const u8,
163+
in_len: usize,
164+
) -> ::std::os::raw::c_int {
165+
let mut compressed = unsafe { slice::from_raw_parts(in_, in_len) };
166+
167+
let uncompressed = if let Ok(uncompressed) = zstd::decode_all(&mut compressed) {
168+
uncompressed
169+
} else {
170+
return 0;
171+
};
172+
173+
if uncompressed.len() != uncompressed_len {
174+
return 0;
175+
}
176+
177+
unsafe {
178+
*buffer = ffi::CRYPTO_BUFFER_new(
179+
uncompressed.as_ptr(),
180+
uncompressed_len,
181+
std::ptr::null_mut(),
182+
);
183+
184+
if buffer.is_null() {
185+
return 0;
186+
}
187+
}
188+
189+
1
190+
}

boring/src/ssl/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ pub use self::async_callbacks::{
101101
BoxCustomVerifyFuture, BoxGetSessionFinish, BoxGetSessionFuture, BoxPrivateKeyMethodFinish,
102102
BoxPrivateKeyMethodFuture, BoxSelectCertFinish, BoxSelectCertFuture, ExDataFuture,
103103
};
104+
#[cfg(feature = "cert-compression")]
105+
pub use self::cert_compression::CertCompressionAlgorithm;
104106
pub use self::connector::{
105107
ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder,
106108
};
@@ -109,6 +111,8 @@ pub use self::error::{Error, ErrorCode, HandshakeError};
109111
mod async_callbacks;
110112
mod bio;
111113
mod callbacks;
114+
#[cfg(feature = "cert-compression")]
115+
mod cert_compression;
112116
mod connector;
113117
mod error;
114118
mod mut_only;
@@ -1353,6 +1357,24 @@ impl SslContextBuilder {
13531357
unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) }
13541358
}
13551359

1360+
/// Sets whether a certificate compression algorithm should be used.
1361+
#[cfg(feature = "cert-compression")]
1362+
#[corresponds(SSL_CTX_add_cert_compression_alg)]
1363+
pub fn add_cert_compression_alg(
1364+
&mut self,
1365+
alg: CertCompressionAlgorithm,
1366+
) -> Result<(), ErrorStack> {
1367+
unsafe {
1368+
cvt(ffi::SSL_CTX_add_cert_compression_alg(
1369+
self.as_ptr(),
1370+
alg as _,
1371+
alg.compression_fn(),
1372+
alg.decompression_fn(),
1373+
))
1374+
.map(|_| ())
1375+
}
1376+
}
1377+
13561378
/// Sets the list of supported ciphers for protocols before TLSv1.3.
13571379
///
13581380
/// The `set_ciphersuites` method controls the cipher suites for TLSv1.3 in OpenSSL.

0 commit comments

Comments
 (0)