diff --git a/Cargo.lock b/Cargo.lock index c3393d5..2bd3107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -262,6 +268,19 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.5" @@ -422,6 +441,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -529,6 +554,7 @@ dependencies = [ "rmp-serde", "serde", "serde_arrays", + "serial_test", "sha2", "simple_logger", "tcp-test", @@ -551,6 +577,16 @@ dependencies = [ "cc", ] +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -638,6 +674,29 @@ dependencies = [ "libm", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + [[package]] name = "paste" version = "1.0.12" @@ -747,6 +806,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "rmp" version = "0.8.11" @@ -769,6 +837,12 @@ dependencies = [ "serde", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "scratch" version = "1.0.5" @@ -804,6 +878,31 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "sha2" version = "0.10.6" @@ -843,6 +942,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" version = "0.4.9" diff --git a/libepf/Cargo.toml b/libepf/Cargo.toml index d2b49e8..90bc144 100644 --- a/libepf/Cargo.toml +++ b/libepf/Cargo.toml @@ -26,4 +26,5 @@ tokio = { version = "1.28", features = ["io-util", "macros", "rt-multi-thread", tcp-test = "0.1" futures = "0.3" simple_logger = "4.1" -hex-literal = "0.4.1" \ No newline at end of file +hex-literal = "0.4.1" +serial_test = "2.0.0" \ No newline at end of file diff --git a/libepf/src/ca_pool.rs b/libepf/src/ca_pool.rs index 527abc2..e927224 100644 --- a/libepf/src/ca_pool.rs +++ b/libepf/src/ca_pool.rs @@ -50,6 +50,7 @@ impl Display for EpfCaPoolLoaderError { impl Error for EpfCaPoolLoaderError {} #[cfg(unix)] +#[cfg(not(tarpaulin))] pub fn load_ca_pool() -> Result> { let mut cert_strings = vec![]; @@ -68,3 +69,13 @@ pub fn load_ca_pool() -> Result> { Ok(ca_pool) } + +#[cfg(test)] +mod tests { + use crate::ca_pool::EpfCaPoolLoaderError; + + #[test] + pub fn ca_pool_error_display_test() { + println!("{}", EpfCaPoolLoaderError::CertDirDoesNotExist("".to_string())); + } +} \ No newline at end of file diff --git a/libepf/src/error.rs b/libepf/src/error.rs index c2f829e..9b1ba59 100644 --- a/libepf/src/error.rs +++ b/libepf/src/error.rs @@ -28,3 +28,19 @@ impl Display for EpfHandshakeError { } } impl Error for EpfHandshakeError {} + +#[cfg(test)] +mod tests { + use crate::error::EpfHandshakeError; + use crate::pki::EpfPkiCertificateValidationError; + + #[test] + pub fn error_display_test() { + println!("{}", EpfHandshakeError::AlreadyTunnelled); + println!("{}", EpfHandshakeError::UnsupportedProtocolVersion(0)); + println!("{}", EpfHandshakeError::InvalidCertificate(EpfPkiCertificateValidationError::ValidAfterSigner)); + println!("{}", EpfHandshakeError::UntrustedCertificate); + println!("{}", EpfHandshakeError::EncryptionError); + println!("{}", EpfHandshakeError::MissingKeyProof); + } +} \ No newline at end of file diff --git a/libepf/src/handshake_stream.rs b/libepf/src/handshake_stream.rs index ccb47e4..2954846 100644 --- a/libepf/src/handshake_stream.rs +++ b/libepf/src/handshake_stream.rs @@ -707,15 +707,16 @@ mod tests { use std::net::SocketAddr; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; - - use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use serial_test::serial; + use tokio::join; use tokio::net::{TcpListener, TcpSocket, TcpStream}; use x25519_dalek::{PublicKey, StaticSecret}; #[tokio::test] + #[serial] pub async fn stream_test() { - simple_logger::init().unwrap(); + //simple_logger::init().unwrap(); let tcp_listener = TcpListener::bind("0.0.0.0:36116").await.unwrap(); @@ -787,6 +788,82 @@ mod tests { let mut s: EpfServerUpgraded = EpfServerUpgradable::upgrade(s, server_cert, server_private_key).await; + let server_handshake_accept_task = tokio::spawn(async move { + trace!("starting server handshake listener"); + s.handshake(cert_pool_2).await.unwrap(); + let mut upgraded = s.upgrade().await; + assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42]); + upgraded.write(&[0x42, 0x42]).await.unwrap(); + }); + + let client_handshake_send_task = tokio::spawn(async move { + trace!("starting client handshake sender"); + c.handshake(cert_pool).await.unwrap(); + let mut upgraded = EpfClientHandshaker::upgrade(c).await; + upgraded.write(&[0x42, 0x42]).await.unwrap(); + assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42]); + }); + + let (a, b) = join![server_handshake_accept_task, client_handshake_send_task]; + a.unwrap(); + b.unwrap(); + } + + #[tokio::test] + #[serial] + pub async fn stream_test_ephemeral() { + //simple_logger::init().unwrap(); + + let tcp_listener = TcpListener::bind("0.0.0.0:36117").await.unwrap(); + + let tcp_client_future = TcpSocket::new_v4() + .unwrap() + .connect(SocketAddr::from_str("127.0.0.1:36117").unwrap()); + + let (a, b) = join![tcp_listener.accept(), tcp_client_future]; + + let (s, _) = a.unwrap(); + let c = b.unwrap(); + + let server_private_key = SigningKey::from([1u8; 32]); + + let mut server_cert = EPFCertificate { + details: EPFCertificateDetails { + name: "Testing Server Certificate".to_string(), + not_before: 0, + not_after: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + 30, + public_key: server_private_key.verifying_key().to_bytes(), + issuer_public_key: [0u8; 32], + claims: Default::default(), + }, + fingerprint: "".to_string(), + signature: [0u8; 64], + }; + server_cert.sign(&server_private_key).unwrap(); + + debug!( + "{}", + hex::encode(server_private_key.verifying_key().to_bytes()) + ); + + let mut cert_pool = EpfCaPool::new(); + let mut cert_pool_2 = EpfCaPool::new(); + cert_pool.insert(&server_cert); + + cert_pool_2.insert(&server_cert); + + let mut c: EpfClientUpgraded = EpfClientUpgradable::upgrade( + c, + ClientAuthentication::Ephemeral, + ) + .await; + let mut s: EpfServerUpgraded = + EpfServerUpgradable::upgrade(s, server_cert, server_private_key).await; + let server_handshake_accept_task = tokio::spawn(async move { trace!("starting server handshake listener"); s.handshake(cert_pool_2).await.unwrap(); diff --git a/libepf/src/pki.rs b/libepf/src/pki.rs index 0cfd479..c9131a8 100644 --- a/libepf/src/pki.rs +++ b/libepf/src/pki.rs @@ -615,6 +615,74 @@ mod tests { )) } + #[test] + pub fn certificate_verification_fingerprint_does_not_match() { + let private_key = SigningKey::generate(&mut OsRng); + let public_key = private_key.verifying_key(); + + let mut fingerprint_does_not_match_cert = EPFCertificate { + details: EPFCertificateDetails { + name: "Testing Certificate - Fingerprint Non Matching".to_string(), + not_before: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + - 20, + not_after: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + 30, + public_key: [0u8; 32], + issuer_public_key: *public_key.as_bytes(), + claims: Default::default(), + }, + fingerprint: "".to_string(), + signature: [0u8; EPFPKI_SIGNATURE_LENGTH], + }; + fingerprint_does_not_match_cert.sign(&private_key).unwrap(); + + fingerprint_does_not_match_cert.fingerprint = "0".to_string(); + + let ca_pool = EpfCaPool::new(); + + assert!(matches!(fingerprint_does_not_match_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::FingerprintDoesNotMatch { .. })); + } + + #[test] + pub fn certificate_verification_invalid_signature() { + let private_key = SigningKey::generate(&mut OsRng); + let public_key = private_key.verifying_key(); + + let mut fingerprint_does_not_match_cert = EPFCertificate { + details: EPFCertificateDetails { + name: "Testing Certificate - Fingerprint Non Matching".to_string(), + not_before: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + - 20, + not_after: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + 30, + public_key: [0u8; 32], + issuer_public_key: *public_key.as_bytes(), + claims: Default::default(), + }, + fingerprint: "".to_string(), + signature: [0u8; EPFPKI_SIGNATURE_LENGTH], + }; + fingerprint_does_not_match_cert.sign(&private_key).unwrap(); + + fingerprint_does_not_match_cert.signature = [0u8; 64]; + + let ca_pool = EpfCaPool::new(); + + assert!(matches!(fingerprint_does_not_match_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::InvalidSignature { .. })); + } + #[test] pub fn certificate_verification_not_valid_yet() { let not_yet_valid_cert = EPFCertificate { diff --git a/tarpaulin-report.html b/tarpaulin-report.html index ca50d5b..f202f48 100644 --- a/tarpaulin-report.html +++ b/tarpaulin-report.html @@ -107,8 +107,8 @@