Skip to content

Commit c73dda7

Browse files
authored
Merge branch 'seanmonstar:master' into local-socketaddr
2 parents 6f8de48 + 2c3581e commit c73dda7

File tree

8 files changed

+139
-66
lines changed

8 files changed

+139
-66
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
### v0.3.6 (September 27, 2023)
2+
3+
- **Features**:
4+
- Add ability to pass `None` to `multipart::form().max_length()`.
5+
- Implement `Reply` for `Result<impl Reply, impl Reply>`.
6+
- Make `multipart::Part::content_type()` return the full mime string.
7+
- Add `TlsServer::try_bind_with_graceful_shutdown()`.
8+
- **Fixes**:
9+
- Updated tungstenite and rustls dependencies for security fixes.
10+
111
### v0.3.5 (April 28, 2023)
212

313
- **Fixes**:

Cargo.toml

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "warp"
3-
version = "0.3.5" # don't forget to update html_root_url
3+
version = "0.3.6" # don't forget to update html_root_url
44
description = "serve the web at warp speeds"
55
authors = ["Sean McArthur <[email protected]>"]
66
license = "MIT"
@@ -21,7 +21,7 @@ async-compression = { version = "0.3.7", features = ["tokio"], optional = true }
2121
bytes = "1.0"
2222
futures-util = { version = "0.3", default-features = false, features = ["sink"] }
2323
futures-channel = { version = "0.3.17", features = ["sink"]}
24-
headers = "0.3"
24+
headers = "0.3.5"
2525
http = "0.2"
2626
hyper = { version = "0.14.19", features = ["stream", "server", "http1", "http2", "tcp", "client"] }
2727
log = "0.4"
@@ -31,17 +31,16 @@ multer = { version = "2.1.0", optional = true }
3131
scoped-tls = "1.0"
3232
serde = "1.0"
3333
serde_json = "1.0"
34-
serde_urlencoded = "0.7"
34+
serde_urlencoded = "0.7.1"
3535
tokio = { version = "1.0", features = ["fs", "sync", "time"] }
36-
tokio-stream = "0.1.1"
37-
tokio-util = { version = "0.7", features = ["io"] }
36+
tokio-util = { version = "0.7.1", features = ["io"] }
3837
tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] }
3938
tower-service = "0.3"
40-
tokio-tungstenite = { version = "0.18", optional = true }
39+
tokio-tungstenite = { version = "0.20", optional = true }
4140
percent-encoding = "2.1"
4241
pin-project = "1.0"
43-
tokio-rustls = { version = "0.24", optional = true }
44-
rustls-pemfile = "1.0"
42+
tokio-rustls = { version = "0.25", optional = true }
43+
rustls-pemfile = { version = "2.0", optional = true }
4544

4645
[dev-dependencies]
4746
pretty_env_logger = "0.5"
@@ -57,7 +56,7 @@ listenfd = "1.0"
5756
default = ["multipart", "websocket"]
5857
multipart = ["multer"]
5958
websocket = ["tokio-tungstenite"]
60-
tls = ["tokio-rustls"]
59+
tls = ["tokio-rustls", "rustls-pemfile"]
6160

6261
# Enable compression-related filters
6362
compression = ["compression-brotli", "compression-gzip"]

examples/tls.rs

+4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ async fn main() {
1313

1414
warp::serve(routes)
1515
.tls()
16+
// RSA
1617
.cert_path("examples/tls/cert.pem")
1718
.key_path("examples/tls/key.rsa")
19+
// ECC
20+
// .cert_path("examples/tls/cert.ecc.pem")
21+
// .key_path("examples/tls/key.ecc")
1822
.run(([127, 0, 0, 1], 3030))
1923
.await;
2024
}

examples/tls/cert.ecc.pem

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBtDCCAVoCCQDFz95/8CeJaDAKBggqhkjOPQQDAjBiMQswCQYDVQQGEwJERTEQ
3+
MA4GA1UECAwHR2VybWFueTEQMA4GA1UEBwwHTGVpcHppZzESMBAGA1UEAwwJbG9j
4+
YWwuZGV2MRswGQYJKoZIhvcNAQkBFgxoaUBsb2NhbC5kZXYwHhcNMjMwNTI4MTk0
5+
NzA4WhcNMjYwNTI3MTk0NzA4WjBiMQswCQYDVQQGEwJERTEQMA4GA1UECAwHR2Vy
6+
bWFueTEQMA4GA1UEBwwHTGVpcHppZzESMBAGA1UEAwwJbG9jYWwuZGV2MRswGQYJ
7+
KoZIhvcNAQkBFgxoaUBsb2NhbC5kZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
8+
AATZR4F60X+iHjeD6kySZfXljNckDb22QYQ76Ts4GFYWkdDstU6yehxyER+MZWsm
9+
UnTE/Gy3mnpSmMzoSBfoKRmHMAoGCCqGSM49BAMCA0gAMEUCIQChOTwbAYlx6zg0
10+
yc3Oc+zrNY8Yd8oRUD+cG/wdz+gN/wIgP199zXAPXiYUFFd1CnIYmWJSglaOUbYj
11+
ZP/ixZR9HQs=
12+
-----END CERTIFICATE-----

examples/tls/key.ecc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIPwp3LAnLEyWe2lLz66Y3QCCJ/BEMJheTM0shZnnSw6toAoGCCqGSM49
3+
AwEHoUQDQgAE2UeBetF/oh43g+pMkmX15YzXJA29tkGEO+k7OBhWFpHQ7LVOsnoc
4+
chEfjGVrJlJ0xPxst5p6UpjM6EgX6CkZhw==
5+
-----END EC PRIVATE KEY-----

