diff --git a/Cargo.lock b/Cargo.lock index 9d307d4c3..5903a67d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3606,6 +3606,7 @@ dependencies = [ "getrandom 0.2.6", "hex", "hex-literal", + "openssl", "rand 0.8.5", "rand_core 0.5.1", "rand_core 0.6.3", @@ -3619,6 +3620,7 @@ dependencies = [ "simple_asn1", "subtle", "thiserror", + "typenum", "x25519-dalek", "zeroize", ] diff --git a/crates/tor-llcrypto/Cargo.toml b/crates/tor-llcrypto/Cargo.toml index faa0e42bc..97c14e631 100644 --- a/crates/tor-llcrypto/Cargo.toml +++ b/crates/tor-llcrypto/Cargo.toml @@ -13,6 +13,7 @@ repository = "https://gitlab.torproject.org/tpo/core/arti.git/" [features] default = [] +with-openssl = ["openssl", "typenum", "cipher"] # Enable support for cryptography needed to be a Tor relay. relay = [] @@ -24,12 +25,14 @@ hsv3-client = [] aes = { version = "0.8" } arrayref = "0.3" base64 = "0.13.0" +cipher = { version = "0.4.3", optional = true } ctr = "0.9" curve25519-dalek = "3.2" digest = "0.10.0" ed25519-dalek = { version = "1", features = ["batch"] } hex = "0.4" old_rand_core = { package = "rand_core", version = "0.5.1" } +openssl = { version = "0.10.30", optional = true } rand_core = "0.6.2" rsa = "0.5.0" serde = "1.0.103" @@ -40,6 +43,7 @@ signature = "1" simple_asn1 = "0.6" subtle = "2" thiserror = "1" +typenum = { version = "1.15.0", optional = true } x25519-dalek = "1.2" zeroize = "1" diff --git a/crates/tor-llcrypto/src/cipher.rs b/crates/tor-llcrypto/src/cipher.rs index 663d44ace..bf631be53 100644 --- a/crates/tor-llcrypto/src/cipher.rs +++ b/crates/tor-llcrypto/src/cipher.rs @@ -7,6 +7,7 @@ /// /// These ciphers implement the `cipher::StreamCipher` trait, so use /// the [`cipher`](https://docs.rs/cipher) crate to access them. +#[cfg(not(feature = "with-openssl"))] pub mod aes { // These implement StreamCipher. /// AES128 in counter mode as used by Tor. @@ -15,3 +16,106 @@ pub mod aes { /// AES256 in counter mode as used by Tor. pub type Aes256Ctr = ctr::Ctr128BE; } + +/// Compatiblity layer between OpenSSL and `cipher::StreamCipher`. +/// +/// These ciphers implement the `cipher::StreamCipher` trait, so use +/// the [`cipher`](https://docs.rs/cipher) crate to access them. +#[cfg(feature = "with-openssl")] +pub mod aes { + use cipher::generic_array::GenericArray; + use cipher::inout::InOutBuf; + use cipher::{InnerIvInit, IvSizeUser, StreamCipher, StreamCipherError}; + use digest::crypto_common::{InnerUser, KeyInit, KeySizeUser}; + use openssl::symm::{Cipher, Crypter, Mode}; + + /// AES 128 in counter mode as used by Tor. + pub struct Aes128Ctr(Crypter); + + /// AES 128 key + pub struct Aes128Key([u8; 16]); + + impl KeySizeUser for Aes128Key { + type KeySize = typenum::consts::U16; + } + + impl KeyInit for Aes128Key { + fn new(key: &GenericArray) -> Self { + Aes128Key((*key).into()) + } + } + + impl InnerUser for Aes128Ctr { + type Inner = Aes128Key; + } + + impl IvSizeUser for Aes128Ctr { + type IvSize = typenum::consts::U16; + } + + impl StreamCipher for Aes128Ctr { + fn try_apply_keystream_inout( + &mut self, + mut buf: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError> { + let in_buf = buf.get_in().to_vec(); + self.0 + .update(&in_buf, buf.get_out()) + .map_err(|_| StreamCipherError)?; + Ok(()) + } + } + + impl InnerIvInit for Aes128Ctr { + fn inner_iv_init(inner: Self::Inner, iv: &GenericArray) -> Self { + let crypter = Crypter::new(Cipher::aes_128_ctr(), Mode::Encrypt, &inner.0, Some(iv)) + .expect("openssl error while initializeing Aes128Ctr"); + Aes128Ctr(crypter) + } + } + + /// AES 256 in counter mode as used by Tor. + pub struct Aes256Ctr(Crypter); + + /// AES 256 key + pub struct Aes256Key([u8; 32]); + + impl KeySizeUser for Aes256Key { + type KeySize = typenum::consts::U32; + } + + impl KeyInit for Aes256Key { + fn new(key: &GenericArray) -> Self { + Aes256Key((*key).into()) + } + } + + impl InnerUser for Aes256Ctr { + type Inner = Aes256Key; + } + + impl IvSizeUser for Aes256Ctr { + type IvSize = typenum::consts::U16; + } + + impl StreamCipher for Aes256Ctr { + fn try_apply_keystream_inout( + &mut self, + mut buf: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError> { + let in_buf = buf.get_in().to_vec(); + self.0 + .update(&in_buf, buf.get_out()) + .map_err(|_| StreamCipherError)?; + Ok(()) + } + } + + impl InnerIvInit for Aes256Ctr { + fn inner_iv_init(inner: Self::Inner, iv: &GenericArray) -> Self { + let crypter = Crypter::new(Cipher::aes_256_ctr(), Mode::Encrypt, &inner.0, Some(iv)) + .expect("openssl error while initializeing Aes256Ctr"); + Aes256Ctr(crypter) + } + } +} diff --git a/crates/tor-llcrypto/src/d.rs b/crates/tor-llcrypto/src/d.rs index 0c6dda072..432b39bb2 100644 --- a/crates/tor-llcrypto/src/d.rs +++ b/crates/tor-llcrypto/src/d.rs @@ -7,6 +7,40 @@ //! Other code should access these digests via the traits in the //! [`digest`] crate. +#[cfg(feature = "with-openssl")] +pub use openssl_compat::Sha1; +#[cfg(not(feature = "with-openssl"))] pub use sha1::Sha1; + pub use sha2::{Sha256, Sha512}; pub use sha3::{Sha3_256, Shake128, Shake256}; + +/// Compatibility layer between OpenSSL and `digest` +#[cfg(feature = "with-openssl")] +mod openssl_compat { + use openssl::sha::Sha1 as OpenSslSha1; + + use digest::{FixedOutput, HashMarker, Output, OutputSizeUser, Update}; + + /// Wrapper arround OpenSSL Sha1 to make it compatible with `digest` + #[derive(Default, Clone)] + pub struct Sha1(OpenSslSha1); + + impl Update for Sha1 { + fn update(&mut self, data: &[u8]) { + self.0.update(data); + } + } + + impl OutputSizeUser for Sha1 { + type OutputSize = typenum::consts::U20; + } + + impl FixedOutput for Sha1 { + fn finalize_into(self, out: &mut Output) { + *out = self.0.finish().into(); + } + } + + impl HashMarker for Sha1 {} +}