RSA tests and fix chacha20 in tls 1.2
This commit is contained in:
parent
624eda8168
commit
7d7b1b3f99
6 changed files with 108 additions and 65 deletions
|
|
@ -17,7 +17,7 @@ AES_256_GCM_SHA384
|
||||||
CHACHA20_POLY1305_SHA256
|
CHACHA20_POLY1305_SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
TLS 1.2 (only ECDSA is tested):
|
TLS 1.2:
|
||||||
```
|
```
|
||||||
ECDHE_ECDSA_AES128_GCM_SHA256
|
ECDHE_ECDSA_AES128_GCM_SHA256
|
||||||
ECDHE_RSA_AES128_GCM_SHA256
|
ECDHE_RSA_AES128_GCM_SHA256
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,19 @@ pub(crate) mod aes;
|
||||||
pub(crate) mod chacha20;
|
pub(crate) mod chacha20;
|
||||||
|
|
||||||
pub(crate) trait BoringCipher {
|
pub(crate) trait BoringCipher {
|
||||||
/// Constructs a new instance of this cipher as an AEAD algorithm
|
/// The lengths of the explicit nonce. (Not the full nonce length, only the part that changes)
|
||||||
fn new_cipher() -> Algorithm;
|
/// See also [`BoringCipher::fixed_iv_len`]
|
||||||
/// The key size in bytes
|
const EXPLICIT_NONCE_LEN: usize;
|
||||||
fn key_size() -> usize;
|
|
||||||
/// The IV's fixed length (Not the full IV length, only the part that doesn't change).
|
/// The IV's fixed length (Not the full IV length, only the part that doesn't change).
|
||||||
/// Together with [`BoringCipher::explicit_nonce_len`] it determines the total
|
/// Together with [`BoringCipher::explicit_nonce_len`] it determines the total
|
||||||
/// lengths of the used nonce.
|
/// lengths of the used nonce.
|
||||||
fn fixed_iv_len() -> usize;
|
const FIXED_IV_LEN: usize;
|
||||||
/// The lengths of the explicit nonce. (Not the full nonce length, only the part that changes)
|
/// The key size in bytes
|
||||||
/// See also [`BoringCipher::fixed_iv_len`]
|
const KEY_SIZE: usize;
|
||||||
fn explicit_nonce_len() -> usize;
|
|
||||||
|
/// Constructs a new instance of this cipher as an AEAD algorithm
|
||||||
|
fn new_cipher() -> Algorithm;
|
||||||
|
|
||||||
/// Extract keys
|
/// Extract keys
|
||||||
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets;
|
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets;
|
||||||
}
|
}
|
||||||
|
|
@ -116,8 +118,8 @@ where
|
||||||
match self.tls_version {
|
match self.tls_version {
|
||||||
#[cfg(feature = "tls12")]
|
#[cfg(feature = "tls12")]
|
||||||
ProtocolVersion::TLSv1_2 => {
|
ProtocolVersion::TLSv1_2 => {
|
||||||
let fixed_iv_len = <T as BoringCipher>::fixed_iv_len();
|
let fixed_iv_len = <T as BoringCipher>::FIXED_IV_LEN;
|
||||||
let explicit_nonce_len = <T as BoringCipher>::explicit_nonce_len();
|
let explicit_nonce_len = <T as BoringCipher>::EXPLICIT_NONCE_LEN;
|
||||||
|
|
||||||
let total_len =
|
let total_len =
|
||||||
msg.payload.len() + self.crypter.max_overhead() + explicit_nonce_len;
|
msg.payload.len() + self.crypter.max_overhead() + explicit_nonce_len;
|
||||||
|
|
@ -173,13 +175,10 @@ where
|
||||||
mut m: cipher::OpaqueMessage,
|
mut m: cipher::OpaqueMessage,
|
||||||
seq: u64,
|
seq: u64,
|
||||||
) -> Result<cipher::PlainMessage, rustls::Error> {
|
) -> Result<cipher::PlainMessage, rustls::Error> {
|
||||||
// construct nonce
|
|
||||||
|
|
||||||
// construct the aad and decrypt
|
|
||||||
match self.tls_version {
|
match self.tls_version {
|
||||||
#[cfg(feature = "tls12")]
|
#[cfg(feature = "tls12")]
|
||||||
ProtocolVersion::TLSv1_2 => {
|
ProtocolVersion::TLSv1_2 => {
|
||||||
let explicit_nonce_len = <T as BoringCipher>::explicit_nonce_len();
|
let explicit_nonce_len = <T as BoringCipher>::EXPLICIT_NONCE_LEN;
|
||||||
|
|
||||||
// payload is: [nonce] | [ciphertext] | [auth tag]
|
// payload is: [nonce] | [ciphertext] | [auth tag]
|
||||||
let actual_payload_length =
|
let actual_payload_length =
|
||||||
|
|
@ -193,12 +192,12 @@ where
|
||||||
let (explicit_nonce, payload) = payload.split_at_mut(explicit_nonce_len);
|
let (explicit_nonce, payload) = payload.split_at_mut(explicit_nonce_len);
|
||||||
|
|
||||||
let nonce = {
|
let nonce = {
|
||||||
let fixed_iv_len = <T as BoringCipher>::fixed_iv_len();
|
let fixed_iv_len = <T as BoringCipher>::FIXED_IV_LEN;
|
||||||
|
|
||||||
assert_eq!(explicit_nonce_len + fixed_iv_len, 12);
|
assert_eq!(explicit_nonce_len + fixed_iv_len, 12);
|
||||||
|
|
||||||
// grab the IV by constructing a nonce, this is just an xor
|
// grab the IV by constructing a nonce, this is just an xor
|
||||||
let iv = cipher::Nonce::new(&self.iv, 0).0;
|
let iv = cipher::Nonce::new(&self.iv, seq).0;
|
||||||
let mut nonce = [0u8; 12];
|
let mut nonce = [0u8; 12];
|
||||||
nonce[..fixed_iv_len].copy_from_slice(&iv[..fixed_iv_len]);
|
nonce[..fixed_iv_len].copy_from_slice(&iv[..fixed_iv_len]);
|
||||||
nonce[fixed_iv_len..].copy_from_slice(explicit_nonce);
|
nonce[fixed_iv_len..].copy_from_slice(explicit_nonce);
|
||||||
|
|
@ -211,7 +210,7 @@ where
|
||||||
|
|
||||||
self.crypter
|
self.crypter
|
||||||
.open_in_place(&nonce, &aad, payload, tag)
|
.open_in_place(&nonce, &aad, payload, tag)
|
||||||
.map_err(|_| rustls::Error::DecryptError)
|
.map_err(|e| log_and_map("open_in_place", e, rustls::Error::DecryptError))
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
// rotate the nonce to the end
|
// rotate the nonce to the end
|
||||||
m.payload_mut().rotate_left(explicit_nonce_len);
|
m.payload_mut().rotate_left(explicit_nonce_len);
|
||||||
|
|
@ -256,7 +255,7 @@ impl<T: BoringAead + 'static> cipher::Tls13AeadAlgorithm for Aead<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_len(&self) -> usize {
|
fn key_len(&self) -> usize {
|
||||||
<T as BoringCipher>::key_size()
|
<T as BoringCipher>::KEY_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_keys(
|
fn extract_keys(
|
||||||
|
|
@ -286,10 +285,9 @@ impl<T: BoringAead + 'static> cipher::Tls12AeadAlgorithm for Aead<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypter(&self, key: cipher::AeadKey, iv: &[u8]) -> Box<dyn cipher::MessageDecrypter> {
|
fn decrypter(&self, key: cipher::AeadKey, iv: &[u8]) -> Box<dyn cipher::MessageDecrypter> {
|
||||||
let mut pseudo_iv =
|
let mut pseudo_iv = Vec::with_capacity(iv.len() + <T as BoringCipher>::EXPLICIT_NONCE_LEN);
|
||||||
Vec::with_capacity(iv.len() + <T as BoringCipher>::explicit_nonce_len());
|
|
||||||
pseudo_iv.extend_from_slice(iv);
|
pseudo_iv.extend_from_slice(iv);
|
||||||
pseudo_iv.extend_from_slice(&vec![0u8; <T as BoringCipher>::explicit_nonce_len()]);
|
pseudo_iv.extend_from_slice(&vec![0u8; <T as BoringCipher>::EXPLICIT_NONCE_LEN]);
|
||||||
Box::new(
|
Box::new(
|
||||||
BoringAeadCrypter::<T>::new(
|
BoringAeadCrypter::<T>::new(
|
||||||
Iv::copy(&pseudo_iv),
|
Iv::copy(&pseudo_iv),
|
||||||
|
|
@ -302,11 +300,9 @@ impl<T: BoringAead + 'static> cipher::Tls12AeadAlgorithm for Aead<T> {
|
||||||
|
|
||||||
fn key_block_shape(&self) -> cipher::KeyBlockShape {
|
fn key_block_shape(&self) -> cipher::KeyBlockShape {
|
||||||
cipher::KeyBlockShape {
|
cipher::KeyBlockShape {
|
||||||
enc_key_len: <T as BoringCipher>::key_size(),
|
enc_key_len: <T as BoringCipher>::KEY_SIZE,
|
||||||
// there is no benefit of splitting these up here, we'd need to stich them anyways
|
fixed_iv_len: <T as BoringCipher>::FIXED_IV_LEN,
|
||||||
// by only setting fixed_iv_len we get the full lengths
|
explicit_nonce_len: <T as BoringCipher>::EXPLICIT_NONCE_LEN,
|
||||||
fixed_iv_len: <T as BoringCipher>::fixed_iv_len(),
|
|
||||||
explicit_nonce_len: <T as BoringCipher>::explicit_nonce_len(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,8 +313,8 @@ impl<T: BoringAead + 'static> cipher::Tls12AeadAlgorithm for Aead<T> {
|
||||||
explicit: &[u8],
|
explicit: &[u8],
|
||||||
) -> Result<ConnectionTrafficSecrets, cipher::UnsupportedOperationError> {
|
) -> Result<ConnectionTrafficSecrets, cipher::UnsupportedOperationError> {
|
||||||
let nonce = {
|
let nonce = {
|
||||||
let fixed_iv_len = <T as BoringCipher>::fixed_iv_len();
|
let fixed_iv_len = <T as BoringCipher>::FIXED_IV_LEN;
|
||||||
let explicit_nonce_len = <T as BoringCipher>::explicit_nonce_len();
|
let explicit_nonce_len = <T as BoringCipher>::EXPLICIT_NONCE_LEN;
|
||||||
assert_eq!(explicit_nonce_len + fixed_iv_len, 12);
|
assert_eq!(explicit_nonce_len + fixed_iv_len, 12);
|
||||||
|
|
||||||
// grab the IV by constructing a nonce, this is just an xor
|
// grab the IV by constructing a nonce, this is just an xor
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,19 @@ pub struct Aes128 {}
|
||||||
impl BoringAead for Aes128 {}
|
impl BoringAead for Aes128 {}
|
||||||
|
|
||||||
impl BoringCipher for Aes128 {
|
impl BoringCipher for Aes128 {
|
||||||
|
const EXPLICIT_NONCE_LEN: usize = 8;
|
||||||
|
|
||||||
|
const FIXED_IV_LEN: usize = 4;
|
||||||
|
|
||||||
|
const KEY_SIZE: usize = 16;
|
||||||
|
|
||||||
fn new_cipher() -> Algorithm {
|
fn new_cipher() -> Algorithm {
|
||||||
Algorithm::aes_128_gcm()
|
Algorithm::aes_128_gcm()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_size() -> usize {
|
|
||||||
16
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
||||||
ConnectionTrafficSecrets::Aes128Gcm { key, iv }
|
ConnectionTrafficSecrets::Aes128Gcm { key, iv }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixed_iv_len() -> usize {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn explicit_nonce_len() -> usize {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl aead::AeadCore for Aes128 {
|
impl aead::AeadCore for Aes128 {
|
||||||
|
|
@ -42,25 +36,19 @@ pub struct Aes256 {}
|
||||||
impl BoringAead for Aes256 {}
|
impl BoringAead for Aes256 {}
|
||||||
|
|
||||||
impl BoringCipher for Aes256 {
|
impl BoringCipher for Aes256 {
|
||||||
|
const EXPLICIT_NONCE_LEN: usize = 8;
|
||||||
|
|
||||||
|
const FIXED_IV_LEN: usize = 4;
|
||||||
|
|
||||||
|
const KEY_SIZE: usize = 32;
|
||||||
|
|
||||||
fn new_cipher() -> Algorithm {
|
fn new_cipher() -> Algorithm {
|
||||||
Algorithm::aes_256_gcm()
|
Algorithm::aes_256_gcm()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_size() -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
||||||
ConnectionTrafficSecrets::Aes256Gcm { key, iv }
|
ConnectionTrafficSecrets::Aes256Gcm { key, iv }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixed_iv_len() -> usize {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn explicit_nonce_len() -> usize {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl aead::AeadCore for Aes256 {
|
impl aead::AeadCore for Aes256 {
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,19 @@ pub struct ChaCha20Poly1305 {}
|
||||||
impl BoringAead for ChaCha20Poly1305 {}
|
impl BoringAead for ChaCha20Poly1305 {}
|
||||||
|
|
||||||
impl BoringCipher for ChaCha20Poly1305 {
|
impl BoringCipher for ChaCha20Poly1305 {
|
||||||
|
const EXPLICIT_NONCE_LEN: usize = 0;
|
||||||
|
|
||||||
|
const FIXED_IV_LEN: usize = 12;
|
||||||
|
|
||||||
|
const KEY_SIZE: usize = 32;
|
||||||
|
|
||||||
fn new_cipher() -> Algorithm {
|
fn new_cipher() -> Algorithm {
|
||||||
Algorithm::chacha20_poly1305()
|
Algorithm::chacha20_poly1305()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_size() -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
fn extract_keys(key: cipher::AeadKey, iv: cipher::Iv) -> ConnectionTrafficSecrets {
|
||||||
ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }
|
ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixed_iv_len() -> usize {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn explicit_nonce_len() -> usize {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AeadCore for ChaCha20Poly1305 {
|
impl AeadCore for ChaCha20Poly1305 {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn log_and_map<E: core::fmt::Display, T>(func: &'static str, e: E, mapped: T) -> T {
|
pub(crate) fn log_and_map<E: core::fmt::Display, T>(func: &'static str, e: E, mapped: T) -> T {
|
||||||
trace!("failed {}, error: {}", func, e);
|
trace!("failed {}, error: {}", func, e);
|
||||||
mapped
|
mapped
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use rcgen::CertificateParams;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncReadExt, AsyncWriteExt},
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
|
@ -95,6 +96,47 @@ async fn test_tls12_ec_crypto() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_tls12_rsa_crypto() {
|
||||||
|
let pki = TestPki::new(&rcgen::PKCS_RSA_SHA256);
|
||||||
|
|
||||||
|
let root_store = pki.client_root_store();
|
||||||
|
let server_config = pki.server_config();
|
||||||
|
|
||||||
|
let ciphers = [
|
||||||
|
SupportedCipherSuite::Tls12(&tls12::ECDHE_RSA_AES128_GCM_SHA256),
|
||||||
|
SupportedCipherSuite::Tls12(&tls12::ECDHE_RSA_AES256_GCM_SHA384),
|
||||||
|
SupportedCipherSuite::Tls12(&tls12::ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||||
|
];
|
||||||
|
|
||||||
|
for cipher in ciphers {
|
||||||
|
let config = rustls::ClientConfig::builder_with_provider(PROVIDER)
|
||||||
|
.with_cipher_suites(&[cipher])
|
||||||
|
.with_safe_default_kx_groups()
|
||||||
|
.with_protocol_versions(&[&TLS12])
|
||||||
|
.unwrap()
|
||||||
|
.with_root_certificates(root_store.clone())
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
let listener = new_listener().await;
|
||||||
|
let addr = listener.local_addr().unwrap();
|
||||||
|
tokio::spawn(spawn_echo_server(listener, server_config.clone()));
|
||||||
|
|
||||||
|
let connector = TlsConnector::from(Arc::new(config));
|
||||||
|
let stream = TcpStream::connect(&addr).await.unwrap();
|
||||||
|
|
||||||
|
let mut stream = connector
|
||||||
|
.connect(rustls::ServerName::try_from("localhost").unwrap(), stream)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
stream.write_all(b"HELLO").await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let bytes = stream.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(&buf[..bytes], b"HELLO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn new_listener() -> TcpListener {
|
async fn new_listener() -> TcpListener {
|
||||||
TcpListener::bind("localhost:0").await.unwrap()
|
TcpListener::bind("localhost:0").await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +175,7 @@ impl TestPki {
|
||||||
rcgen::KeyUsagePurpose::KeyCertSign,
|
rcgen::KeyUsagePurpose::KeyCertSign,
|
||||||
rcgen::KeyUsagePurpose::DigitalSignature,
|
rcgen::KeyUsagePurpose::DigitalSignature,
|
||||||
];
|
];
|
||||||
|
keypair_for_alg(&mut ca_params, alg);
|
||||||
ca_params.alg = alg;
|
ca_params.alg = alg;
|
||||||
let ca_cert = rcgen::Certificate::from_params(ca_params).unwrap();
|
let ca_cert = rcgen::Certificate::from_params(ca_params).unwrap();
|
||||||
|
|
||||||
|
|
@ -142,6 +185,7 @@ impl TestPki {
|
||||||
server_ee_params.is_ca = rcgen::IsCa::NoCa;
|
server_ee_params.is_ca = rcgen::IsCa::NoCa;
|
||||||
server_ee_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
|
server_ee_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
|
||||||
server_ee_params.alg = alg;
|
server_ee_params.alg = alg;
|
||||||
|
keypair_for_alg(&mut server_ee_params, alg);
|
||||||
let server_cert = rcgen::Certificate::from_params(server_ee_params).unwrap();
|
let server_cert = rcgen::Certificate::from_params(server_ee_params).unwrap();
|
||||||
let server_cert_der =
|
let server_cert_der =
|
||||||
CertificateDer::from(server_cert.serialize_der_with_signer(&ca_cert).unwrap());
|
CertificateDer::from(server_cert.serialize_der_with_signer(&ca_cert).unwrap());
|
||||||
|
|
@ -172,3 +216,23 @@ impl TestPki {
|
||||||
root_store
|
root_store
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_rsa_key(bits: u32) -> rcgen::KeyPair {
|
||||||
|
let rsa = boring::rsa::Rsa::generate(bits).unwrap();
|
||||||
|
|
||||||
|
let der_pkcs8 = boring::pkey::PKey::from_rsa(rsa)
|
||||||
|
.unwrap()
|
||||||
|
.private_key_to_der_pkcs8()
|
||||||
|
.unwrap();
|
||||||
|
rcgen::KeyPair::from_der(&der_pkcs8).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keypair_for_alg(params: &mut CertificateParams, alg: &rcgen::SignatureAlgorithm) {
|
||||||
|
if alg == &rcgen::PKCS_RSA_SHA256 {
|
||||||
|
params.key_pair = Some(gen_rsa_key(2048));
|
||||||
|
} else if alg == &rcgen::PKCS_RSA_SHA384 {
|
||||||
|
params.key_pair = Some(gen_rsa_key(3072));
|
||||||
|
} else if alg == &rcgen::PKCS_RSA_SHA512 {
|
||||||
|
params.key_pair = Some(gen_rsa_key(4096));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue