From dc8f7e28328713611500a03d72bc8fe2fc53f813 Mon Sep 17 00:00:00 2001 From: quininer Date: Wed, 1 Nov 2017 16:23:36 +0800 Subject: [PATCH] Add experimental SIV support (#81) * add optional miscreant support * add miscreant test * fix miscreant version * update README + add `aes-128-pmac-siv`, `aes-256-pmac-siv` + remove `aes-192-gcm` --- Cargo.lock | 101 +++++++++++++++++++--------- Cargo.toml | 1 + README.md | 7 +- src/crypto/aead.rs | 10 +++ src/crypto/cipher.rs | 77 +++++++++++++++------ src/crypto/mod.rs | 2 + src/crypto/ring.rs | 35 +++------- src/crypto/siv.rs | 156 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 9 files changed, 311 insertions(+), 80 deletions(-) create mode 100644 src/crypto/siv.rs diff --git a/Cargo.lock b/Cargo.lock index 72f5c32d..fcfc9163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,36 +1,7 @@ -[root] -name = "shadowsocks-rust" -version = "1.6.8" -dependencies = [ - "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte_string 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "libsodium-ffi 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "md-5 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", - "qrcode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subprocess 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +[[package]] +name = "aesni" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aho-corasick" @@ -137,6 +108,14 @@ dependencies = [ "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clear_on_drop" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "coco" version = "0.1.1" @@ -351,6 +330,18 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "miscreant" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aesni 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.31" @@ -533,6 +524,41 @@ dependencies = [ "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "shadowsocks-rust" +version = "1.6.8" +dependencies = [ + "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byte_string 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libsodium-ffi 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "md-5 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miscreant 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", + "qrcode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subprocess 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "slab" version = "0.3.0" @@ -559,6 +585,11 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "subtle" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "termion" version = "1.5.1" @@ -722,6 +753,7 @@ dependencies = [ ] [metadata] +"checksum aesni 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ef0ab1be561c7753c0c416e1b73466c5a07a134c222f4115a4c76ea6526eb56a" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f" @@ -737,6 +769,7 @@ dependencies = [ "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" +"checksum clear_on_drop 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27fc408dd80e7fe9d5b1c8cee6c2add84bea237d8dee70880a76ae7957f6d3a6" "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" @@ -765,6 +798,7 @@ dependencies = [ "checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb" "checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum miscreant 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7dc55762103c586148e451ce13e0fbc0ec9f92b90ae70042a2ffc0d77ddb1c" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" @@ -792,6 +826,7 @@ dependencies = [ "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum subprocess 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "78a77399f1ffa6f2ee3595ab8e329381bbeaf640d59073821519e6a0aa957f9d" +"checksum subtle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32dbfc4beea0a08bfe33114d0f5e5092f35f6fa387a955b4a2c68888cdd7b0f2" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" diff --git a/Cargo.toml b/Cargo.toml index 18386dcc..66e425c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ url = "1.5" byte_string = "1.0" libsodium-ffi = { version = "0.1", optional = true } futures-cpupool = "0.1" +miscreant = { version = "0.2", optional = true } [target.'cfg(unix)'.dependencies] tokio-signal = "0.1" diff --git a/README.md b/README.md index 2005f751..56245b8a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ make install ``` Then `sslocal`, `ssserver` and `ssurl` will be installed in `/usr/local/bin` (variable PREFIX). - + ### **Build standalone binaries** Requirements: @@ -170,12 +170,13 @@ $ ssserver -s "[::]:8388" -m "aes-256-gcm" -k "hello-kitty" --plugin "obfs-local * `dummy` (No encryption, just for debugging) ### AEAD Ciphers -* `aes-128-gcm`, `aes-192-gcm`, `aes-256-gcm` +* `aes-128-gcm`, `aes-256-gcm` * `chacha20-ietf-poly1305` +* `aes-128-pmac-siv`, `aes-256-pmac-siv` (experimental) ## Useful Tools -1. `ssurl` is for encoding and decoding ShadowSocks URLs (SIP002). Example: +1. `ssurl` is for encoding and decoding ShadowSocks URLs (SIP002). Example: ```plain ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmQ@127.0.0.1:8388/?plugin=obfs-local%3Bobfs%3Dhttp%3Bobfs-host%3Dwww.baidu.com diff --git a/src/crypto/aead.rs b/src/crypto/aead.rs index 6406f493..1a4c2291 100644 --- a/src/crypto/aead.rs +++ b/src/crypto/aead.rs @@ -3,6 +3,8 @@ use crypto::cipher::{CipherCategory, CipherResult, CipherType}; use crypto::ring::RingAeadCipher; +#[cfg(feature = "miscreant")] +use crypto::siv::MiscreantCipher; use ring::digest::SHA1; use ring::hkdf; @@ -37,6 +39,10 @@ pub fn new_aead_encryptor(t: CipherType, key: &[u8], nonce: &[u8]) -> BoxAeadEnc CipherType::Aes256Gcm | CipherType::ChaCha20Poly1305 => Box::new(RingAeadCipher::new(t, key, nonce, true)), + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv | + CipherType::Aes256PmacSiv => Box::new(MiscreantCipher::new(t, key, nonce)), + _ => unreachable!(), } } @@ -50,6 +56,10 @@ pub fn new_aead_decryptor(t: CipherType, key: &[u8], nonce: &[u8]) -> BoxAeadDec CipherType::Aes256Gcm | CipherType::ChaCha20Poly1305 => Box::new(RingAeadCipher::new(t, key, nonce, false)), + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv | + CipherType::Aes256PmacSiv => Box::new(MiscreantCipher::new(t, key, nonce)), + _ => unreachable!(), } } diff --git a/src/crypto/cipher.rs b/src/crypto/cipher.rs index c976d969..cb36d358 100644 --- a/src/crypto/cipher.rs +++ b/src/crypto/cipher.rs @@ -67,35 +67,40 @@ impl From<::openssl::error::ErrorStack> for Error { } } -const CIPHER_AES_128_CFB: &'static str = "aes-128-cfb"; -const CIPHER_AES_128_CFB_1: &'static str = "aes-128-cfb1"; -const CIPHER_AES_128_CFB_8: &'static str = "aes-128-cfb8"; -const CIPHER_AES_128_CFB_128: &'static str = "aes-128-cfb128"; +const CIPHER_AES_128_CFB: &str = "aes-128-cfb"; +const CIPHER_AES_128_CFB_1: &str = "aes-128-cfb1"; +const CIPHER_AES_128_CFB_8: &str = "aes-128-cfb8"; +const CIPHER_AES_128_CFB_128: &str = "aes-128-cfb128"; -const CIPHER_AES_256_CFB: &'static str = "aes-256-cfb"; -const CIPHER_AES_256_CFB_1: &'static str = "aes-256-cfb1"; -const CIPHER_AES_256_CFB_8: &'static str = "aes-256-cfb8"; -const CIPHER_AES_256_CFB_128: &'static str = "aes-256-cfb128"; +const CIPHER_AES_256_CFB: &str = "aes-256-cfb"; +const CIPHER_AES_256_CFB_1: &str = "aes-256-cfb1"; +const CIPHER_AES_256_CFB_8: &str = "aes-256-cfb8"; +const CIPHER_AES_256_CFB_128: &str = "aes-256-cfb128"; -const CIPHER_RC4: &'static str = "rc4"; -const CIPHER_RC4_MD5: &'static str = "rc4-md5"; +const CIPHER_RC4: &str = "rc4"; +const CIPHER_RC4_MD5: &str = "rc4-md5"; -const CIPHER_TABLE: &'static str = "table"; +const CIPHER_TABLE: &str = "table"; #[cfg(feature = "sodium")] -const CIPHER_CHACHA20: &'static str = "chacha20"; +const CIPHER_CHACHA20: &str = "chacha20"; #[cfg(feature = "sodium")] -const CIPHER_SALSA20: &'static str = "salsa20"; +const CIPHER_SALSA20: &str = "salsa20"; #[cfg(feature = "sodium")] -const CIPHER_XSALSA20: &'static str = "xsalsa20"; +const CIPHER_XSALSA20: &str = "xsalsa20"; #[cfg(feature = "sodium")] -const CIPHER_CHACHA20_IETF: &'static str = "chacha20-ietf"; +const CIPHER_CHACHA20_IETF: &str = "chacha20-ietf"; -const CIPHER_DUMMY: &'static str = "dummy"; +#[cfg(feature = "miscreant")] +const CIPHER_AES_128_PMAC_SIV: &str = "aes-128-pmac-siv"; +#[cfg(feature = "miscreant")] +const CIPHER_AES_256_PMAC_SIV: &str = "aes-256-pmac-siv"; -const CIPHER_AES_128_GCM: &'static str = "aes-128-gcm"; -const CIPHER_AES_256_GCM: &'static str = "aes-256-gcm"; -const CIPHER_CHACHA20_POLY1305: &'static str = "chacha20-ietf-poly1305"; +const CIPHER_DUMMY: &str = "dummy"; + +const CIPHER_AES_128_GCM: &str = "aes-128-gcm"; +const CIPHER_AES_256_GCM: &str = "aes-256-gcm"; +const CIPHER_CHACHA20_POLY1305: &str = "chacha20-ietf-poly1305"; /// ShadowSocks cipher type #[derive(Clone, Debug, Copy)] @@ -129,6 +134,11 @@ pub enum CipherType { Aes256Gcm, ChaCha20Poly1305, + + #[cfg(feature = "miscreant")] + Aes128PmacSiv, + #[cfg(feature = "miscreant")] + Aes256PmacSiv, } /// Category of ciphers @@ -168,6 +178,11 @@ impl CipherType { CipherType::Aes256Gcm => AES_256_GCM.key_len(), CipherType::ChaCha20Poly1305 => CHACHA20_POLY1305.key_len(), + + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv => 32, + #[cfg(feature = "miscreant")] + CipherType::Aes256PmacSiv => 64, } } @@ -263,6 +278,11 @@ impl CipherType { CipherType::Aes128Gcm => AES_128_GCM.nonce_len(), CipherType::Aes256Gcm => AES_256_GCM.nonce_len(), CipherType::ChaCha20Poly1305 => CHACHA20_POLY1305.nonce_len(), + + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv => 8, + #[cfg(feature = "miscreant")] + CipherType::Aes256PmacSiv => 8, } } @@ -294,6 +314,11 @@ impl CipherType { CipherType::Aes128Gcm | CipherType::Aes256Gcm | CipherType::ChaCha20Poly1305 => CipherCategory::Aead, + + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv | + CipherType::Aes256PmacSiv => CipherCategory::Aead, + _ => CipherCategory::Stream, } } @@ -307,6 +332,10 @@ impl CipherType { CipherType::Aes256Gcm => AES_256_GCM.tag_len(), CipherType::ChaCha20Poly1305 => CHACHA20_POLY1305.tag_len(), + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv | + CipherType::Aes256PmacSiv => 16, + _ => panic!("Only support AEAD ciphers, found {:?}", self), } } @@ -356,6 +385,11 @@ impl FromStr for CipherType { CIPHER_CHACHA20_POLY1305 => Ok(CipherType::ChaCha20Poly1305), + #[cfg(feature = "miscreant")] + CIPHER_AES_128_PMAC_SIV => Ok(CipherType::Aes128PmacSiv), + #[cfg(feature = "miscreant")] + CIPHER_AES_256_PMAC_SIV => Ok(CipherType::Aes256PmacSiv), + _ => Err(Error::UnknownCipherType), } } @@ -391,6 +425,11 @@ impl Display for CipherType { CipherType::Aes128Gcm => write!(f, "{}", CIPHER_AES_128_GCM), CipherType::Aes256Gcm => write!(f, "{}", CIPHER_AES_256_GCM), CipherType::ChaCha20Poly1305 => write!(f, "{}", CIPHER_CHACHA20_POLY1305), + + #[cfg(feature = "miscreant")] + CipherType::Aes128PmacSiv => write!(f, "{}", CIPHER_AES_128_PMAC_SIV), + #[cfg(feature = "miscreant")] + CipherType::Aes256PmacSiv => write!(f, "{}", CIPHER_AES_256_PMAC_SIV), } } } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index e876bba2..6cc4df49 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -18,6 +18,8 @@ pub mod ring; pub mod dummy; pub mod aead; pub mod stream; +#[cfg(feature = "miscreant")] +pub mod siv; #[cfg(feature = "sodium")] pub mod sodium; diff --git a/src/crypto/ring.rs b/src/crypto/ring.rs index e5d5665c..af1d0db3 100644 --- a/src/crypto/ring.rs +++ b/src/crypto/ring.rs @@ -57,32 +57,17 @@ impl RingAeadCipher { macro_rules! seal_or_open { ( $item:ident, $key:ident, $crypt:ident ) => { RingAeadCryptoVariant::$item($key::new(&$crypt, key).unwrap(), Bytes::from(nonce)) + }; + ( $crypt:ident ) => { + if is_seal { seal_or_open!(Seal, SealingKey, $crypt) } + else { seal_or_open!(Open, OpeningKey, $crypt) } } } match t { - CipherType::Aes128Gcm => { - if is_seal { - seal_or_open!(Seal, SealingKey, AES_128_GCM) - } else { - seal_or_open!(Open, OpeningKey, AES_128_GCM) - } - } - CipherType::Aes256Gcm => { - if is_seal { - seal_or_open!(Seal, SealingKey, AES_256_GCM) - } else { - seal_or_open!(Open, OpeningKey, AES_256_GCM) - } - } - CipherType::ChaCha20Poly1305 => { - if is_seal { - seal_or_open!(Seal, SealingKey, CHACHA20_POLY1305) - } else { - seal_or_open!(Open, OpeningKey, CHACHA20_POLY1305) - } - } - + CipherType::Aes128Gcm => seal_or_open!(AES_128_GCM), + CipherType::Aes256Gcm => seal_or_open!(AES_256_GCM), + CipherType::ChaCha20Poly1305 => seal_or_open!(CHACHA20_POLY1305), _ => panic!("unsupported cipher in ring {:?}", t), } } @@ -115,8 +100,8 @@ impl AeadEncryptor for RingAeadCipher { if let RingAeadCryptoVariant::Seal(ref key, ref nonce) = self.cipher { seal_in_place(key, nonce, &[], &mut buf, tag_len).unwrap(); - output.clone_from_slice(&buf[..input.len()]); - tag.clone_from_slice(&buf[input.len()..]); + output.copy_from_slice(&buf[..input.len()]); + tag.copy_from_slice(&buf[input.len()..]); } else { unreachable!("encrypt is called on a non-seal cipher"); } @@ -134,7 +119,7 @@ impl AeadDecryptor for RingAeadCipher { let r = if let RingAeadCryptoVariant::Open(ref key, ref nonce) = self.cipher { match open_in_place(key, nonce, &[], 0, &mut buf) { Ok(buf) => { - output.clone_from_slice(&buf[..input.len()]); + output.copy_from_slice(&buf[..input.len()]); Ok(()) } Err(err) => { diff --git a/src/crypto/siv.rs b/src/crypto/siv.rs new file mode 100644 index 00000000..4c76d748 --- /dev/null +++ b/src/crypto/siv.rs @@ -0,0 +1,156 @@ +//! Cipher defined with Miscreant + +use std::{iter, ptr}; + +use miscreant::{Aes128PmacSiv, Aes256PmacSiv}; + +use crypto::{AeadDecryptor, AeadEncryptor}; +use crypto::{CipherResult, CipherType}; +use crypto::aead::{increase_nonce, make_skey}; +use crypto::cipher::Error; + +use bytes::{BufMut, BytesMut}; + +use byte_string::ByteStr; + + +/// AEAD ciphers provided by Miscreant +pub enum MiscreantCryptoVariant { + Aes128(Aes128PmacSiv), + Aes256(Aes256PmacSiv) +} + +/// AEAD Cipher context +/// +/// According to SIP004, the `nonce` has to incr 1 after each encrypt/decrypt. +pub struct MiscreantCipher { + cipher: MiscreantCryptoVariant, + nonce: BytesMut +} + +impl MiscreantCipher { + /// Initialize context + pub fn new(t: CipherType, key: &[u8], salt: &[u8]) -> Self { + // NOTE: Don't need check salt is duplicated. :) + + let nonce_size = t.iv_size(); + let mut nonce = BytesMut::with_capacity(nonce_size); + unsafe { + nonce.set_len(nonce_size); + ptr::write_bytes(nonce.as_mut_ptr(), 0, nonce_size); + } + + let skey = make_skey(t, key, salt); + let cipher = Self::new_variant(t, &skey); + MiscreantCipher { cipher: cipher, nonce: nonce } + } + + fn new_variant(t: CipherType, key: &[u8]) -> MiscreantCryptoVariant { + match t { + CipherType::Aes128PmacSiv => { + let mut skey = [0; 32]; + skey.copy_from_slice(key); + MiscreantCryptoVariant::Aes128(Aes128PmacSiv::new(&skey)) + }, + CipherType::Aes256PmacSiv => { + let mut skey = [0; 64]; + skey.copy_from_slice(key); + MiscreantCryptoVariant::Aes256(Aes256PmacSiv::new(&skey)) + }, + _ => panic!("unsupported cipher in miscreant {:?}", t), + } + } +} + +impl AeadEncryptor for MiscreantCipher { + fn encrypt(&mut self, input: &[u8], output: &mut [u8], tag: &mut [u8]) { + let tag_len = tag.len(); + let buf_len = input.len() + tag_len; + + let mut buf = BytesMut::with_capacity(buf_len); + unsafe { + buf.set_len(buf_len); + } + buf[tag_len..].copy_from_slice(input); + + match self.cipher { + MiscreantCryptoVariant::Aes128(ref mut cipher) => { + cipher.seal_in_place(iter::once(&self.nonce), &mut buf); + tag.copy_from_slice(&buf[..tag_len]); + output.copy_from_slice(&buf[tag_len..]); + }, + MiscreantCryptoVariant::Aes256(ref mut cipher) => { + cipher.seal_in_place(iter::once(&self.nonce), &mut buf); + tag.copy_from_slice(&buf[..tag_len]); + output.copy_from_slice(&buf[tag_len..]); + } + } + + increase_nonce(&mut self.nonce); + } +} + +impl AeadDecryptor for MiscreantCipher { + fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> CipherResult<()> { + let mut buf = BytesMut::with_capacity(input.len() + tag.len()); + buf.put_slice(tag); + buf.put_slice(input); + + let result = match self.cipher { + MiscreantCryptoVariant::Aes128(ref mut cipher) => + cipher.open_in_place(iter::once(&self.nonce), &mut buf), + MiscreantCryptoVariant::Aes256(ref mut cipher) => + cipher.open_in_place(iter::once(&self.nonce), &mut buf) + }; + + result + .map(|buf| { + output.copy_from_slice(buf); + increase_nonce(&mut self.nonce); + }) + .map_err(|_| { + error!("AEAD decrypt failed, nonce={:?}, input={:?}, tag={:?}, err: decrypt failure", + ByteStr::new(&self.nonce), + ByteStr::new(input), + ByteStr::new(tag)); + Error::AeadDecryptFailed + }) + } +} + + +#[cfg(test)] +mod test { + use super::*; + use crypto::{CipherType, AeadEncryptor, AeadDecryptor}; + + fn test_miscreant(ct: CipherType) { + let key = ct.bytes_to_key(b"PassWORD"); + let message = b"message"; + + let iv = ct.gen_init_vec(); + + let mut enc = MiscreantCipher::new(ct, &key[..], &iv[..]); + let mut encrypted_msg = vec![0; message.len()]; + let mut tag = [0; 16]; + enc.encrypt(message, &mut encrypted_msg, &mut tag); + + assert_ne!(message, &encrypted_msg[..]); + + let mut dec = MiscreantCipher::new(ct, &key[..], &iv[..]); + let mut decrypted_msg = vec![0; encrypted_msg.len()]; + dec.decrypt(&encrypted_msg[..], &mut decrypted_msg, &tag).unwrap(); + + assert_eq!(&decrypted_msg[..], message); + } + + #[test] + fn test_rust_crypto_cipher_chacha20() { + test_miscreant(CipherType::Aes128PmacSiv); + } + + #[test] + fn test_rust_crypto_cipher_salsa20() { + test_miscreant(CipherType::Aes256PmacSiv); + } +} diff --git a/src/lib.rs b/src/lib.rs index b43bec02..bdffad41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,8 @@ extern crate byteorder; extern crate rand; extern crate typenum; extern crate digest; +#[cfg(feature = "miscreant")] +extern crate miscreant; #[cfg(feature = "sodium")] extern crate libsodium_ffi; extern crate md_5 as md5;