Skip to content

Commit

Permalink
Implement QUIC support (#8)
Browse files Browse the repository at this point in the history
* add quic support

* test using rustls provider tests
  • Loading branch information
tofay authored Nov 11, 2024
1 parent fd06cbd commit f121181
Show file tree
Hide file tree
Showing 17 changed files with 832 additions and 349 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,28 @@ jobs:
run: cargo fmt -- --check -l
- name: cargo clippy (warnings)
run: cargo clippy --all-targets --all-features -- -D warnings
rustls_provider_tests:
name: Test using rustls provider
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
path: rustls-openssl
- name: Check out repository
uses: actions/checkout@v4
with:
repository: rustls/rustls
path: rustls
- name: Install toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- name: Cache build artifacts
uses: Swatinem/rust-cache@v2
- name: Patch rustls
run: cd rustls && ../rustls-openssl/tests/patch.sh
- name: find
run: find .
- name: cargo test test_with_openssl
run: cd rustls && cargo test test_with_openssl
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Supported cipher suites are listed below, in descending order of preference.

### TLS 1.3

The following cipher suites are supported for TLS 1.3. These support QUIC.

```
TLS13_AES_256_GCM_SHA384
TLS13_AES_128_GCM_SHA256
Expand All @@ -40,3 +42,8 @@ SECP384R1
SECP256R1
X25519 // Requires the `x25519` feature
```

# Tests

In addition to the tests in this repo, CI also runs rustls tests that run against all providers.
This is done by patching this repo as a module into a rustls checkout repo, hence this repo mirroring the `rustls::crypto::<provider>` module structure.
54 changes: 28 additions & 26 deletions src/aead.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
use openssl::{cipher::CipherRef, cipher_ctx::CipherCtx};
use rustls::crypto::cipher::{AeadKey, Iv, Nonce};
use alloc::format;
use openssl::{
cipher::{Cipher, CipherRef},
cipher_ctx::CipherCtx,
};
use rustls::{
crypto::cipher::{AeadKey, Iv, Nonce},
Error,
};

pub(crate) struct AeadMessageCrypter {
pub algo: AeadAlgorithm,
pub(crate) struct MessageCrypter {
pub algo: Algorithm,
pub key: AeadKey,
pub iv: Iv,
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum AeadAlgorithm {
pub(crate) enum Algorithm {
Aes128Gcm,
Aes256Gcm,
#[cfg(feature = "chacha")]
ChaCha20Poly1305,
}

impl AeadAlgorithm {
pub(crate) fn openssl_cipher(&self) -> &'static CipherRef {
match self {
AeadAlgorithm::Aes128Gcm => openssl::cipher::Cipher::aes_128_gcm(),
AeadAlgorithm::Aes256Gcm => openssl::cipher::Cipher::aes_256_gcm(),
#[cfg(feature = "chacha")]
AeadAlgorithm::ChaCha20Poly1305 => openssl::cipher::Cipher::chacha20_poly1305(),
}
}
/// The tag length is 16 bytes for all supported ciphers.
pub(crate) const TAG_LEN: usize = 16;

pub(crate) fn tag_len(&self) -> usize {
impl Algorithm {
pub(crate) fn openssl_cipher(self) -> &'static CipherRef {
match self {
AeadAlgorithm::Aes128Gcm | AeadAlgorithm::Aes256Gcm => 16,
Self::Aes128Gcm => Cipher::aes_128_gcm(),
Self::Aes256Gcm => Cipher::aes_256_gcm(),
#[cfg(feature = "chacha")]
AeadAlgorithm::ChaCha20Poly1305 => 16,
Self::ChaCha20Poly1305 => Cipher::chacha20_poly1305(),
}
}
}

impl AeadMessageCrypter {
impl MessageCrypter {
/// Encrypts the data in place and returns the tag.
pub(crate) fn encrypt_in_place(
&self,
sequence_number: u64,
aad: &[u8],
data: &mut [u8],
) -> Result<Vec<u8>, rustls::Error> {
) -> Result<[u8; TAG_LEN], Error> {
CipherCtx::new()
.and_then(|mut ctx| {
ctx.encrypt_init(
Expand All @@ -57,11 +59,11 @@ impl AeadMessageCrypter {
// ... and no more data should be written at the end.
let rest = ctx.cipher_final(&mut [])?;
debug_assert!(rest == 0);
let mut tag = vec![0u8; self.algo.tag_len()];
let mut tag = [0u8; TAG_LEN];
ctx.tag(&mut tag)?;
Ok(tag)
})
.map_err(|e| rustls::Error::General(format!("OpeenSSL error: {}", e)))
.map_err(|e| Error::General(format!("OpenSSL error: {e}")))
}

/// Decrypts in place, verifying the tag and returns the length of the
Expand All @@ -72,13 +74,13 @@ impl AeadMessageCrypter {
sequence_number: u64,
aad: &[u8],
data: &mut [u8],
) -> Result<usize, rustls::Error> {
) -> Result<usize, Error> {
let payload_len = data.len();
if payload_len < self.algo.tag_len() {
return Err(rustls::Error::DecryptError);
if payload_len < TAG_LEN {
return Err(Error::DecryptError);
}

let (ciphertext, tag) = data.split_at_mut(payload_len - self.algo.tag_len());
let (ciphertext, tag) = data.split_at_mut(payload_len - TAG_LEN);

CipherCtx::new()
.and_then(|mut ctx| {
Expand All @@ -95,6 +97,6 @@ impl AeadMessageCrypter {
debug_assert!(rest == 0);
Ok(count + rest)
})
.map_err(|e| rustls::Error::General(format!("OpenSSL error: {}", e)))
.map_err(|e| Error::General(format!("OpenSSL error: {e}")))
}
}
155 changes: 0 additions & 155 deletions src/cipher_suites.rs

This file was deleted.

30 changes: 15 additions & 15 deletions src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
//! Provide Rustls `Hash` implementation using OpenSSL `MessageDigest`.
use alloc::boxed::Box;
use openssl::{
hash::MessageDigest,
sha::{self, sha256, sha384},
};
use rustls::crypto::hash::{Context, Hash, Output};
use rustls::crypto::hash::{Context, Output};

pub(crate) static SHA256: Hash = Hash(HashAlgorithm::SHA256);
pub(crate) static SHA384: Hash = Hash(HashAlgorithm::SHA384);
pub(crate) struct Hash(HashAlgorithm);

pub(crate) enum HashAlgorithm {
SHA256,
SHA384,
}

impl HashAlgorithm {
pub fn message_digest(&self) -> MessageDigest {
match &self {
HashAlgorithm::SHA256 => MessageDigest::sha256(),
HashAlgorithm::SHA384 => MessageDigest::sha384(),
}
}
}

impl Hash for HashAlgorithm {
impl rustls::crypto::hash::Hash for Hash {
fn start(&self) -> Box<dyn Context> {
match &self {
match &self.0 {
HashAlgorithm::SHA256 => Box::new(Sha256Context(sha::Sha256::new())),
HashAlgorithm::SHA384 => Box::new(Sha384Context(sha::Sha384::new())),
}
}

fn hash(&self, data: &[u8]) -> Output {
match &self {
match &self.0 {
HashAlgorithm::SHA256 => Output::new(&sha256(data)[..]),
HashAlgorithm::SHA384 => Output::new(&sha384(data)[..]),
}
}

fn output_len(&self) -> usize {
self.message_digest().size()
let md = match &self.0 {
HashAlgorithm::SHA256 => MessageDigest::sha256(),
HashAlgorithm::SHA384 => MessageDigest::sha384(),
};
md.size()
}

fn algorithm(&self) -> rustls::crypto::hash::HashAlgorithm {
match &self {
match &self.0 {
HashAlgorithm::SHA256 => rustls::crypto::hash::HashAlgorithm::SHA256,
HashAlgorithm::SHA384 => rustls::crypto::hash::HashAlgorithm::SHA384,
}
Expand Down
9 changes: 5 additions & 4 deletions src/hmac.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use alloc::boxed::Box;
use openssl::{
hash::MessageDigest,
pkey::{PKey, Private},
sign::Signer,
};
use rustls::crypto::hmac::{Hmac, Key, Tag};

pub struct HmacSha256;
pub struct HmacSha256Key(PKey<Private>);
pub(crate) struct HmacSha256;
pub(crate) struct HmacSha256Key(PKey<Private>);

pub struct HmacSha384;
pub struct HmacSha384Key(PKey<Private>);
pub(crate) struct HmacSha384;
pub(crate) struct HmacSha384Key(PKey<Private>);

impl Hmac for HmacSha256 {
fn with_key(&self, key: &[u8]) -> Box<dyn Key> {
Expand Down
Loading

0 comments on commit f121181

Please sign in to comment.