src/filters/ws.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,21 @@ impl Ws {
9292

9393
// config
9494

95-
/// Set the size of the internal message send queue.
96-
pub fn max_send_queue(mut self, max: usize) -> Self {
95+
/// Does nothing.
96+
///
97+
/// # Deprecated
98+
///
99+
/// Use `max_write_buffer_size()` instead.
100+
#[deprecated = "use max_write_buffer_size instead"]
101+
pub fn max_send_queue(self, _max: usize) -> Self {
102+
self
103+
}
104+
105+
/// The max size of the write buffer, in bytes.
106+
pub fn max_write_buffer_size(mut self, max: usize) -> Self {
97107
self.config
98108
.get_or_insert_with(WebSocketConfig::default)
99-
.max_send_queue = Some(max);
109+
.max_write_buffer_size = max;
100110
self
101111
}
102112

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![doc(html_root_url = "https://docs.rs/warp/0.3.5")]
1+
#![doc(html_root_url = "https://docs.rs/warp/0.3.6")]
22
#![deny(missing_docs)]
33
#![deny(missing_debug_implementations)]
44
#![deny(rust_2018_idioms)]

src/tls.rs

+86-53
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
1212
use futures_util::ready;
1313
use hyper::server::accept::Accept;
1414
use hyper::server::conn::{AddrIncoming, AddrStream};
15+
use tokio_rustls::rustls::server::WebPkiClientVerifier;
16+
use tokio_rustls::rustls::{Error as TlsError, RootCertStore, ServerConfig};
1517

1618
use crate::transport::Transport;
17-
use tokio_rustls::rustls::{
18-
server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth},
19-
Certificate, Error as TlsError, PrivateKey, RootCertStore, ServerConfig,
20-
};
2119

2220
/// Represents errors that can occur building the TlsConfig
2321
#[derive(Debug)]
2422
pub(crate) enum TlsConfigError {
2523
Io(io::Error),
2624
/// An Error parsing the Certificate
2725
CertParseError,
28-
/// An Error parsing a Pkcs8 key
29-
Pkcs8ParseError,
30-
/// An Error parsing a Rsa key
31-
RsaParseError,
26+
/// Identity PEM is invalid
27+
InvalidIdentityPem,
28+
/// Identity PEM is missing a private key such as RSA, ECC or PKCS8
29+
MissingPrivateKey,
30+
/// Unknown private key format
31+
UnknownPrivateKeyFormat,
3232
/// An error from an empty key
3333
EmptyKey,
3434
/// An error from an invalid key
@@ -40,8 +40,12 @@ impl fmt::Display for TlsConfigError {
4040
match self {
4141
TlsConfigError::Io(err) => err.fmt(f),
4242
TlsConfigError::CertParseError => write!(f, "certificate parse error"),
43-
TlsConfigError::Pkcs8ParseError => write!(f, "pkcs8 parse error"),
44-
TlsConfigError::RsaParseError => write!(f, "rsa parse error"),
43+
TlsConfigError::UnknownPrivateKeyFormat => write!(f, "unknown private key format"),
44+
TlsConfigError::MissingPrivateKey => write!(
45+
f,
46+
"Identity PEM is missing a private key such as RSA, ECC or PKCS8"
47+
),
48+
TlsConfigError::InvalidIdentityPem => write!(f, "identity PEM is invalid"),
4549
TlsConfigError::EmptyKey => write!(f, "key contains no private key"),
4650
TlsConfigError::InvalidKey(err) => write!(f, "key contains an invalid key, {}", err),
4751
}
@@ -170,73 +174,81 @@ impl TlsConfigBuilder {
170174
pub(crate) fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
171175
let mut cert_rdr = BufReader::new(self.cert);
172176
let cert = rustls_pemfile::certs(&mut cert_rdr)
173-
.map_err(|_e| TlsConfigError::CertParseError)?
174-
.into_iter()
175-
.map(Certificate)
176-
.collect();
177-
178-
let key = {
179-
// convert it to Vec<u8> to allow reading it again if key is RSA
180-
let mut key_vec = Vec::new();
181-
self.key
182-
.read_to_end(&mut key_vec)
183-
.map_err(TlsConfigError::Io)?;
184-
185-
if key_vec.is_empty() {
186-
return Err(TlsConfigError::EmptyKey);
187-
}
177+
.collect::<Result<Vec<_>, _>>()
178+
.map_err(|_e| TlsConfigError::CertParseError)?;
188179

189-
let mut pkcs8 = rustls_pemfile::pkcs8_private_keys(&mut key_vec.as_slice())
190-
.map_err(|_e| TlsConfigError::Pkcs8ParseError)?;
180+
let mut key_vec = Vec::new();
181+
self.key
182+
.read_to_end(&mut key_vec)
183+
.map_err(TlsConfigError::Io)?;
191184

192-
if !pkcs8.is_empty() {
193-
PrivateKey(pkcs8.remove(0))
194-
} else {
195-
let mut rsa = rustls_pemfile::rsa_private_keys(&mut key_vec.as_slice())
196-
.map_err(|_e| TlsConfigError::RsaParseError)?;
185+
if key_vec.is_empty() {
186+
return Err(TlsConfigError::EmptyKey);
187+
}
197188

198-
if !rsa.is_empty() {
199-
PrivateKey(rsa.remove(0))
200-
} else {
201-
return Err(TlsConfigError::EmptyKey);
202-
}
189+
let mut key_opt = None;
190+
let mut key_cur = std::io::Cursor::new(key_vec);
191+
for item in rustls_pemfile::read_all(&mut key_cur)
192+
.collect::<Result<Vec<_>, _>>()
193+
.map_err(|_e| TlsConfigError::InvalidIdentityPem)?
194+
{
195+
match item {
196+
rustls_pemfile::Item::Pkcs1Key(k) => key_opt = Some(k.into()),
197+
rustls_pemfile::Item::Pkcs8Key(k) => key_opt = Some(k.into()),
198+
rustls_pemfile::Item::Sec1Key(k) => key_opt = Some(k.into()),
199+
_ => return Err(TlsConfigError::UnknownPrivateKeyFormat),
203200
}
201+
}
202+
let key = match key_opt {
203+
Some(v) => v,
204+
_ => return Err(TlsConfigError::MissingPrivateKey),
204205
};
205206

206207
fn read_trust_anchor(
207208
trust_anchor: Box<dyn Read + Send + Sync>,
208209
) -> Result<RootCertStore, TlsConfigError> {
209210
let trust_anchors = {
210211
let mut reader = BufReader::new(trust_anchor);
211-
rustls_pemfile::certs(&mut reader).map_err(TlsConfigError::Io)?
212+
rustls_pemfile::certs(&mut reader)
213+
.collect::<Result<Vec<_>, _>>()
214+
.map_err(TlsConfigError::Io)?
212215
};
213216

214217
let mut store = RootCertStore::empty();
215-
let (added, _skipped) = store.add_parsable_certificates(&trust_anchors);
218+
let (added, _skipped) = store.add_parsable_certificates(trust_anchors);
216219
if added == 0 {
217220
return Err(TlsConfigError::CertParseError);
218221
}
219222

220223
Ok(store)
221224
}
222225

223-
let client_auth = match self.client_auth {
224-
TlsClientAuth::Off => NoClientAuth::boxed(),
225-
TlsClientAuth::Optional(trust_anchor) => {
226-
AllowAnyAnonymousOrAuthenticatedClient::new(read_trust_anchor(trust_anchor)?)
227-
.boxed()
228-
}
229-
TlsClientAuth::Required(trust_anchor) => {
230-
AllowAnyAuthenticatedClient::new(read_trust_anchor(trust_anchor)?).boxed()
226+
let config = {
227+
let builder = ServerConfig::builder();
228+
let mut config = match self.client_auth {
229+
TlsClientAuth::Off => builder.with_no_client_auth(),
230+
TlsClientAuth::Optional(trust_anchor) => {
231+
let verifier =
232+
WebPkiClientVerifier::builder(read_trust_anchor(trust_anchor)?.into())
233+
.allow_unauthenticated()
234+
.build()
235+
.map_err(|_| TlsConfigError::CertParseError)?;
236+
builder.with_client_cert_verifier(verifier)
237+
}
238+
TlsClientAuth::Required(trust_anchor) => {
239+
let verifier =
240+
WebPkiClientVerifier::builder(read_trust_anchor(trust_anchor)?.into())
241+
.build()
242+
.map_err(|_| TlsConfigError::CertParseError)?;
243+
builder.with_client_cert_verifier(verifier)
244+
}
231245
}
246+
.with_single_cert_with_ocsp(cert, key, self.ocsp_resp)
247+
.map_err(TlsConfigError::InvalidKey)?;
248+
config.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
249+
config
232250
};
233251

234-
let mut config = ServerConfig::builder()
235-
.with_safe_defaults()
236-
.with_client_cert_verifier(client_auth)
237-
.with_single_cert_with_ocsp_and_sct(cert, key, self.ocsp_resp, Vec::new())
238-
.map_err(TlsConfigError::InvalidKey)?;
239-
config.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
240252
Ok(config)
241253
}
242254
}
@@ -416,4 +428,25 @@ mod tests {
416428
.build()
417429
.unwrap();
418430
}
431+
432+
#[test]
433+
fn file_ecc_cert_key() {
434+
TlsConfigBuilder::new()
435+
.key_path("examples/tls/key.ecc")
436+
.cert_path("examples/tls/cert.ecc.pem")
437+
.build()
438+
.unwrap();
439+
}
440+
441+
#[test]
442+
fn bytes_ecc_cert_key() {
443+
let key = include_str!("../examples/tls/key.ecc");
444+
let cert = include_str!("../examples/tls/cert.ecc.pem");
445+
446+
TlsConfigBuilder::new()
447+
.key(key.as_bytes())
448+
.cert(cert.as_bytes())
449+
.build()
450+
.unwrap();
451+
}
419452
}

0 commit comments

Comments
 (0)