Skip to content
Snippets Groups Projects
Commit 40ff691c authored by Bartolomeo Berend Müller's avatar Bartolomeo Berend Müller
Browse files

WIP added minimal example to showcase usage of cquiche client

parent 5b028543
No related branches found
No related tags found
No related merge requests found
Showing
with 1849 additions and 0 deletions
OPENSSL_INSTALL=tmp/.local/openssl
build_with_pkg_config:
cd cquiche_s_timer && PKG_CONFIG_PATH=${OPENSSL_INSTALL}/lib64/pkgconfig LD_LIBRARY_PATH=${OPENSSL_INSTALL}/lib64 cargo run
ld_trace_loaded_objects:
LD_TRACE_LOADED_OBJECTS=1 ./your_executable
- need to add functionality to use choose the group for the key exchange with openssl and cquiche
- in line 1492 of ssl.h the function gets renamed `# define SSL_CTX_set1_groups_list(ctx, s) \
SSL_CTX_ctrl(ctx,SSL_CTRL_SET_GROUPS_LIST,0,(char *)(s))`
add to Context
```
// #[cfg(feature = "openssl")]
// pub fn set_groups(&mut self, groups: &str) -> Result<()> {
// let cstr = ffi::CString::new(groups).map_err(|_| Error::TlsFail)?;
// map_result(unsafe {
// SSL_CTX_set1_groups_list(self.as_mut_ptr(), cstr.as_ptr())
// })
// }
pub fn set_groups(&mut self, groups: &str) -> Result<()> {
let cstr = ffi::CString::new(groups).map_err(|_| Error::TlsFail)?;
map_result(unsafe {
SSL_CTX_ctrl(self.as_mut_ptr(), 92, 0,cstr.as_ptr())
})
}
// ........ and to extern functions
// fn SSL_CTX_set1_groups_list(
// ctx: *mut SSL_CTX, groups: *const c_char,
// ) -> c_int;
fn SSL_CTX_ctrl(ctx: *mut SSL_CTX, cmd: c_int, larg: i64, parg: *const c_char) -> c_int;
```
add to Config, call to context
```
/// Sets groups for TLS key exchange.
/// Own patch implementation
// #[cfg(feature = "openssl")]
pub fn set_groups(&mut self, groups: &str) -> Result<()> {
self.tls_ctx.set_groups(groups)
}
```
target/
[package]
name = "cquiche_s_timer"
version = "0.1.0"
edition = "2021"
[dependencies]
quiche = { path = "../tmp/quiche/quiche", features = ["openssl", "qlog"] }
mio = { version = "1.0", features = ["net", "os-poll"] }
log = "0.4"
url = "2.5"
ring = "0.17"
env_logger = "0.11"
// Copyright (C) 2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#[macro_use]
extern crate log;
use quiche::h3::NameValue;
use ring::rand::*;
// const MAX_DATAGRAM_SIZE: usize = 1350;
const MAX_DATAGRAM_SIZE: usize = 1200;
// PKG_CONFIG_PATH=${OPENSSL_INSTALL}/lib64/pkgconfig LD_LIBRARY_PATH=${OPENSSL_INSTALL}/lib64 cargo run -- https://localhost:8443/
fn main() {
env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.init();
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
let mut args = std::env::args();
let cmd = &args.next().unwrap();
if args.len() != 1 {
println!("Usage: {cmd} URL");
println!("\nSee tools/apps/ for more complete implementations.");
return;
}
let url = url::Url::parse(&args.next().unwrap()).unwrap();
// Setup the event loop.
let mut poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(1024);
// Resolve server address.
let peer_addr = url.socket_addrs(|| None).unwrap()[0];
// Bind to INADDR_ANY or IN6ADDR_ANY depending on the IP family of the
// server address. This is needed on macOS and BSD variants that don't
// support binding to IN6ADDR_ANY for both v4 and v6.
let bind_addr = match peer_addr {
std::net::SocketAddr::V4(_) => "0.0.0.0:0",
std::net::SocketAddr::V6(_) => "[::]:0",
};
// Create the UDP socket backing the QUIC connection, and register it with
// the event loop.
let mut socket = mio::net::UdpSocket::bind(bind_addr.parse().unwrap()).unwrap();
poll.registry()
.register(&mut socket, mio::Token(0), mio::Interest::READABLE)
.unwrap();
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
config.set_groups("mlkem512").unwrap();
config
.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
.unwrap();
config.verify_peer(true);
config
.load_verify_locations_from_file("../../tmp/.local/nginx/conf/CA.crt")
.unwrap();
// config.log_keys();
// config.discover_pmtu(true);
config.set_cc_algorithm(quiche::CongestionControlAlgorithm::CUBIC);
// config.set_cc_algorithm(quiche::CongestionControlAlgorithm::RENO);
// config.set_cc_algorithm(quiche::CongestionControlAlgorithm::BBR);
// config.set_cc_algorithm(quiche::CongestionControlAlgorithm::BBR2);
// TODO disable pacing?
// TODO read about every option and evaluate if default is good and comparable to openssl's impl
config.set_max_idle_timeout(30000);
config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_initial_max_data(10_000_000);
config.set_initial_max_stream_data_bidi_local(1_000_000);
config.set_initial_max_stream_data_bidi_remote(1_000_000);
config.set_initial_max_stream_data_uni(1_000_000);
config.set_initial_max_streams_bidi(100);
config.set_initial_max_streams_uni(100);
// config.set_disable_active_migration(true);
// Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
SystemRandom::new().fill(&mut scid[..]).unwrap();
let scid = quiche::ConnectionId::from_ref(&scid);
// Get local address.
let local_addr = socket.local_addr().unwrap();
// Create a QUIC connection and initiate handshake.
let mut conn =
quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config).unwrap();
// if handling of QLOGDIR env variable is wanted, then code should be written for it, there is no default
// see cquiche/tmp/quiche/apps/src/bin/quiche-client.rs or adjacent files for an example to copy
// title and description are just fields in the qlog file, they can be anything
let writer = Box::new(std::fs::File::create("qlog.sqlog").unwrap());
conn.set_qlog_with_level(
writer,
"qtitle".to_string(),
"qdescription".to_string(),
quiche::QlogLevel::Extra,
);
info!(
"connecting to {:} from {:} with scid {}",
peer_addr,
socket.local_addr().unwrap(),
hex_dump(&scid)
);
let (write, send_info) = conn.send(&mut out).expect("initial send failed");
while let Err(e) = socket.send_to(&out[..write], send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
continue;
}
panic!("send() failed: {:?}", e);
}
debug!("written {}", write);
let h3_config = quiche::h3::Config::new().unwrap();
// Prepare request.
let mut path = String::from(url.path());
if let Some(query) = url.query() {
path.push('?');
path.push_str(query);
}
let req = vec![
quiche::h3::Header::new(b":method", b"GET"),
quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()),
quiche::h3::Header::new(b":authority", url.host_str().unwrap().as_bytes()),
quiche::h3::Header::new(b":path", path.as_bytes()),
quiche::h3::Header::new(b"user-agent", b"quiche"),
];
let req_start = std::time::Instant::now();
let mut req_sent = false;
println!("here");
let mut http3_conn = None;
loop {
poll.poll(&mut events, conn.timeout()).unwrap();
// Read incoming UDP packets from the socket and feed them to quiche,
// until there are no more packets to read.
'read: loop {
// If the event loop reported no events, it means that the timeout
// has expired, so handle it without attempting to read packets. We
// will then proceed with the send loop.
if events.is_empty() {
debug!("timed out");
conn.on_timeout();
break 'read;
}
let (len, from) = match socket.recv_from(&mut buf) {
Ok(v) => v,
Err(e) => {
// There are no more UDP packets to read, so end the read
// loop.
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("recv() would block");
break 'read;
}
panic!("recv() failed: {:?}", e);
}
};
debug!("got {} bytes", len);
let recv_info = quiche::RecvInfo {
to: local_addr,
from,
};
// Process potentially coalesced packets.
let read = match conn.recv(&mut buf[..len], recv_info) {
Ok(v) => v,
Err(e) => {
error!("recv failed: {:?}", e);
continue 'read;
}
};
debug!("processed {} bytes", read);
}
debug!("done reading");
if conn.is_closed() {
info!("connection closed, {:?}", conn.stats());
break;
}
// Create a new HTTP/3 connection once the QUIC connection is established.
if conn.is_established() && http3_conn.is_none() {
http3_conn = Some(
quiche::h3::Connection::with_transport(&mut conn, &h3_config)
.expect("Unable to create HTTP/3 connection, check the server's uni stream limit and window size"),
);
}
// Send HTTP requests once the QUIC connection is established, and until
// all requests have been sent.
if let Some(h3_conn) = &mut http3_conn {
if !req_sent {
info!("sending HTTP request {:?}", req);
h3_conn.send_request(&mut conn, &req, true).unwrap();
req_sent = true;
}
}
if let Some(http3_conn) = &mut http3_conn {
// Process HTTP/3 events.
loop {
match http3_conn.poll(&mut conn) {
Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
info!(
"got response headers {:?} on stream id {}",
hdrs_to_strings(&list),
stream_id
);
}
Ok((stream_id, quiche::h3::Event::Data)) => {
while let Ok(read) = http3_conn.recv_body(&mut conn, stream_id, &mut buf) {
debug!(
"got {} bytes of response data on stream {}",
read, stream_id
);
print!("{}", unsafe { std::str::from_utf8_unchecked(&buf[..read]) });
}
}
Ok((_stream_id, quiche::h3::Event::Finished)) => {
info!("response received in {:?}, closing...", req_start.elapsed());
conn.close(true, 0x100, b"kthxbye").unwrap();
}
Ok((_stream_id, quiche::h3::Event::Reset(e))) => {
error!("request was reset by peer with {}, closing...", e);
conn.close(true, 0x100, b"kthxbye").unwrap();
}
Ok((_, quiche::h3::Event::PriorityUpdate)) => unreachable!(),
Ok((goaway_id, quiche::h3::Event::GoAway)) => {
info!("GOAWAY id={}", goaway_id);
}
Err(quiche::h3::Error::Done) => {
break;
}
Err(e) => {
error!("HTTP/3 processing failed: {:?}", e);
break;
}
}
}
}
// Generate outgoing QUIC packets and send them on the UDP socket, until
// quiche reports that there are no more packets to be sent.
loop {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,
Err(quiche::Error::Done) => {
debug!("done writing");
break;
}
Err(e) => {
error!("send failed: {:?}", e);
conn.close(false, 0x1, b"fail").ok();
break;
}
};
if let Err(e) = socket.send_to(&out[..write], send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
break;
}
panic!("send() failed: {:?}", e);
}
debug!("written {}", write);
}
if conn.is_closed() {
info!("connection closed, {:?}", conn.stats());
break;
}
}
}
fn hex_dump(buf: &[u8]) -> String {
let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
vec.join("")
}
pub fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> {
hdrs.iter()
.map(|h| {
let name = String::from_utf8_lossy(h.name()).to_string();
let value = String::from_utf8_lossy(h.value()).to_string();
(name, value)
})
.collect()
}
#!/bin/bash
set -ex
# Make sure to have a recent version of openssl installed by default. For example by having an up to date os version.
sudo apt update
sudo apt install -y git \
build-essential \
autoconf \
automake \
libtool \
ninja-build \
libssl-dev \
libpcre3-dev \
wget
NGINX_VERSION=1.26.1
CMAKE_VERSION=3.30
CMAKE_BUILD=0
mkdir -p tmp
cd tmp
ROOT=$(pwd)
INSTALL_DIR=${ROOT}/.local
OPENSSL_INSTALL=${ROOT}/.local/openssl
# Fetch all the files we need
wget https://cmake.org/files/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}.${CMAKE_BUILD}-linux-x86_64.sh
# git clone --no-checkout --single-branch --branch openssl-3.4 https://github.com/openssl/openssl.git
# (cd openssl && git switch --detach tags/openssl-3.4.0)
git clone --no-checkout --single-branch --branch openssl-3.3.0+quic https://github.com/quictls/openssl.git quictls/openssl
(cd quictls/openssl && git switch --detach openssl-3.3.0+quic)
git clone --recursive https://github.com/cloudflare/quiche
git clone --no-checkout --single-branch --branch 0.10.1-release https://github.com/open-quantum-safe/liboqs.git
(cd liboqs && git switch --detach tags/0.10.1)
git clone --no-checkout --single-branch --branch main https://github.com/open-quantum-safe/oqs-provider.git
(cd oqs-provider && git switch --detach tags/0.6.1)
wget nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && tar -zxvf nginx-${NGINX_VERSION}.tar.gz
# Install the latest CMake
mkdir -p ${INSTALL_DIR}/cmake
sh cmake-${CMAKE_VERSION}.${CMAKE_BUILD}-linux-x86_64.sh --skip-license --prefix=${INSTALL_DIR}/cmake
# # Patch openssl to have a large CRYPTO_RECV_BUF
# patch ${ROOT}/openssl/ssl/quic/quic_channel.c < ${ROOT}/../patches/openssl/quic_channel.c.patch
# Build OpenSSL so 'libcrypto.so' is avaiable for the build of liboqs. With Ubuntu 22.04 not longer needed.
(
cd quictls/openssl
# adds a runtime path to the executable, so it can find the libcrypto.so
LDFLAGS="-Wl,-rpath -Wl,${OPENSSL_INSTALL}/lib64" ./Configure --prefix=${OPENSSL_INSTALL} --openssldir=${OPENSSL_INSTALL}/ssl
# Commented out are the commands from https://github.com/open-quantum-safe/oqs-provider/blob/main/scripts/fullbuild.sh
# export OSSL_PREFIX=`pwd`/.local
# LDFLAGS="-Wl,-rpath -Wl,${OSSL_PREFIX}/lib64" ./config --prefix=$OSSL_PREFIX
make && make install_sw install_ssldirs
# NOTE maybe create a softlink if issue arises, something like
# ln -s lib64 lib
)
# build liboqs
(
cd liboqs
# It needs the libcrypto library, either in .a or .so format, in 'openssl' it is .so and in 'openssl-source' it is .a
# -- Found OpenSSL: /absolute-path-to/tmp/openssl/lib64/libcrypto.so (found suitable version "3.0.2", minimum required is "1.1.1")
# NOTE here the enabled algorithms could be specified
${INSTALL_DIR}/cmake/bin/cmake -GNinja -DOPENSSL_ROOT_DIR=${OPENSSL_INSTALL} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}/liboqs -S . -B build
cd build
ninja && ninja install
)
(
cd oqs-provider
liboqs_DIR=${INSTALL_DIR}/liboqs ${INSTALL_DIR}/cmake/bin/cmake -DOPENSSL_ROOT_DIR=${OPENSSL_INSTALL} -S . -B build && ${INSTALL_DIR}/cmake/bin/cmake --build build
# next command does not work, but is not needed maybe cuz we just copy the library???
# maybe use --install-prefix for next command
# cmake --install build --prefix ${ROOT}/oqs-provider/install
cp build/lib/oqsprovider.so ${OPENSSL_INSTALL}/lib64/ossl-modules/
# can also be installed to system
# sudo cp /home/bebbo/own/master/benchmarking-pqc-in-quic/pq-tls-benchmark-framework/emulation-exp/code/tmp/oqs-provider/build/lib/oqsprovider.so /lib/x86_64-linux-gnu/ossl-modules
)
sed -i "s/default = default_sect/default = default_sect\noqsprovider = oqsprovider_sect/g" ${OPENSSL_INSTALL}/ssl/openssl.cnf
sed -i "s/\[default_sect\]/\[default_sect\]\nactivate = 1\n\[oqsprovider_sect\]\nactivate = 1\n/g" ${OPENSSL_INSTALL}/ssl/openssl.cnf
${OPENSSL_INSTALL}/bin/openssl version -d
if [[ $(${OPENSSL_INSTALL}/bin/openssl version -d) != *"${OPENSSL_INSTALL}/ssl"* ]]; then
echo "The output of 'openssl version -d' does not include the string ${OPENSSL_INSTALL}/ssl"
exit 1
fi
if [[ $(${OPENSSL_INSTALL}/bin/openssl list -providers) != *"OpenSSL OQS Provider"* ]]; then
echo "The output of 'openssl list -providers' does not include the string 'OpenSSL OQS Provider'"
exit 1
fi
echo "Openssl seems to be installed correctly"
# build cquiche
(
cd quiche
# give cargo the context of the custom openssl (the pkgconfig file), so that it can find the correct libs
PKG_CONFIG_PATH=${OPENSSL_INSTALL}/lib64/pkgconfig cargo build --features=openssl
# how to find out if openssl is used or boringssl?
)
# # for nginx build
# # export OPENSSL_CONF=${OPENSSL_INSTALL}/ssl/openssl.cnf
# # apply patches to nginx source
# patch ${ROOT}/nginx-${NGINX_VERSION}/src/event/quic/ngx_event_quic_openssl_compat.c < ${ROOT}/../patches/nginx-${NGINX_VERSION}-patches/ngx_event_quic_openssl_compat.c.diff
# # build nginx with dynamically linked custom openssl
# # NOTE it may be that the first time nginx is built it is not linked correctly to the custom openssl, but just do it again, it should work
# (
# cd nginx-${NGINX_VERSION}
# # NOTE why --without-http_gzip_module
# # -rpath with --enable-new-dtags makes it use RUNPATH which is evaluated after LD_LIBRARY_PATH (which is evaluated after RPATH)
# # export LD_LIBRARY_PATH=${OPENSSL_INSTALL}/lib64:${LD_LIBRARY_PATH}
# ./configure --prefix=${INSTALL_DIR}/nginx \
# --with-debug \
# --with-http_v2_module \
# --with-http_v3_module \
# --without-http_gzip_module \
# --with-http_ssl_module \
# --with-ld-opt="-Wl,--enable-new-dtags,-rpath,${OPENSSL_INSTALL}/lib64"
# # The --with-openssl builds openssl again, but we just want to link against it
# # --with-openssl=${ROOT}/openssl \
# # --with-cc-opt="-I ${OPENSSL_INSTALL}/include/openssl" \
# # --with-ld-opt="-L ${OPENSSL_INSTALL}/lib64"
# # sed -i 's/libcrypto.a/libcrypto.a -loqs/g' objs/Makefile;
# # NOTE why change this?
# # sed -i 's/EVP_MD_CTX_create/EVP_MD_CTX_new/g; s/EVP_MD_CTX_destroy/EVP_MD_CTX_free/g' src/event/ngx_event_openssl.c
# make && make install
# )
# # NOTE check with nginx -V and ldd if the openssl is linked correctly
# # it should show the path to the custom openssl
# # This only shows the correct path (if RUNPATH is not used), if the LD_LIBRARY_PATH env var is set correctly
# ${INSTALL_DIR}/nginx/sbin/nginx -V
# ldd ${INSTALL_DIR}/nginx/sbin/nginx
# readelf -d ${INSTALL_DIR}/nginx/sbin/nginx | grep 'R.*PATH'
# echo "You should see that nginx is linked against the custom openssl in tmp/.local/openssl"
...@@ -28,6 +28,7 @@ OPENSSL_INSTALL=${ROOT}/.local/openssl ...@@ -28,6 +28,7 @@ OPENSSL_INSTALL=${ROOT}/.local/openssl
wget https://cmake.org/files/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}.${CMAKE_BUILD}-linux-x86_64.sh wget https://cmake.org/files/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}.${CMAKE_BUILD}-linux-x86_64.sh
git clone --no-checkout --single-branch --branch openssl-3.4 https://github.com/openssl/openssl.git git clone --no-checkout --single-branch --branch openssl-3.4 https://github.com/openssl/openssl.git
(cd openssl && git switch --detach tags/openssl-3.4.0) (cd openssl && git switch --detach tags/openssl-3.4.0)
# 0.11 was released, maybe switch to it when running experiment again
git clone --no-checkout --single-branch --branch 0.10.1-release https://github.com/open-quantum-safe/liboqs.git git clone --no-checkout --single-branch --branch 0.10.1-release https://github.com/open-quantum-safe/liboqs.git
(cd liboqs && git switch --detach tags/0.10.1) (cd liboqs && git switch --detach tags/0.10.1)
git clone --no-checkout --single-branch --branch main https://github.com/open-quantum-safe/oqs-provider.git git clone --no-checkout --single-branch --branch main https://github.com/open-quantum-safe/oqs-provider.git
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment