mirror of
https://github.com/actix/actix-web.git
synced 2024-12-30 12:00:38 +00:00
update ssl impls
This commit is contained in:
parent
9e95efcc16
commit
a6a2d2f444
7 changed files with 150 additions and 120 deletions
|
@ -27,7 +27,7 @@ path = "src/lib.rs"
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["open-ssl", "actix-connect/openssl"]
|
openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"]
|
||||||
|
|
||||||
# rustls support
|
# rustls support
|
||||||
rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"]
|
rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"]
|
||||||
|
@ -100,6 +100,8 @@ flate2 = { version="1.0.7", optional = true, default-features = false }
|
||||||
# optional deps
|
# optional deps
|
||||||
failure = { version = "0.1.5", optional = true }
|
failure = { version = "0.1.5", optional = true }
|
||||||
open-ssl = { version="0.10", package="openssl", optional = true }
|
open-ssl = { version="0.10", package="openssl", optional = true }
|
||||||
|
tokio-openssl = { version = "0.4.0-alpha.6", optional = true }
|
||||||
|
|
||||||
rust-tls = { version = "0.16.0", package="rustls", optional = true }
|
rust-tls = { version = "0.16.0", package="rustls", optional = true }
|
||||||
webpki-roots = { version = "0.18", optional = true }
|
webpki-roots = { version = "0.18", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -20,22 +20,22 @@ use super::error::ConnectError;
|
||||||
use super::pool::{ConnectionPool, Protocol};
|
use super::pool::{ConnectionPool, Protocol};
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use openssl::ssl::SslConnector as OpensslConnector;
|
use open_ssl::ssl::SslConnector as OpensslConnector;
|
||||||
|
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
use rustls::ClientConfig;
|
use rust_tls::ClientConfig;
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
enum SslConnector {
|
enum SslConnector {
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
Openssl(OpensslConnector),
|
Openssl(OpensslConnector),
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
Rustls(Arc<ClientConfig>),
|
Rustls(Arc<ClientConfig>),
|
||||||
}
|
}
|
||||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
type SslConnector = ();
|
type SslConnector = ();
|
||||||
|
|
||||||
/// Manages http client network connectivity
|
/// Manages http client network connectivity
|
||||||
|
@ -76,9 +76,9 @@ impl Connector<(), ()> {
|
||||||
TcpStream,
|
TcpStream,
|
||||||
> {
|
> {
|
||||||
let ssl = {
|
let ssl = {
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
{
|
{
|
||||||
use openssl::ssl::SslMethod;
|
use open_ssl::ssl::SslMethod;
|
||||||
|
|
||||||
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
|
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
let _ = ssl
|
let _ = ssl
|
||||||
|
@ -86,7 +86,7 @@ impl Connector<(), ()> {
|
||||||
.map_err(|e| error!("Can not set alpn protocol: {:?}", e));
|
.map_err(|e| error!("Can not set alpn protocol: {:?}", e));
|
||||||
SslConnector::Openssl(ssl.build())
|
SslConnector::Openssl(ssl.build())
|
||||||
}
|
}
|
||||||
#[cfg(all(not(feature = "ssl"), feature = "rust-tls"))]
|
#[cfg(all(not(feature = "openssl"), feature = "rustls"))]
|
||||||
{
|
{
|
||||||
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
let mut config = ClientConfig::new();
|
let mut config = ClientConfig::new();
|
||||||
|
@ -96,7 +96,7 @@ impl Connector<(), ()> {
|
||||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||||
SslConnector::Rustls(Arc::new(config))
|
SslConnector::Rustls(Arc::new(config))
|
||||||
}
|
}
|
||||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,7 +145,8 @@ where
|
||||||
Request = TcpConnect<Uri>,
|
Request = TcpConnect<Uri>,
|
||||||
Response = TcpConnection<Uri, U>,
|
Response = TcpConnection<Uri, U>,
|
||||||
Error = actix_connect::ConnectError,
|
Error = actix_connect::ConnectError,
|
||||||
> + 'static,
|
> + Clone
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
/// Connection timeout, i.e. max time to connect to remote host including dns name resolution.
|
/// Connection timeout, i.e. max time to connect to remote host including dns name resolution.
|
||||||
/// Set to 1 second by default.
|
/// Set to 1 second by default.
|
||||||
|
@ -154,14 +155,14 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// Use custom `SslConnector` instance.
|
/// Use custom `SslConnector` instance.
|
||||||
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
||||||
self.ssl = SslConnector::Openssl(connector);
|
self.ssl = SslConnector::Openssl(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
pub fn rustls(mut self, connector: Arc<ClientConfig>) -> Self {
|
pub fn rustls(mut self, connector: Arc<ClientConfig>) -> Self {
|
||||||
self.ssl = SslConnector::Rustls(connector);
|
self.ssl = SslConnector::Rustls(connector);
|
||||||
self
|
self
|
||||||
|
@ -217,7 +218,7 @@ where
|
||||||
self,
|
self,
|
||||||
) -> impl Service<Request = Connect, Response = impl Connection, Error = ConnectError>
|
) -> impl Service<Request = Connect, Response = impl Connection, Error = ConnectError>
|
||||||
+ Clone {
|
+ Clone {
|
||||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
{
|
{
|
||||||
let connector = TimeoutService::new(
|
let connector = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
|
@ -242,46 +243,49 @@ where
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
{
|
{
|
||||||
const H2: &[u8] = b"h2";
|
const H2: &[u8] = b"h2";
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use actix_connect::ssl::OpensslConnector;
|
use actix_connect::ssl::OpensslConnector;
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
use actix_connect::ssl::RustlsConnector;
|
use actix_connect::ssl::RustlsConnector;
|
||||||
use actix_service::boxed::service;
|
use actix_service::{boxed::service, pipeline};
|
||||||
#[cfg(feature = "rust-tls")]
|
#[cfg(feature = "rustls")]
|
||||||
use rustls::Session;
|
use rust_tls::Session;
|
||||||
|
|
||||||
let ssl_service = TimeoutService::new(
|
let ssl_service = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
apply_fn(self.connector.clone(), |msg: Connect, srv| {
|
pipeline(
|
||||||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
apply_fn(
|
||||||
})
|
UnpinWrapper(self.connector.clone()),
|
||||||
.map_err(ConnectError::from)
|
|msg: Connect, srv| {
|
||||||
|
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(ConnectError::from),
|
||||||
|
)
|
||||||
.and_then(match self.ssl {
|
.and_then(match self.ssl {
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "openssl")]
|
||||||
SslConnector::Openssl(ssl) => service(
|
SslConnector::Openssl(ssl) => OpensslConnector::service(ssl)
|
||||||
OpensslConnector::service(ssl)
|
.map(|stream| {
|
||||||
.map_err(ConnectError::from)
|
let sock = stream.into_parts().0;
|
||||||
.map(|stream| {
|
let h2 = sock
|
||||||
let sock = stream.into_parts().0;
|
.ssl()
|
||||||
let h2 = sock
|
.selected_alpn_protocol()
|
||||||
.get_ref()
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
.ssl()
|
.unwrap_or(false);
|
||||||
.selected_alpn_protocol()
|
if h2 {
|
||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
(Box::new(sock) as Box<dyn Io + Unpin>, Protocol::Http2)
|
||||||
.unwrap_or(false);
|
} else {
|
||||||
if h2 {
|
(Box::new(sock) as Box<dyn Io + Unpin>, Protocol::Http1)
|
||||||
(Box::new(sock) as Box<dyn Io>, Protocol::Http2)
|
}
|
||||||
} else {
|
})
|
||||||
(Box::new(sock) as Box<dyn Io>, Protocol::Http1)
|
.map_err(ConnectError::from),
|
||||||
}
|
|
||||||
}),
|
#[cfg(feature = "rustls")]
|
||||||
),
|
|
||||||
#[cfg(feature = "rust-tls")]
|
|
||||||
SslConnector::Rustls(ssl) => service(
|
SslConnector::Rustls(ssl) => service(
|
||||||
RustlsConnector::service(ssl)
|
UnpinWrapper(RustlsConnector::service(ssl))
|
||||||
.map_err(ConnectError::from)
|
.map_err(ConnectError::from)
|
||||||
.map(|stream| {
|
.map(|stream| {
|
||||||
let sock = stream.into_parts().0;
|
let sock = stream.into_parts().0;
|
||||||
|
@ -292,9 +296,15 @@ where
|
||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if h2 {
|
if h2 {
|
||||||
(Box::new(sock) as Box<dyn Io>, Protocol::Http2)
|
(
|
||||||
|
Box::new(sock) as Box<dyn Io + Unpin>,
|
||||||
|
Protocol::Http2,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(Box::new(sock) as Box<dyn Io>, Protocol::Http1)
|
(
|
||||||
|
Box::new(sock) as Box<dyn Io + Unpin>,
|
||||||
|
Protocol::Http1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -307,7 +317,7 @@ where
|
||||||
|
|
||||||
let tcp_service = TimeoutService::new(
|
let tcp_service = TimeoutService::new(
|
||||||
self.timeout,
|
self.timeout,
|
||||||
apply_fn(self.connector.clone(), |msg: Connect, srv| {
|
apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| {
|
||||||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||||
})
|
})
|
||||||
.map_err(ConnectError::from)
|
.map_err(ConnectError::from)
|
||||||
|
@ -339,11 +349,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct UnpinWrapper<T>(T);
|
struct UnpinWrapper<T: Clone>(T);
|
||||||
|
|
||||||
impl<T: Service> Unpin for UnpinWrapper<T> {}
|
impl<T: Clone> Unpin for UnpinWrapper<T> {}
|
||||||
|
|
||||||
impl<T: Service> Service for UnpinWrapper<T> {
|
impl<T: Service + Clone> Service for UnpinWrapper<T> {
|
||||||
type Request = T::Request;
|
type Request = T::Request;
|
||||||
type Response = T::Response;
|
type Response = T::Response;
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
@ -374,7 +384,7 @@ impl<T: Service> Future for UnpinWrapperFut<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
mod connect_impl {
|
mod connect_impl {
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
@ -439,7 +449,7 @@ mod connect_impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
||||||
mod connect_impl {
|
mod connect_impl {
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
@ -510,11 +520,11 @@ mod connect_impl {
|
||||||
|
|
||||||
fn call(&mut self, req: Connect) -> Self::Future {
|
fn call(&mut self, req: Connect) -> Self::Future {
|
||||||
match req.uri.scheme_str() {
|
match req.uri.scheme_str() {
|
||||||
Some("https") | Some("wss") => Either::B(InnerConnectorResponseB {
|
Some("https") | Some("wss") => Either::Right(InnerConnectorResponseB {
|
||||||
fut: self.ssl_pool.call(req),
|
fut: self.ssl_pool.call(req),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}),
|
}),
|
||||||
_ => Either::A(InnerConnectorResponseA {
|
_ => Either::Left(InnerConnectorResponseA {
|
||||||
fut: self.tcp_pool.call(req),
|
fut: self.tcp_pool.call(req),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use ring::digest::{Algorithm, SHA256};
|
use ring::hkdf::{Algorithm, KeyType, Prk, HKDF_SHA256};
|
||||||
use ring::hkdf::expand;
|
use ring::hmac;
|
||||||
use ring::hmac::SigningKey;
|
|
||||||
use ring::rand::{SecureRandom, SystemRandom};
|
use ring::rand::{SecureRandom, SystemRandom};
|
||||||
|
|
||||||
use super::private::KEY_LEN as PRIVATE_KEY_LEN;
|
use super::private::KEY_LEN as PRIVATE_KEY_LEN;
|
||||||
use super::signed::KEY_LEN as SIGNED_KEY_LEN;
|
use super::signed::KEY_LEN as SIGNED_KEY_LEN;
|
||||||
|
|
||||||
static HKDF_DIGEST: &Algorithm = &SHA256;
|
static HKDF_DIGEST: Algorithm = HKDF_SHA256;
|
||||||
const KEYS_INFO: &str = "COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
|
const KEYS_INFO: &[&[u8]] = &[b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM"];
|
||||||
|
|
||||||
/// A cryptographic master key for use with `Signed` and/or `Private` jars.
|
/// A cryptographic master key for use with `Signed` and/or `Private` jars.
|
||||||
///
|
///
|
||||||
|
@ -25,6 +24,13 @@ pub struct Key {
|
||||||
encryption_key: [u8; PRIVATE_KEY_LEN],
|
encryption_key: [u8; PRIVATE_KEY_LEN],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyType for &Key {
|
||||||
|
#[inline]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
SIGNED_KEY_LEN + PRIVATE_KEY_LEN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Key {
|
impl Key {
|
||||||
/// Derives new signing/encryption keys from a master key.
|
/// Derives new signing/encryption keys from a master key.
|
||||||
///
|
///
|
||||||
|
@ -56,21 +62,26 @@ impl Key {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand the user's key into two.
|
// An empty `Key` structure; will be filled in with HKDF derived keys.
|
||||||
let prk = SigningKey::new(HKDF_DIGEST, key);
|
let mut output_key = Key {
|
||||||
|
signing_key: [0; SIGNED_KEY_LEN],
|
||||||
|
encryption_key: [0; PRIVATE_KEY_LEN],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expand the master key into two HKDF generated keys.
|
||||||
let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
|
let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
|
||||||
expand(&prk, KEYS_INFO.as_bytes(), &mut both_keys);
|
let prk = Prk::new_less_safe(HKDF_DIGEST, key);
|
||||||
|
let okm = prk.expand(KEYS_INFO, &output_key).expect("okm expand");
|
||||||
|
okm.fill(&mut both_keys).expect("fill keys");
|
||||||
|
|
||||||
// Copy the keys into their respective arrays.
|
// Copy the key parts into their respective fields.
|
||||||
let mut signing_key = [0; SIGNED_KEY_LEN];
|
output_key
|
||||||
let mut encryption_key = [0; PRIVATE_KEY_LEN];
|
.signing_key
|
||||||
signing_key.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
|
.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
|
||||||
encryption_key.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
|
output_key
|
||||||
|
.encryption_key
|
||||||
Key {
|
.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
|
||||||
signing_key,
|
output_key
|
||||||
encryption_key,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates signing/encryption keys from a secure, random source. Keys are
|
/// Generates signing/encryption keys from a secure, random source. Keys are
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use ring::aead::{open_in_place, seal_in_place, Aad, Algorithm, Nonce, AES_256_GCM};
|
use ring::aead::{Aad, Algorithm, Nonce, AES_256_GCM};
|
||||||
use ring::aead::{OpeningKey, SealingKey};
|
use ring::aead::{LessSafeKey, UnboundKey};
|
||||||
use ring::rand::{SecureRandom, SystemRandom};
|
use ring::rand::{SecureRandom, SystemRandom};
|
||||||
|
|
||||||
use super::Key;
|
use super::Key;
|
||||||
|
@ -10,7 +10,7 @@ use crate::cookie::{Cookie, CookieJar};
|
||||||
|
|
||||||
// Keep these in sync, and keep the key len synced with the `private` docs as
|
// Keep these in sync, and keep the key len synced with the `private` docs as
|
||||||
// well as the `KEYS_INFO` const in secure::Key.
|
// well as the `KEYS_INFO` const in secure::Key.
|
||||||
static ALGO: &Algorithm = &AES_256_GCM;
|
static ALGO: &'static Algorithm = &AES_256_GCM;
|
||||||
const NONCE_LEN: usize = 12;
|
const NONCE_LEN: usize = 12;
|
||||||
pub const KEY_LEN: usize = 32;
|
pub const KEY_LEN: usize = 32;
|
||||||
|
|
||||||
|
@ -53,11 +53,14 @@ impl<'a> PrivateJar<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ad = Aad::from(name.as_bytes());
|
let ad = Aad::from(name.as_bytes());
|
||||||
let key = OpeningKey::new(ALGO, &self.key).expect("opening key");
|
let key = LessSafeKey::new(
|
||||||
let (nonce, sealed) = data.split_at_mut(NONCE_LEN);
|
UnboundKey::new(&ALGO, &self.key).expect("matching key length"),
|
||||||
|
);
|
||||||
|
let (nonce, mut sealed) = data.split_at_mut(NONCE_LEN);
|
||||||
let nonce =
|
let nonce =
|
||||||
Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`");
|
Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`");
|
||||||
let unsealed = open_in_place(&key, nonce, ad, 0, sealed)
|
let unsealed = key
|
||||||
|
.open_in_place(nonce, ad, &mut sealed)
|
||||||
.map_err(|_| "invalid key/nonce/value: bad seal")?;
|
.map_err(|_| "invalid key/nonce/value: bad seal")?;
|
||||||
|
|
||||||
if let Ok(unsealed_utf8) = str::from_utf8(unsealed) {
|
if let Ok(unsealed_utf8) = str::from_utf8(unsealed) {
|
||||||
|
@ -196,30 +199,33 @@ Please change it as soon as possible."
|
||||||
|
|
||||||
fn encrypt_name_value(name: &[u8], value: &[u8], key: &[u8]) -> Vec<u8> {
|
fn encrypt_name_value(name: &[u8], value: &[u8], key: &[u8]) -> Vec<u8> {
|
||||||
// Create the `SealingKey` structure.
|
// Create the `SealingKey` structure.
|
||||||
let key = SealingKey::new(ALGO, key).expect("sealing key creation");
|
let unbound = UnboundKey::new(&ALGO, key).expect("matching key length");
|
||||||
|
let key = LessSafeKey::new(unbound);
|
||||||
|
|
||||||
// Create a vec to hold the [nonce | cookie value | overhead].
|
// Create a vec to hold the [nonce | cookie value | overhead].
|
||||||
let overhead = ALGO.tag_len();
|
let mut data = vec![0; NONCE_LEN + value.len() + ALGO.tag_len()];
|
||||||
let mut data = vec![0; NONCE_LEN + value.len() + overhead];
|
|
||||||
|
|
||||||
// Randomly generate the nonce, then copy the cookie value as input.
|
// Randomly generate the nonce, then copy the cookie value as input.
|
||||||
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
|
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
|
||||||
|
let (in_out, tag) = in_out.split_at_mut(value.len());
|
||||||
|
in_out.copy_from_slice(value);
|
||||||
|
|
||||||
|
// Randomly generate the nonce into the nonce piece.
|
||||||
SystemRandom::new()
|
SystemRandom::new()
|
||||||
.fill(nonce)
|
.fill(nonce)
|
||||||
.expect("couldn't random fill nonce");
|
.expect("couldn't random fill nonce");
|
||||||
in_out[..value.len()].copy_from_slice(value);
|
let nonce = Nonce::try_assume_unique_for_key(nonce).expect("invalid `nonce` length");
|
||||||
let nonce =
|
|
||||||
Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`");
|
|
||||||
|
|
||||||
// Use cookie's name as associated data to prevent value swapping.
|
// Use cookie's name as associated data to prevent value swapping.
|
||||||
let ad = Aad::from(name);
|
let ad = Aad::from(name);
|
||||||
|
let ad_tag = key
|
||||||
|
.seal_in_place_separate_tag(nonce, ad, in_out)
|
||||||
|
.expect("in-place seal");
|
||||||
|
|
||||||
// Perform the actual sealing operation and get the output length.
|
// Copy the tag into the tag piece.
|
||||||
let output_len =
|
tag.copy_from_slice(ad_tag.as_ref());
|
||||||
seal_in_place(&key, nonce, ad, in_out, overhead).expect("in-place seal");
|
|
||||||
|
|
||||||
// Remove the overhead and return the sealed content.
|
// Remove the overhead and return the sealed content.
|
||||||
data.truncate(NONCE_LEN + output_len);
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use ring::digest::{Algorithm, SHA256};
|
use ring::hmac::{self, sign, verify};
|
||||||
use ring::hmac::{sign, verify_with_own_key as verify, SigningKey};
|
|
||||||
|
|
||||||
use super::Key;
|
use super::Key;
|
||||||
use crate::cookie::{Cookie, CookieJar};
|
use crate::cookie::{Cookie, CookieJar};
|
||||||
|
|
||||||
// Keep these in sync, and keep the key len synced with the `signed` docs as
|
// Keep these in sync, and keep the key len synced with the `signed` docs as
|
||||||
// well as the `KEYS_INFO` const in secure::Key.
|
// well as the `KEYS_INFO` const in secure::Key.
|
||||||
static HMAC_DIGEST: &Algorithm = &SHA256;
|
static HMAC_DIGEST: hmac::Algorithm = hmac::HMAC_SHA256;
|
||||||
const BASE64_DIGEST_LEN: usize = 44;
|
const BASE64_DIGEST_LEN: usize = 44;
|
||||||
pub const KEY_LEN: usize = 32;
|
pub const KEY_LEN: usize = 32;
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ pub const KEY_LEN: usize = 32;
|
||||||
/// This type is only available when the `secure` feature is enabled.
|
/// This type is only available when the `secure` feature is enabled.
|
||||||
pub struct SignedJar<'a> {
|
pub struct SignedJar<'a> {
|
||||||
parent: &'a mut CookieJar,
|
parent: &'a mut CookieJar,
|
||||||
key: SigningKey,
|
key: hmac::Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SignedJar<'a> {
|
impl<'a> SignedJar<'a> {
|
||||||
|
@ -32,7 +31,7 @@ impl<'a> SignedJar<'a> {
|
||||||
pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
|
pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
|
||||||
SignedJar {
|
SignedJar {
|
||||||
parent,
|
parent,
|
||||||
key: SigningKey::new(HMAC_DIGEST, key.signing()),
|
key: hmac::Key::new(HMAC_DIGEST, key.signing()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,7 @@ mod tests {
|
||||||
fn test_unread_data() {
|
fn test_unread_data() {
|
||||||
Runtime::new()
|
Runtime::new()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.block_on(lazy(|| {
|
.block_on(async {
|
||||||
let (_, mut payload) = Payload::create(false);
|
let (_, mut payload) = Payload::create(false);
|
||||||
|
|
||||||
payload.unread_data(Bytes::from("data"));
|
payload.unread_data(Bytes::from("data"));
|
||||||
|
@ -242,13 +242,12 @@ mod tests {
|
||||||
assert_eq!(payload.len(), 4);
|
assert_eq!(payload.len(), 4);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Async::Ready(Some(Bytes::from("data"))),
|
Poll::Ready(Some(Bytes::from("data"))),
|
||||||
payload.poll().ok().unwrap()
|
payload.next_item().await.ok().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let res: Result<(), ()> = Ok(());
|
result(())
|
||||||
result(res)
|
})
|
||||||
}))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ use std::{net, thread};
|
||||||
|
|
||||||
use actix_http_test::TestServer;
|
use actix_http_test::TestServer;
|
||||||
use actix_server_config::ServerConfig;
|
use actix_server_config::ServerConfig;
|
||||||
use actix_service::{new_service_cfg, service_fn, NewService};
|
use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::future::{self, ok, Future};
|
use futures::future::{self, err, ok, ready, Future, FutureExt};
|
||||||
use futures::stream::{once, Stream};
|
use futures::stream::{once, Stream, StreamExt};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use tokio_timer::sleep;
|
use tokio_timer::sleep;
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ fn test_expect_continue() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.expect(service_fn(|req: Request| {
|
.expect(service_fn(|req: Request| {
|
||||||
if req.head().uri.query() == Some("yes=") {
|
if req.head().uri.query() == Some("yes=") {
|
||||||
Ok(req)
|
ok(req)
|
||||||
} else {
|
} else {
|
||||||
Err(error::ErrorPreconditionFailed("error"))
|
err(error::ErrorPreconditionFailed("error"))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
|
.finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
|
||||||
|
@ -117,9 +117,12 @@ fn test_chunked_payload() {
|
||||||
HttpService::build().h1(|mut request: Request| {
|
HttpService::build().h1(|mut request: Request| {
|
||||||
request
|
request
|
||||||
.take_payload()
|
.take_payload()
|
||||||
.map_err(|e| panic!(format!("Error reading payload: {}", e)))
|
.map(|res| match res {
|
||||||
.fold(0usize, |acc, chunk| future::ok::<_, ()>(acc + chunk.len()))
|
Ok(pl) => pl,
|
||||||
.map(|req_size| Response::Ok().body(format!("size={}", req_size)))
|
Err(e) => panic!(format!("Error reading payload: {}", e)),
|
||||||
|
})
|
||||||
|
.fold(0usize, |acc, chunk| ready(acc + chunk.len()))
|
||||||
|
.map(|req_size| ok(Response::Ok().body(format!("size={}", req_size))))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -414,7 +417,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
#[test]
|
#[test]
|
||||||
fn test_h1_body() {
|
fn test_h1_body() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = srv.block_on(srv.get("/").send()).unwrap();
|
let response = srv.block_on(srv.get("/").send()).unwrap();
|
||||||
|
@ -493,7 +496,7 @@ fn test_h1_head_binary2() {
|
||||||
fn test_h1_body_length() {
|
fn test_h1_body_length() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build().h1(|_| {
|
HttpService::build().h1(|_| {
|
||||||
let body = once(Ok(Bytes::from_static(STR.as_ref())));
|
let body = once(ok(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, ()>(
|
||||||
Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)),
|
Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)),
|
||||||
)
|
)
|
||||||
|
@ -512,7 +515,7 @@ fn test_h1_body_length() {
|
||||||
fn test_h1_body_chunked_explicit() {
|
fn test_h1_body_chunked_explicit() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build().h1(|_| {
|
HttpService::build().h1(|_| {
|
||||||
let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, ()>(
|
||||||
Response::Ok()
|
Response::Ok()
|
||||||
.header(header::TRANSFER_ENCODING, "chunked")
|
.header(header::TRANSFER_ENCODING, "chunked")
|
||||||
|
@ -544,7 +547,7 @@ fn test_h1_body_chunked_explicit() {
|
||||||
fn test_h1_body_chunked_implicit() {
|
fn test_h1_body_chunked_implicit() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build().h1(|_| {
|
HttpService::build().h1(|_| {
|
||||||
let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(Response::Ok().streaming(body))
|
ok::<_, ()>(Response::Ok().streaming(body))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -569,15 +572,15 @@ fn test_h1_body_chunked_implicit() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_h1_response_http_error_handling() {
|
fn test_h1_response_http_error_handling() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build().h1(new_service_cfg(|_: &ServerConfig| {
|
HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| {
|
||||||
Ok::<_, ()>(|_| {
|
ok::<_, ()>(pipeline(|_| {
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||||
ok::<_, ()>(
|
ok::<_, ()>(
|
||||||
Response::Ok()
|
Response::Ok()
|
||||||
.header(http::header::CONTENT_TYPE, broken_header)
|
.header(http::header::CONTENT_TYPE, broken_header)
|
||||||
.body(STR),
|
.body(STR),
|
||||||
)
|
)
|
||||||
})
|
}))
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -593,7 +596,7 @@ fn test_h1_response_http_error_handling() {
|
||||||
fn test_h1_service_error() {
|
fn test_h1_service_error() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| Err::<Response, Error>(error::ErrorBadRequest("error")))
|
.h1(|_| future::err::<Response, Error>(error::ErrorBadRequest("error")))
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = srv.block_on(srv.get("/").send()).unwrap();
|
let response = srv.block_on(srv.get("/").send()).unwrap();
|
||||||
|
|
Loading…
Reference in a new issue