Skip to content

Commit

Permalink
Refactored Cipher with macro generated inner implementation
Browse files Browse the repository at this point in the history
- fixes #5
  • Loading branch information
zonyitoo committed Mar 23, 2021
1 parent a489f1b commit ffcd541
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 95 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# Editors
.bak
.vscode
.swp
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shadowsocks-crypto"
version = "0.1.2"
version = "0.2.0"
authors = ["luozijun <[email protected]>"]
edition = "2018"
license = "MIT"
Expand Down
5 changes: 3 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ss-crypto
================
shadowsocks-crypto
==================

shadowsocks' flavored cryptographic algorithm in pure Rust.

Supported Ciphers
-----------------------
Expand Down
115 changes: 91 additions & 24 deletions src/v1/aeadcipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use crypto2::aeadcipher::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305};
pub use super::ring::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305};
use super::CipherKind;

trait AeadCipherInner {
trait AeadCipherExt {
fn ac_kind(&self) -> CipherKind;
fn ac_key_len(&self) -> usize;
fn ac_block_len(&self) -> usize;
Expand All @@ -39,7 +39,7 @@ trait AeadCipherInner {

macro_rules! impl_aead_cipher {
($name:tt, $kind:tt) => {
impl AeadCipherInner for $name {
impl AeadCipherExt for $name {
fn ac_kind(&self) -> CipherKind {
CipherKind::$kind
}
Expand Down Expand Up @@ -76,7 +76,7 @@ macro_rules! impl_aead_cipher {

macro_rules! impl_siv_cmac_cipher {
($name:tt, $kind:tt) => {
impl AeadCipherInner for $name {
impl AeadCipherExt for $name {
fn ac_kind(&self) -> CipherKind {
CipherKind::$kind
}
Expand Down Expand Up @@ -137,8 +137,94 @@ impl_siv_cmac_cipher!(AesSivCmac256, AES_SIV_CMAC_256);
impl_siv_cmac_cipher!(AesSivCmac384, AES_SIV_CMAC_384);
impl_siv_cmac_cipher!(AesSivCmac512, AES_SIV_CMAC_512);

macro_rules! aead_cipher_variant {
($($name:ident @ $kind:ident,)+) => {
enum AeadCipherInner {
$($name($name),)+
}

impl AeadCipherInner {
fn new(kind: CipherKind, key: &[u8]) -> Self {
match kind {
$(CipherKind::$kind => AeadCipherInner::$name($name::new(key)),)+
_ => unreachable!("unrecognized AEAD cipher kind {:?}", kind),
}
}
}

impl AeadCipherExt for AeadCipherInner {
fn ac_kind(&self) -> CipherKind {
match *self {
$(AeadCipherInner::$name(ref c) => c.ac_kind(),)+
}
}

fn ac_key_len(&self) -> usize {
match *self {
$(AeadCipherInner::$name(ref c) => c.ac_key_len(),)+
}
}
fn ac_block_len(&self) -> usize {
match *self {
$(AeadCipherInner::$name(ref c) => c.ac_block_len(),)+
}
}

fn ac_tag_len(&self) -> usize {
match *self {
$(AeadCipherInner::$name(ref c) => c.ac_tag_len(),)+
}
}

fn ac_n_min(&self) -> usize {
match *self {
$(AeadCipherInner::$name(ref c) => c.ac_n_min(),)+
}
}
fn ac_n_max(&self) -> usize {
match *self {
$(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),)+
}
}

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),)+
}
}
}
};
}

aead_cipher_variant! {
Aes128Ccm @ AES_128_CCM,
Aes256Ccm @ AES_256_CCM,

Aes128OcbTag128 @ AES_128_OCB_TAGLEN128,
Aes192OcbTag128 @ AES_192_OCB_TAGLEN128,
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,

Aes128GcmSiv @ AES_128_GCM_SIV,
Aes256GcmSiv @ AES_256_GCM_SIV,

Chacha20Poly1305 @ CHACHA20_POLY1305,
}

pub struct AeadCipher {
cipher: Box<dyn AeadCipherInner + Send + 'static>,
cipher: AeadCipherInner,
nlen: usize,
nonce: [u8; Self::N_MAX],
}
Expand All @@ -147,27 +233,8 @@ impl AeadCipher {
const N_MAX: usize = 16;

pub fn new(kind: CipherKind, key: &[u8]) -> Self {
use self::CipherKind::*;

let cipher: Box<dyn AeadCipherInner + Send + 'static> = match kind {
AES_128_CCM => Box::new(Aes128Ccm::new(key)),
AES_256_CCM => Box::new(Aes256Ccm::new(key)),
AES_128_OCB_TAGLEN128 => Box::new(Aes128OcbTag128::new(key)),
AES_192_OCB_TAGLEN128 => Box::new(Aes192OcbTag128::new(key)),
AES_256_OCB_TAGLEN128 => Box::new(Aes256OcbTag128::new(key)),
AES_128_GCM => Box::new(Aes128Gcm::new(key)),
AES_256_GCM => Box::new(Aes256Gcm::new(key)),
AES_SIV_CMAC_256 => Box::new(AesSivCmac256::new(key)),
AES_SIV_CMAC_384 => Box::new(AesSivCmac384::new(key)),
AES_SIV_CMAC_512 => Box::new(AesSivCmac512::new(key)),
AES_128_GCM_SIV => Box::new(Aes128GcmSiv::new(key)),
AES_256_GCM_SIV => Box::new(Aes256GcmSiv::new(key)),
CHACHA20_POLY1305 => Box::new(Chacha20Poly1305::new(key)),
_ => unreachable!(),
};

let cipher = AeadCipherInner::new(kind, key);
let nlen = std::cmp::min(cipher.ac_n_max(), Self::N_MAX);

let nonce = [0u8; Self::N_MAX];

Self {
Expand Down
68 changes: 49 additions & 19 deletions src/v1/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,34 @@ impl CipherInner for AeadCipher {
}

/// Unified interface of Ciphers
pub struct Cipher {
cipher: Box<dyn CipherInner + Send + 'static>,
pub enum Cipher {
Dummy(DummyCipher),
#[cfg(feature = "v1-stream")]
Stream(StreamCipher),
#[cfg(feature = "v1-aead")]
Aead(AeadCipher),
}

macro_rules! cipher_method_forward {
(ref $self:expr, $method:ident $(, $param:expr),*) => {
match *$self {
Cipher::Dummy(ref c) => c.$method($($param),*),
#[cfg(feature = "v1-stream")]
Cipher::Stream(ref c) => c.$method($($param),*),
#[cfg(feature = "v1-aead")]
Cipher::Aead(ref c) => c.$method($($param),*),
}
};

(mut $self:expr, $method:ident $(, $param:expr),*) => {
match *$self {
Cipher::Dummy(ref mut c) => c.$method($($param),*),
#[cfg(feature = "v1-stream")]
Cipher::Stream(ref mut c) => c.$method($($param),*),
#[cfg(feature = "v1-aead")]
Cipher::Aead(ref mut c) => c.$method($($param),*),
}
};
}

impl Cipher {
Expand All @@ -231,24 +257,18 @@ impl Cipher {
///
/// - Stream Ciphers initialize with IV
/// - AEAD Ciphers initialize with SALT
pub fn new(kind: CipherKind, key: &[u8], iv_or_salt: &[u8]) -> Self {
pub fn new(kind: CipherKind, key: &[u8], iv_or_salt: &[u8]) -> Cipher {
let category = kind.category();

match category {
CipherCategory::None => {
let _ = key;
let _ = iv_or_salt;

let cipher = Box::new(DummyCipher::new());

Self { cipher }
Cipher::Dummy(DummyCipher::new())
}
#[cfg(feature = "v1-stream")]
CipherCategory::Stream => {
let cipher = Box::new(StreamCipher::new(kind, key, iv_or_salt));

Self { cipher }
}
CipherCategory::Stream => Cipher::Stream(StreamCipher::new(kind, key, iv_or_salt)),
#[cfg(feature = "v1-aead")]
CipherCategory::Aead => {
use crypto2::kdf::HkdfSha1;
Expand All @@ -260,34 +280,32 @@ impl Cipher {

let subkey = &okm[..ikm.len()];

let cipher = Box::new(AeadCipher::new(kind, subkey));

Self { cipher }
Cipher::Aead(AeadCipher::new(kind, subkey))
}
}
}

/// Get the `CipherCategory` of the current cipher
pub fn category(&self) -> CipherCategory {
self.cipher.ss_category()
cipher_method_forward!(ref self, ss_category)
}

/// Get the `CipherKind` of the current cipher
pub fn kind(&self) -> CipherKind {
self.cipher.ss_kind()
cipher_method_forward!(ref self, ss_kind)
}

/// Get the TAG length of AEAD ciphers
pub fn tag_len(&self) -> usize {
self.cipher.ss_tag_len()
cipher_method_forward!(ref self, ss_tag_len)
}

/// Encrypt a packet. Encrypted result will be written in `pkt`
///
/// - Stream Ciphers: the size of input and output packets are the same
/// - AEAD Ciphers: the size of output must be at least `input.len() + TAG_LEN`
pub fn encrypt_packet(&mut self, pkt: &mut [u8]) {
self.cipher.ss_encrypt_slice(pkt)
cipher_method_forward!(mut self, ss_encrypt_slice, pkt)
}

/// Decrypt a packet. Decrypted result will be written in `pkt`
Expand All @@ -296,7 +314,7 @@ impl Cipher {
/// - AEAD Ciphers: the size of output is `input.len() - TAG_LEN`
#[must_use]
pub fn decrypt_packet(&mut self, pkt: &mut [u8]) -> bool {
self.cipher.ss_decrypt_slice(pkt)
cipher_method_forward!(mut self, ss_decrypt_slice, pkt)
}
}

Expand Down Expand Up @@ -331,3 +349,15 @@ fn test_cipher_new_stream() {
let cipher = Cipher::new(kind, &key, &iv);
assert_eq!(cipher.tag_len(), 0);
}

#[test]
fn test_send() {
fn test<C: Send>() {}
test::<Cipher>();
}

#[test]
fn test_sync() {
fn test<C: Sync>() {}
test::<Cipher>();
}
13 changes: 7 additions & 6 deletions src/v1/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,14 @@ impl core::fmt::Display for CipherKind {
}
}

/// Error while parsing `CipherKind` from string
#[derive(Debug, Clone)]
pub struct ParseCipherKindError;

impl core::str::FromStr for CipherKind {
type Err = std::io::Error;
type Err = ParseCipherKindError;

fn from_str(s: &str) -> Result<Self, std::io::Error> {
fn from_str(s: &str) -> Result<Self, ParseCipherKindError> {
use self::CipherKind::*;

match s.to_lowercase().as_str() {
Expand Down Expand Up @@ -664,10 +668,7 @@ impl core::str::FromStr for CipherKind {
// "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),
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"unknown cipher type",
)),
_ => Err(ParseCipherKindError),
}
}
}
Loading

0 comments on commit ffcd541

Please sign in to comment.