diff --git a/Cargo.toml b/Cargo.toml index 5f15bcd..fa4417d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,26 +1,28 @@ [package] name = "shadowsocks-crypto" -version = "0.2.0" +version = "0.3.0" authors = ["luozijun "] edition = "2018" license = "MIT" -keywords = [ "Cryptography" ] -description = "Shadowsocks Crypto" -repository = "https://github.com/shadowsocks/shadowsocks-crypto" +keywords = ["Cryptography"] +description = "Shadowsocks Crypto" +repository = "https://github.com/shadowsocks/shadowsocks-crypto" documentation = "https://docs.rs/shadowsocks-crypto" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8" +rand = "0.8" crypto2 = "0.1" +libsodium-sys = { version = "0.2", optional = true } +libc = { version = "0.2", optional = true } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] -ring = { version = "0.16", optional = true } +ring = { version = "0.16", optional = true } [target.'cfg(any(target_arch = "arm", target_arch = "aarch64"))'.dependencies] -ring = { version = "0.16", optional = true } +ring = { version = "0.16", optional = true } [dev-dependencies] @@ -36,7 +38,8 @@ default = [ std = [ "crypto2/std", ] -nightly = [ ] -v1 = [ ] -v1-stream = [ "v1" ] -v1-aead = [ "v1" ] +nightly = [] +v1 = [] +v1-stream = ["v1"] +v1-aead = ["v1"] +v1-aead-extra = ["v1-aead", "libsodium-sys", "libc"] diff --git a/src/v1/aeadcipher.rs b/src/v1/aeadcipher/mod.rs similarity index 70% rename from src/v1/aeadcipher.rs rename to src/v1/aeadcipher/mod.rs index f32543b..d15f5ff 100644 --- a/src/v1/aeadcipher.rs +++ b/src/v1/aeadcipher/mod.rs @@ -22,8 +22,13 @@ pub use crypto2::aeadcipher::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305}; ), feature = "ring" ))] -pub use super::ring::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305}; -use super::CipherKind; +pub use crate::v1::ring::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305}; +use crate::v1::CipherKind; + +#[cfg(feature = "v1-aead-extra")] +mod sodium; +#[cfg(feature = "v1-aead-extra")] +pub use self::sodium::XChacha20Poly1305; trait AeadCipherExt { fn ac_kind(&self) -> CipherKind; @@ -74,6 +79,7 @@ macro_rules! impl_aead_cipher { }; } +#[cfg(feature = "v1-aead-extra")] macro_rules! impl_siv_cmac_cipher { ($name:tt, $kind:tt) => { impl AeadCipherExt for $name { @@ -119,34 +125,54 @@ macro_rules! impl_siv_cmac_cipher { }; } +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes128Ccm, AES_128_CCM); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes256Ccm, AES_256_CCM); + impl_aead_cipher!(Aes128Gcm, AES_128_GCM); impl_aead_cipher!(Aes256Gcm, AES_256_GCM); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes128GcmSiv, AES_128_GCM_SIV); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes256GcmSiv, AES_256_GCM_SIV); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes128OcbTag128, AES_128_OCB_TAGLEN128); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes192OcbTag128, AES_192_OCB_TAGLEN128); +#[cfg(feature = "v1-aead-extra")] impl_aead_cipher!(Aes256OcbTag128, AES_256_OCB_TAGLEN128); impl_aead_cipher!(Chacha20Poly1305, CHACHA20_POLY1305); +#[cfg(feature = "v1-aead-extra")] impl_siv_cmac_cipher!(AesSivCmac256, AES_SIV_CMAC_256); +#[cfg(feature = "v1-aead-extra")] impl_siv_cmac_cipher!(AesSivCmac384, AES_SIV_CMAC_384); +#[cfg(feature = "v1-aead-extra")] impl_siv_cmac_cipher!(AesSivCmac512, AES_SIV_CMAC_512); +#[cfg(feature = "v1-aead-extra")] +impl_aead_cipher!(XChacha20Poly1305, XCHACHA20_POLY1305); + macro_rules! aead_cipher_variant { - ($($name:ident @ $kind:ident,)+) => { + ($($(#[cfg($i_meta:meta)])? $name:ident @ $kind:ident,)+) => { enum AeadCipherInner { - $($name($name),)+ + $( + $(#[cfg($i_meta)])? + $name($name), + )+ } impl AeadCipherInner { fn new(kind: CipherKind, key: &[u8]) -> Self { match kind { - $(CipherKind::$kind => AeadCipherInner::$name($name::new(key)),)+ + $( + $(#[cfg($i_meta)])? + CipherKind::$kind => AeadCipherInner::$name($name::new(key)), + )+ _ => unreachable!("unrecognized AEAD cipher kind {:?}", kind), } } @@ -155,47 +181,71 @@ macro_rules! aead_cipher_variant { impl AeadCipherExt for AeadCipherInner { fn ac_kind(&self) -> CipherKind { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_kind(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_kind(), + )+ } } fn ac_key_len(&self) -> usize { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_key_len(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_key_len(), + )+ } } fn ac_block_len(&self) -> usize { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_block_len(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_block_len(), + )+ } } fn ac_tag_len(&self) -> usize { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_tag_len(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_tag_len(), + )+ } } fn ac_n_min(&self) -> usize { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_n_min(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_n_min(), + )+ } } fn ac_n_max(&self) -> usize { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_n_max(),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_n_max(), + )+ } } fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_encrypt_slice(nonce, plaintext_in_ciphertext_out),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_encrypt_slice(nonce, plaintext_in_ciphertext_out), + )+ } } fn ac_decrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) -> bool { match *self { - $(AeadCipherInner::$name(ref c) => c.ac_decrypt_slice(nonce, plaintext_in_ciphertext_out),)+ + $( + $(#[cfg($i_meta)])? + AeadCipherInner::$name(ref c) => c.ac_decrypt_slice(nonce, plaintext_in_ciphertext_out), + )+ } } } @@ -203,24 +253,26 @@ macro_rules! aead_cipher_variant { } aead_cipher_variant! { - Aes128Ccm @ AES_128_CCM, - Aes256Ccm @ AES_256_CCM, + #[cfg(feature = "v1-aead-extra")] Aes128Ccm @ AES_128_CCM, + #[cfg(feature = "v1-aead-extra")] Aes256Ccm @ AES_256_CCM, - Aes128OcbTag128 @ AES_128_OCB_TAGLEN128, - Aes192OcbTag128 @ AES_192_OCB_TAGLEN128, - Aes256OcbTag128 @ AES_256_OCB_TAGLEN128, + #[cfg(feature = "v1-aead-extra")] Aes128OcbTag128 @ AES_128_OCB_TAGLEN128, + #[cfg(feature = "v1-aead-extra")] Aes192OcbTag128 @ AES_192_OCB_TAGLEN128, + #[cfg(feature = "v1-aead-extra")] Aes256OcbTag128 @ AES_256_OCB_TAGLEN128, Aes128Gcm @ AES_128_GCM, Aes256Gcm @ AES_256_GCM, - AesSivCmac256 @ AES_SIV_CMAC_256, - AesSivCmac384 @ AES_SIV_CMAC_384, - AesSivCmac512 @ AES_SIV_CMAC_512, + #[cfg(feature = "v1-aead-extra")] AesSivCmac256 @ AES_SIV_CMAC_256, + #[cfg(feature = "v1-aead-extra")] AesSivCmac384 @ AES_SIV_CMAC_384, + #[cfg(feature = "v1-aead-extra")] AesSivCmac512 @ AES_SIV_CMAC_512, - Aes128GcmSiv @ AES_128_GCM_SIV, - Aes256GcmSiv @ AES_256_GCM_SIV, + #[cfg(feature = "v1-aead-extra")] Aes128GcmSiv @ AES_128_GCM_SIV, + #[cfg(feature = "v1-aead-extra")] Aes256GcmSiv @ AES_256_GCM_SIV, Chacha20Poly1305 @ CHACHA20_POLY1305, + + #[cfg(feature = "v1-aead-extra")] XChacha20Poly1305 @ XCHACHA20_POLY1305, } pub struct AeadCipher { diff --git a/src/v1/aeadcipher/sodium.rs b/src/v1/aeadcipher/sodium.rs new file mode 100644 index 0000000..00e9a9e --- /dev/null +++ b/src/v1/aeadcipher/sodium.rs @@ -0,0 +1,216 @@ +//! `libsodium` provided ciphers + +use core::convert::TryInto; +use core::ptr; + +use libsodium_sys::{ + crypto_aead_xchacha20poly1305_ietf_ABYTES, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, crypto_aead_xchacha20poly1305_ietf_decrypt, + crypto_aead_xchacha20poly1305_ietf_decrypt_detached, + crypto_aead_xchacha20poly1305_ietf_encrypt, + crypto_aead_xchacha20poly1305_ietf_encrypt_detached, +}; + +use crypto2::mem::Zeroize; +use crypto2::streamcipher::Chacha20; + +pub struct XChacha20Poly1305 { + ek: [u8; Self::KEY_LEN], +} + +impl Zeroize for XChacha20Poly1305 { + fn zeroize(&mut self) { + self.ek.zeroize() + } +} + +impl Drop for XChacha20Poly1305 { + fn drop(&mut self) { + self.zeroize() + } +} + +impl XChacha20Poly1305 { + pub const KEY_LEN: usize = crypto_aead_xchacha20poly1305_ietf_KEYBYTES as usize; + pub const NONCE_LEN: usize = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES as usize; + pub const TAG_LEN: usize = crypto_aead_xchacha20poly1305_ietf_ABYTES as usize; + pub const N_MIN: usize = Self::NONCE_LEN; + pub const N_MAX: usize = Self::NONCE_LEN; + pub const BLOCK_LEN: usize = Chacha20::BLOCK_LEN; + + pub fn new(key: &[u8]) -> Self { + XChacha20Poly1305 { + ek: key + .try_into() + .expect("key.len() != XChacha20Poly1305::KEY_LEN"), + } + } + + pub fn encrypt_slice(&self, nonce: &[u8], aad: &[u8], aead_pkt: &mut [u8]) { + debug_assert_eq!(nonce.len(), Self::NONCE_LEN); + debug_assert!(aead_pkt.len() >= Self::TAG_LEN); + + unsafe { + let mut clen: libc::c_ulonglong = 0; + let ret = crypto_aead_xchacha20poly1305_ietf_encrypt( + aead_pkt.as_mut_ptr() as *mut _, + &mut clen, + aead_pkt.as_ptr() as *const _, + (aead_pkt.len() - Self::TAG_LEN) as libc::c_ulonglong, + aad.as_ptr() as *const _, + aad.len() as libc::c_ulonglong, + ptr::null(), + nonce.as_ptr() as *const _, + self.ek.as_ptr() as *const _, + ); + if ret != 0 || clen != aead_pkt.len() as libc::c_ulonglong { + panic!( + "crypto_aead_xchacha20poly1305_ietf_encrypt ret={} clen={} aead_pkt.len={}", + ret, + clen, + aead_pkt.len(), + ); + } + } + } + + pub fn decrypt_slice(&self, nonce: &[u8], aad: &[u8], aead_pkt: &mut [u8]) -> bool { + debug_assert_eq!(nonce.len(), Self::NONCE_LEN); + debug_assert!(aead_pkt.len() >= Self::TAG_LEN); + + unsafe { + let mut mlen: libc::c_ulonglong = 0; + let ret = crypto_aead_xchacha20poly1305_ietf_decrypt( + aead_pkt.as_mut_ptr() as *mut _, + &mut mlen, + ptr::null_mut(), + aead_pkt.as_ptr() as *const _, + aead_pkt.len() as libc::c_ulonglong, + aad.as_ptr() as *const _, + aad.len() as libc::c_ulonglong, + nonce.as_ptr() as *const _, + self.ek.as_ptr(), + ); + + ret == 0 && mlen == (aead_pkt.len() - Self::TAG_LEN) as libc::c_ulonglong + } + } + + #[allow(dead_code)] + pub fn encrypt_slice_detached( + &self, + nonce: &[u8], + aad: &[u8], + plaintext_in_ciphertext_out: &mut [u8], + tag_out: &mut [u8], + ) { + debug_assert_eq!(nonce.len(), Self::NONCE_LEN); + debug_assert_eq!(tag_out.len(), Self::TAG_LEN); + + unsafe { + let mut maclen: libc::c_ulonglong = 0; + let ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( + plaintext_in_ciphertext_out.as_mut_ptr() as *mut _, + tag_out.as_mut_ptr() as *mut _, + &mut maclen, + plaintext_in_ciphertext_out.as_mut_ptr() as *const _, + plaintext_in_ciphertext_out.len() as libc::c_ulonglong, + aad.as_ptr() as *const _, + aad.len() as libc::c_ulonglong, + ptr::null(), + nonce.as_ptr() as *const _, + self.ek.as_ptr() as *const _, + ); + if ret != 0 || maclen != tag_out.len() as libc::c_ulonglong { + panic!( + "crypto_aead_xchacha20poly1305_ietf_encrypt_detached ret={} maclen={} tag_out.len={}", + ret, maclen, tag_out.len() + ); + } + } + } + + #[allow(dead_code)] + pub fn decrypt_slice_detached( + &self, + nonce: &[u8], + aad: &[u8], + ciphertext_in_plaintext_out: &mut [u8], + tag_in: &[u8], + ) -> bool { + debug_assert_eq!(nonce.len(), Self::NONCE_LEN); + debug_assert_eq!(tag_in.len(), Self::TAG_LEN); + + unsafe { + let ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached( + ciphertext_in_plaintext_out.as_mut_ptr() as *mut _, + ptr::null_mut(), + ciphertext_in_plaintext_out.as_ptr() as *const _, + ciphertext_in_plaintext_out.len() as libc::c_ulonglong, + tag_in.as_ptr() as *const _, + aad.as_ptr() as *const _, + aad.len() as libc::c_ulonglong, + nonce.as_ptr() as *const _, + self.ek.as_ptr() as *const _, + ); + + ret == 0 + } + } +} + +#[test] +fn test_xchacha20_ietf_poly1305_enc() { + let key = + hex::decode("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f").unwrap(); + let nonce = hex::decode("07000000404142434445464748494a4b4c4d4e4f50515253").unwrap(); + let aad = hex::decode("50515253c0c1c2c3c4c5c6c7").unwrap(); + + let cipher = XChacha20Poly1305::new(&key); + + let plain_text = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + + let mut cipher_text = plain_text.to_vec(); + cipher_text.resize(cipher_text.len() + XChacha20Poly1305::TAG_LEN, 0); + + cipher.encrypt_slice(&nonce, &aad, &mut cipher_text); + + let expected_cipher_text = hex::decode("f8ebea4875044066fc162a0604e171feecfb3d20425248563bcfd5a155dcc47bbda70b86e5ab9b55002bd1274c02db35321acd7af8b2e2d25015e136b7679458e9f43243bf719d639badb5feac03f80a19a96ef10cb1d15333a837b90946ba3854ee74da3f2585efc7e1e170e17e15e563e77601f4f85cafa8e5877614e143e68420").unwrap(); + assert_eq!(cipher_text, expected_cipher_text); + + let mut detached_cipher_text = plain_text.to_vec(); + let mut detached_tag = [0u8; XChacha20Poly1305::TAG_LEN]; + cipher.encrypt_slice_detached(&nonce, &aad, &mut detached_cipher_text, &mut detached_tag); + + let (expected_detached_cipher_text, expected_detached_tag) = + expected_cipher_text.split_at(plain_text.len()); + assert_eq!(detached_cipher_text, expected_detached_cipher_text); + assert_eq!(detached_tag, expected_detached_tag); +} + +#[test] +fn test_xchacha20_ietf_poly1305_dec() { + let key = + hex::decode("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f").unwrap(); + let nonce = hex::decode("07000000404142434445464748494a4b4c4d4e4f50515253").unwrap(); + let aad = hex::decode("50515253c0c1c2c3c4c5c6c7").unwrap(); + + let cipher = XChacha20Poly1305::new(&key); + + let expected_plain_text = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + let cipher_text = hex::decode("f8ebea4875044066fc162a0604e171feecfb3d20425248563bcfd5a155dcc47bbda70b86e5ab9b55002bd1274c02db35321acd7af8b2e2d25015e136b7679458e9f43243bf719d639badb5feac03f80a19a96ef10cb1d15333a837b90946ba3854ee74da3f2585efc7e1e170e17e15e563e77601f4f85cafa8e5877614e143e68420").unwrap(); + + let mut plain_text = cipher_text.clone(); + assert!(cipher.decrypt_slice(&nonce, &aad, &mut plain_text)); + + assert_eq!( + &plain_text[..expected_plain_text.len()], + expected_plain_text + ); + + let (cipher_text, cipher_tag) = cipher_text.split_at(expected_plain_text.len()); + let mut plain_text = cipher_text.to_vec(); + assert!(cipher.decrypt_slice_detached(&nonce, &aad, &mut plain_text, &cipher_tag)); + + assert_eq!(plain_text, expected_plain_text); +} diff --git a/src/v1/cipher.rs b/src/v1/cipher.rs index d30ae3d..f657755 100644 --- a/src/v1/cipher.rs +++ b/src/v1/cipher.rs @@ -19,7 +19,7 @@ pub const fn available_ciphers() -> &'static [&'static str] { "table", #[cfg(feature = "v1-stream")] "rc4-md5", - // 序列密码 + // Stream Ciphers #[cfg(feature = "v1-stream")] "aes-128-ctr", #[cfg(feature = "v1-stream")] @@ -96,18 +96,33 @@ pub const fn available_ciphers() -> &'static [&'static str] { "rc4", #[cfg(feature = "v1-stream")] "chacha20-ietf", - // AEAD 密码算法 + // AEAD Ciphers #[cfg(feature = "v1-aead")] "aes-128-gcm", #[cfg(feature = "v1-aead")] "aes-256-gcm", #[cfg(feature = "v1-aead")] "chacha20-ietf-poly1305", - // NOTE: 也许将来会开启的密码算法。 - // "aes-128-ccm", "aes-256-ccm", - // "aes-128-gcm-siv", "aes-256-gcm-siv", - // "aes-128-ocb-taglen128", "aes-192-ocb-taglen128", "aes-256-ocb-taglen128", - // "aes-siv-cmac-256", "aes-siv-cmac-384", "aes-siv-cmac-512", + #[cfg(feature = "v1-aead-extra")] + "aes-128-ccm", + #[cfg(feature = "v1-aead-extra")] + "aes-256-ccm", + #[cfg(feature = "v1-aead-extra")] + "aes-128-gcm-siv", + #[cfg(feature = "v1-aead-extra")] + "aes-256-gcm-siv", + #[cfg(feature = "v1-aead-extra")] + "aes-128-ocb-taglen128", + #[cfg(feature = "v1-aead-extra")] + "aes-192-ocb-taglen128", + #[cfg(feature = "v1-aead-extra")] + "aes-256-ocb-taglen128", + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-256", + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-384", + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-512", ] } diff --git a/src/v1/kind.rs b/src/v1/kind.rs index 515875f..378defc 100644 --- a/src/v1/kind.rs +++ b/src/v1/kind.rs @@ -1,10 +1,12 @@ //! Cipher Kind -#[cfg(feature = "v1-aead")] +#[cfg(feature = "v1-aead-extra")] use super::aeadcipher::{ - Aes128Ccm, Aes128Gcm, Aes128GcmSiv, Aes128OcbTag128, Aes192OcbTag128, Aes256Ccm, Aes256Gcm, - Aes256GcmSiv, Aes256OcbTag128, AesSivCmac256, AesSivCmac384, AesSivCmac512, Chacha20Poly1305, + Aes128Ccm, Aes128GcmSiv, Aes128OcbTag128, Aes192OcbTag128, Aes256Ccm, Aes256GcmSiv, + Aes256OcbTag128, AesSivCmac256, AesSivCmac384, AesSivCmac512, XChacha20Poly1305, }; +#[cfg(feature = "v1-aead")] +use super::aeadcipher::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305}; #[cfg(feature = "v1-stream")] use super::streamcipher::{ Aes128Cfb1, @@ -141,32 +143,54 @@ pub enum CipherKind { // AEAD Cipher #[cfg(feature = "v1-aead")] - AES_128_CCM, // AEAD_AES_128_CCM - #[cfg(feature = "v1-aead")] - AES_256_CCM, // AEAD_AES_256_CCM - #[cfg(feature = "v1-aead")] - AES_128_OCB_TAGLEN128, // AEAD_AES_128_OCB_TAGLEN128 - #[cfg(feature = "v1-aead")] - AES_192_OCB_TAGLEN128, // AEAD_AES_192_OCB_TAGLEN128 - #[cfg(feature = "v1-aead")] - AES_256_OCB_TAGLEN128, // AEAD_AES_256_OCB_TAGLEN128 - #[cfg(feature = "v1-aead")] - AES_128_GCM, // AEAD_AES_128_GCM - #[cfg(feature = "v1-aead")] - AES_256_GCM, // AEAD_AES_256_GCM - #[cfg(feature = "v1-aead")] - AES_SIV_CMAC_256, // AEAD_AES_SIV_CMAC_256 - #[cfg(feature = "v1-aead")] - AES_SIV_CMAC_384, // AEAD_AES_SIV_CMAC_384 + /// AEAD_AES_128_GCM + AES_128_GCM, #[cfg(feature = "v1-aead")] - AES_SIV_CMAC_512, // AEAD_AES_SIV_CMAC_512 - #[cfg(feature = "v1-aead")] - AES_128_GCM_SIV, // AEAD_AES_128_GCM_SIV - #[cfg(feature = "v1-aead")] - AES_256_GCM_SIV, // AEAD_AES_256_GCM_SIV + /// AEAD_AES_256_GCM + AES_256_GCM, + // NOTE: IETF 版本 #[cfg(feature = "v1-aead")] - CHACHA20_POLY1305, // AEAD_CHACHA20_POLY1305 + /// AEAD_CHACHA20_POLY1305 + CHACHA20_POLY1305, + + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_128_CCM + AES_128_CCM, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_256_CCM + AES_256_CCM, + + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_128_OCB_TAGLEN128 + AES_128_OCB_TAGLEN128, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_192_OCB_TAGLEN128 + AES_192_OCB_TAGLEN128, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_256_OCB_TAGLEN128 + AES_256_OCB_TAGLEN128, + + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_SIV_CMAC_256 + AES_SIV_CMAC_256, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_SIV_CMAC_384 + AES_SIV_CMAC_384, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_SIV_CMAC_512 + AES_SIV_CMAC_512, + + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_128_GCM_SIV + AES_128_GCM_SIV, + #[cfg(feature = "v1-aead-extra")] + /// AEAD_AES_256_GCM_SIV + AES_256_GCM_SIV, + + #[cfg(feature = "v1-aead-extra")] + /// AEAD_XCHACHA20_POLY1305 + XCHACHA20_POLY1305, } impl CipherKind { @@ -214,19 +238,21 @@ impl CipherKind { use self::CipherKind::*; match *self { + AES_128_GCM | AES_256_GCM | CHACHA20_POLY1305 => true, + + #[cfg(feature = "v1-aead-extra")] AES_128_CCM | AES_256_CCM | AES_128_OCB_TAGLEN128 | AES_192_OCB_TAGLEN128 | AES_256_OCB_TAGLEN128 - | AES_128_GCM - | AES_256_GCM | AES_SIV_CMAC_256 | AES_SIV_CMAC_384 | AES_SIV_CMAC_512 | AES_128_GCM_SIV | AES_256_GCM_SIV - | CHACHA20_POLY1305 => true, + | XCHACHA20_POLY1305 => true, + _ => false, } } @@ -244,12 +270,14 @@ impl CipherKind { // 但是 SS 这里把 Key 的长度限制在 16. #[cfg(feature = "v1-stream")] SS_RC4_MD5 => 16, + #[cfg(feature = "v1-stream")] AES_128_CTR => Aes128Ctr::KEY_LEN, #[cfg(feature = "v1-stream")] AES_192_CTR => Aes192Ctr::KEY_LEN, #[cfg(feature = "v1-stream")] AES_256_CTR => Aes256Ctr::KEY_LEN, + #[cfg(feature = "v1-stream")] AES_128_CFB1 => Aes128Cfb1::KEY_LEN, #[cfg(feature = "v1-stream")] @@ -268,12 +296,14 @@ impl CipherKind { AES_256_CFB8 => Aes256Cfb8::KEY_LEN, #[cfg(feature = "v1-stream")] AES_256_CFB128 => Aes256Cfb128::KEY_LEN, + #[cfg(feature = "v1-stream")] AES_128_OFB => Aes128Ofb::KEY_LEN, #[cfg(feature = "v1-stream")] AES_192_OFB => Aes192Ofb::KEY_LEN, #[cfg(feature = "v1-stream")] AES_256_OFB => Aes256Ofb::KEY_LEN, + #[cfg(feature = "v1-stream")] CAMELLIA_128_CTR => Camellia128Ctr::KEY_LEN, #[cfg(feature = "v1-stream")] @@ -298,12 +328,14 @@ impl CipherKind { CAMELLIA_256_CFB8 => Camellia256Cfb8::KEY_LEN, #[cfg(feature = "v1-stream")] CAMELLIA_256_CFB128 => Camellia256Cfb128::KEY_LEN, + #[cfg(feature = "v1-stream")] CAMELLIA_128_OFB => Camellia128Ofb::KEY_LEN, #[cfg(feature = "v1-stream")] CAMELLIA_192_OFB => Camellia192Ofb::KEY_LEN, #[cfg(feature = "v1-stream")] CAMELLIA_256_OFB => Camellia256Ofb::KEY_LEN, + // NOTE: RC4 密码本身支持 1..256 长度的 Key, // 但是 SS 这里把 Key 的长度限制在 16. #[cfg(feature = "v1-stream")] @@ -313,33 +345,40 @@ impl CipherKind { // AEAD #[cfg(feature = "v1-aead")] - AES_128_CCM => Aes128Ccm::KEY_LEN, + AES_128_GCM => Aes128Gcm::KEY_LEN, #[cfg(feature = "v1-aead")] - AES_256_CCM => Aes256Ccm::KEY_LEN, + AES_256_GCM => Aes256Gcm::KEY_LEN, + #[cfg(feature = "v1-aead")] + CHACHA20_POLY1305 => Chacha20Poly1305::KEY_LEN, + + #[cfg(feature = "v1-aead-extra")] + AES_128_CCM => Aes128Ccm::KEY_LEN, + #[cfg(feature = "v1-aead-extra")] + AES_256_CCM => Aes256Ccm::KEY_LEN, + + #[cfg(feature = "v1-aead-extra")] AES_128_OCB_TAGLEN128 => Aes128OcbTag128::KEY_LEN, - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_192_OCB_TAGLEN128 => Aes192OcbTag128::KEY_LEN, - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_256_OCB_TAGLEN128 => Aes256OcbTag128::KEY_LEN, - #[cfg(feature = "v1-aead")] - AES_128_GCM => Aes128Gcm::KEY_LEN, - #[cfg(feature = "v1-aead")] - AES_256_GCM => Aes256Gcm::KEY_LEN, // NOTE: 注意 AES_SIV_CMAC_256 的 KEY 是 两个 AES-128 的 Key. // 所以是 256. - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_256 => AesSivCmac256::KEY_LEN, - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_384 => AesSivCmac384::KEY_LEN, - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_512 => AesSivCmac512::KEY_LEN, - #[cfg(feature = "v1-aead")] + + #[cfg(feature = "v1-aead-extra")] AES_128_GCM_SIV => Aes128GcmSiv::KEY_LEN, - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] AES_256_GCM_SIV => Aes256GcmSiv::KEY_LEN, - #[cfg(feature = "v1-aead")] - CHACHA20_POLY1305 => Chacha20Poly1305::KEY_LEN, + + #[cfg(feature = "v1-aead-extra")] + XCHACHA20_POLY1305 => XChacha20Poly1305::KEY_LEN, } } @@ -357,6 +396,7 @@ impl CipherKind { AES_128_CTR => Aes128Ctr::IV_LEN, AES_192_CTR => Aes192Ctr::IV_LEN, AES_256_CTR => Aes256Ctr::IV_LEN, + AES_128_CFB1 => Aes128Cfb1::IV_LEN, AES_128_CFB8 => Aes128Cfb8::IV_LEN, AES_128_CFB128 => Aes128Cfb128::IV_LEN, @@ -366,12 +406,15 @@ impl CipherKind { AES_256_CFB1 => Aes256Cfb1::IV_LEN, AES_256_CFB8 => Aes256Cfb8::IV_LEN, AES_256_CFB128 => Aes256Cfb128::IV_LEN, + AES_128_OFB => Aes128Ofb::IV_LEN, AES_192_OFB => Aes192Ofb::IV_LEN, AES_256_OFB => Aes256Ofb::IV_LEN, + CAMELLIA_128_CTR => Camellia128Ctr::IV_LEN, CAMELLIA_192_CTR => Camellia192Ctr::IV_LEN, CAMELLIA_256_CTR => Camellia256Ctr::IV_LEN, + CAMELLIA_128_CFB1 => Camellia128Cfb1::IV_LEN, CAMELLIA_128_CFB8 => Camellia128Cfb8::IV_LEN, CAMELLIA_128_CFB128 => Camellia128Cfb128::IV_LEN, @@ -381,10 +424,11 @@ impl CipherKind { CAMELLIA_256_CFB1 => Camellia256Cfb1::IV_LEN, CAMELLIA_256_CFB8 => Camellia256Cfb8::IV_LEN, CAMELLIA_256_CFB128 => Camellia256Cfb128::IV_LEN, + CAMELLIA_128_OFB => Camellia128Ofb::IV_LEN, CAMELLIA_192_OFB => Camellia192Ofb::IV_LEN, CAMELLIA_256_OFB => Camellia256Ofb::IV_LEN, - // NOTE: RC4 密码本身没有 IV 概念, + // NOTE: RC4 doesn't have an IV RC4 => 0, CHACHA20 => Chacha20::NONCE_LEN, _ => panic!("only support Stream ciphers"), @@ -397,19 +441,38 @@ impl CipherKind { use self::CipherKind::*; match *self { + AES_128_GCM => Aes128Gcm::TAG_LEN, + AES_256_GCM => Aes256Gcm::TAG_LEN, + + CHACHA20_POLY1305 => Chacha20Poly1305::TAG_LEN, + + #[cfg(feature = "v1-aead-extra")] AES_128_CCM => Aes128Ccm::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_256_CCM => Aes256Ccm::TAG_LEN, + + #[cfg(feature = "v1-aead-extra")] AES_128_OCB_TAGLEN128 => Aes128OcbTag128::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_192_OCB_TAGLEN128 => Aes192OcbTag128::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_256_OCB_TAGLEN128 => Aes256OcbTag128::TAG_LEN, - AES_128_GCM => Aes128Gcm::TAG_LEN, - AES_256_GCM => Aes256Gcm::TAG_LEN, + + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_256 => AesSivCmac256::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_384 => AesSivCmac384::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_SIV_CMAC_512 => AesSivCmac512::TAG_LEN, + + #[cfg(feature = "v1-aead-extra")] AES_128_GCM_SIV => Aes128GcmSiv::TAG_LEN, + #[cfg(feature = "v1-aead-extra")] AES_256_GCM_SIV => Aes256GcmSiv::TAG_LEN, - CHACHA20_POLY1305 => Chacha20Poly1305::TAG_LEN, + + #[cfg(feature = "v1-aead-extra")] + XCHACHA20_POLY1305 => XChacha20Poly1305::TAG_LEN, + _ => panic!("only support AEAD ciphers"), } } @@ -518,29 +581,32 @@ impl core::fmt::Display for CipherKind { #[cfg(feature = "v1-aead")] CipherKind::CHACHA20_POLY1305 => "chacha20-ietf-poly1305", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_128_CCM => "aes-128-ccm", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_256_CCM => "aes-256-ccm", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_128_GCM_SIV => "aes-128-gcm-siv", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_256_GCM_SIV => "aes-256-gcm-siv", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_128_OCB_TAGLEN128 => "aes-128-ocb-taglen128", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_192_OCB_TAGLEN128 => "aes-192-ocb-taglen128", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_256_OCB_TAGLEN128 => "aes-256-ocb-taglen128", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_SIV_CMAC_256 => "aes-siv-cmac-256", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_SIV_CMAC_384 => "aes-siv-cmac-384", - #[cfg(feature = "v1-aead")] + #[cfg(feature = "v1-aead-extra")] CipherKind::AES_SIV_CMAC_512 => "aes-siv-cmac-512", + + #[cfg(feature = "v1-aead-extra")] + CipherKind::XCHACHA20_POLY1305 => "xchacha20-ietf-poly1305", }) } } @@ -557,6 +623,7 @@ impl core::str::FromStr for CipherKind { match s.to_lowercase().as_str() { "plain" | "none" => Ok(NONE), + #[cfg(feature = "v1-stream")] "table" | "" => Ok(SS_TABLE), #[cfg(feature = "v1-stream")] @@ -649,7 +716,7 @@ impl core::str::FromStr for CipherKind { #[cfg(feature = "v1-stream")] "chacha20-ietf" => Ok(CHACHA20), - // AEAD 密码算法 + // AEAD Ciphers #[cfg(feature = "v1-aead")] "aes-128-gcm" => Ok(AES_128_GCM), #[cfg(feature = "v1-aead")] @@ -657,17 +724,30 @@ impl core::str::FromStr for CipherKind { #[cfg(feature = "v1-aead")] "chacha20-ietf-poly1305" => Ok(CHACHA20_POLY1305), - // NOTE: 下面的 AEAD 密码也可以使用,但是目前 ShadowSocks 的 SIP 里面并没有规范这些,所以暂时将它注释掉。 - // "aes-128-ccm" => Ok(AES_128_CCM), - // "aes-256-ccm" => Ok(AES_256_CCM), - // "aes-128-gcm-siv" => Ok(AES_128_GCM_SIV), - // "aes-256-gcm-siv" => Ok(AES_256_GCM_SIV), - // "aes-128-ocb-taglen128" => Ok(AES_128_OCB_TAGLEN128), - // "aes-192-ocb-taglen128" => Ok(AES_192_OCB_TAGLEN128), - // "aes-256-ocb-taglen128" => Ok(AES_256_OCB_TAGLEN128), - // "aes-siv-cmac-256" => Ok(AES_SIV_CMAC_256), - // "aes-siv-cmac-384" => Ok(AES_SIV_CMAC_384), - // "aes-siv-cmac-512" => Ok(AES_SIV_CMAC_512), + #[cfg(feature = "v1-aead-extra")] + "aes-128-ccm" => Ok(AES_128_CCM), + #[cfg(feature = "v1-aead-extra")] + "aes-256-ccm" => Ok(AES_256_CCM), + + #[cfg(feature = "v1-aead-extra")] + "aes-128-gcm-siv" => Ok(AES_128_GCM_SIV), + #[cfg(feature = "v1-aead-extra")] + "aes-256-gcm-siv" => Ok(AES_256_GCM_SIV), + + #[cfg(feature = "v1-aead-extra")] + "aes-128-ocb-taglen128" => Ok(AES_128_OCB_TAGLEN128), + #[cfg(feature = "v1-aead-extra")] + "aes-192-ocb-taglen128" => Ok(AES_192_OCB_TAGLEN128), + #[cfg(feature = "v1-aead-extra")] + "aes-256-ocb-taglen128" => Ok(AES_256_OCB_TAGLEN128), + + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-256" => Ok(AES_SIV_CMAC_256), + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-384" => Ok(AES_SIV_CMAC_384), + #[cfg(feature = "v1-aead-extra")] + "aes-siv-cmac-512" => Ok(AES_SIV_CMAC_512), + _ => Err(ParseCipherKindError), } } diff --git a/src/v1/ring.rs b/src/v1/ring.rs index 80945b3..20c56d0 100644 --- a/src/v1/ring.rs +++ b/src/v1/ring.rs @@ -12,8 +12,11 @@ impl Aes128Gcm { pub const NONCE_LEN: usize = 12; pub const TAG_LEN: usize = 16; + #[allow(dead_code)] pub const A_MAX: usize = 2305843009213693951; // 2 ** 61 + #[allow(dead_code)] pub const P_MAX: usize = 68719476735; // 2^36 - 31 + #[allow(dead_code)] pub const C_MAX: usize = 68719476721; // 2^36 - 15 pub const N_MIN: usize = Self::NONCE_LEN; pub const N_MAX: usize = Self::NONCE_LEN; @@ -77,8 +80,11 @@ impl Aes256Gcm { pub const NONCE_LEN: usize = 12; pub const TAG_LEN: usize = 16; + #[allow(dead_code)] pub const A_MAX: usize = 2305843009213693951; // 2 ** 61 + #[allow(dead_code)] pub const P_MAX: usize = 68719476735; // 2^36 - 31 + #[allow(dead_code)] pub const C_MAX: usize = 68719476721; // 2^36 - 15 pub const N_MIN: usize = Self::NONCE_LEN; pub const N_MAX: usize = Self::NONCE_LEN; @@ -142,8 +148,11 @@ impl Chacha20Poly1305 { pub const NONCE_LEN: usize = 12; pub const TAG_LEN: usize = 16; + #[allow(dead_code)] pub const A_MAX: usize = usize::MAX; // 2^32 - 1 + #[allow(dead_code)] pub const P_MAX: usize = 274877906880; // (2^32 - 1) * BLOCK_LEN + #[allow(dead_code)] pub const C_MAX: usize = Self::P_MAX + Self::TAG_LEN; // 274,877,906,896 pub const N_MIN: usize = Self::NONCE_LEN; pub const N_MAX: usize = Self::NONCE_LEN;