Skip to content

Commit

Permalink
Removes non-standard AEAD ciphers that have variable NONCE_LEN
Browse files Browse the repository at this point in the history
- These ciphers support variable NONCE_LEN. A fixed NONCE_LEN have to be
  chosen by a SIP process
- Bump version to v0.2.4

BREAKING CHANGES:

These ciphers have been removed

- aes-128-ocb-taglen128, aes-192-ocb-taglen128, aes-256-ocb-taglen128
- aes-siv-cmac-256, aes-siv-cmac-384, aes-siv-cmac-512
  • Loading branch information
zonyitoo committed Apr 18, 2021
1 parent b5ffd28 commit cb54882
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 230 deletions.
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.2.3"
version = "0.2.4"
authors = ["luozijun <luozijun.assistant@gmail.com>"]
edition = "2018"
license = "MIT"
Expand Down
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ shadowsocks' flavored cryptographic algorithm in pure Rust.

Stream Ciphers:

* SS_TABLE
* SS_RC4_MD5
* AES_128_CTR, AES_192_CTR, AES_256_CTR
* AES_128_CFB1, AES_128_CFB8, AES_128_CFB128, AES_192_CFB1, AES_192_CFB8, AES_192_CFB128, AES_256_CFB1, AES_256_CFB8, AES_256_CFB128
* AES_128_OFB, AES_192_OFB, AES_256_OFB
* CAMELLIA_128_CTR, CAMELLIA_192_CTR, CAMELLIA_256_CTR
* CAMELLIA_128_CFB1, CAMELLIA_128_CFB8, CAMELLIA_128_CFB128, CAMELLIA_192_CFB1, CAMELLIA_192_CFB8, CAMELLIA_192_CFB128, CAMELLIA_256_CFB1, CAMELLIA_256_CFB8, CAMELLIA_256_CFB128
* CAMELLIA_128_OFB, CAMELLIA_192_OFB, CAMELLIA_256_OFB
* RC4
* CHACHA20 (IETF Version)
* [x] SS_TABLE
* [x] SS_RC4_MD5
* [x] AES_128_CTR, AES_192_CTR, AES_256_CTR
* [x] AES_128_CFB1, AES_128_CFB8, AES_128_CFB128, AES_192_CFB1, AES_192_CFB8, AES_192_CFB128, AES_256_CFB1, AES_256_CFB8, AES_256_CFB128
* [x] AES_128_OFB, AES_192_OFB, AES_256_OFB
* [x] CAMELLIA_128_CTR, CAMELLIA_192_CTR, CAMELLIA_256_CTR
* [x] CAMELLIA_128_CFB1, CAMELLIA_128_CFB8, CAMELLIA_128_CFB128, CAMELLIA_192_CFB1, CAMELLIA_192_CFB8, CAMELLIA_192_CFB128, CAMELLIA_256_CFB1, CAMELLIA_256_CFB8, CAMELLIA_256_CFB128
* [x] CAMELLIA_128_OFB, CAMELLIA_192_OFB, CAMELLIA_256_OFB
* [x] RC4
* [x] CHACHA20 (IETF Version)

AEAD Ciphers:

* 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 (IETF Version)
* XCHACHA20_POLY1305 (IETF Version)
* [x] AES_128_CCM, AES_256_CCM
* [x] AES_128_GCM, AES_256_GCM
* [x] AES_128_GCM_SIV, AES_256_GCM_SIV
* [x] CHACHA20_POLY1305 (IETF Version)
* [x] XCHACHA20_POLY1305 (IETF Version)
* [ ] AES_128_OCB_TAGLEN128, AES_192_OCB_TAGLEN128, AES_256_OCB_TAGLEN128
* [ ] AES_SIV_CMAC_256, AES_SIV_CMAC_384, AES_SIV_CMAC_512
95 changes: 24 additions & 71 deletions src/v1/aeadcipher/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
pub use crypto2::aeadcipher::{
Aes128Ccm, Aes128GcmSiv, Aes128OcbTag128, Aes192OcbTag128, Aes256Ccm, Aes256GcmSiv,
Aes256OcbTag128, AesSivCmac256, AesSivCmac384, AesSivCmac512, Sm4Ccm, Sm4Gcm,
};
pub use crypto2::aeadcipher::{Aes128Ccm, Aes128GcmSiv, Aes256Ccm, Aes256GcmSiv, Sm4Ccm, Sm4Gcm};
#[cfg(not(all(
any(
target_arch = "x86",
Expand Down Expand Up @@ -37,13 +34,14 @@ trait AeadCipherExt {
fn ac_n_min(&self) -> usize;
fn ac_n_max(&self) -> usize;
fn ac_tag_len(&self) -> usize;
fn ac_n_len(&self) -> usize;

fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]);
fn ac_decrypt_slice(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool;
}

macro_rules! impl_aead_cipher {
($name:tt, $kind:tt) => {
($name:tt, $kind:tt $(, $nlen:expr)?) => {
impl AeadCipherExt for $name {
fn ac_kind(&self) -> CipherKind {
CipherKind::$kind
Expand All @@ -63,6 +61,7 @@ macro_rules! impl_aead_cipher {
fn ac_tag_len(&self) -> usize {
$name::TAG_LEN
}
impl_aead_cipher!(ac_n_len $(, $nlen)?);

fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
self.encrypt_slice(nonce, &[], plaintext_in_ciphertext_out);
Expand All @@ -77,50 +76,17 @@ macro_rules! impl_aead_cipher {
}
}
};
}

#[cfg(feature = "v1-aead-extra")]
macro_rules! impl_siv_cmac_cipher {
($name:tt, $kind:tt) => {
impl AeadCipherExt for $name {
fn ac_kind(&self) -> CipherKind {
CipherKind::$kind
}
fn ac_key_len(&self) -> usize {
$name::KEY_LEN
}
fn ac_block_len(&self) -> usize {
$name::BLOCK_LEN
}
fn ac_n_min(&self) -> usize {
$name::N_MIN
}
fn ac_n_max(&self) -> usize {
$name::N_MAX
}
fn ac_tag_len(&self) -> usize {
$name::TAG_LEN
}

// NOTE: SIV-CMAC 模式,Nonce 在 AAD 数据的最后面。
// TAG 默认也在 PKT 的前面,为此我们这里需要手动把 TAG 数据放置在 密文的后面。
fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let len = plaintext_in_ciphertext_out.len();
let plen = len - Self::TAG_LEN;
let (plaintext, tag_out) = plaintext_in_ciphertext_out.split_at_mut(plen);
self.encrypt_slice_detached(&[nonce], plaintext, tag_out);
}

fn ac_decrypt_slice(
&self,
nonce: &[u8],
ciphertext_in_plaintext_out: &mut [u8],
) -> bool {
let len = ciphertext_in_plaintext_out.len();
let clen = len - Self::TAG_LEN;
let (ciphertext, tag_in) = ciphertext_in_plaintext_out.split_at_mut(clen);
self.decrypt_slice_detached(&[nonce], ciphertext, &tag_in)
}
(ac_n_len) => {
fn ac_n_len(&self) -> usize {
debug_assert_eq!(self.ac_n_min(), self.ac_n_max());
self.ac_n_max()
}
};
(ac_n_len, $nlen:expr) => {
fn ac_n_len(&self) -> usize {
debug_assert!($nlen >= self.ac_n_min() && $nlen <= self.ac_n_max());
$nlen
}
};
}
Expand All @@ -138,22 +104,8 @@ 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);

Expand Down Expand Up @@ -235,6 +187,14 @@ macro_rules! aead_cipher_variant {
)+
}
}
fn ac_n_len(&self) -> usize {
match *self {
$(
$(#[cfg($i_meta)])?
AeadCipherInner::$name(ref c) => c.ac_n_len(),
)+
}
}

fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
match *self {
Expand All @@ -261,17 +221,9 @@ aead_cipher_variant! {
#[cfg(feature = "v1-aead-extra")] Aes128Ccm @ AES_128_CCM,
#[cfg(feature = "v1-aead-extra")] Aes256Ccm @ AES_256_CCM,

#[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,

#[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,

#[cfg(feature = "v1-aead-extra")] Aes128GcmSiv @ AES_128_GCM_SIV,
#[cfg(feature = "v1-aead-extra")] Aes256GcmSiv @ AES_256_GCM_SIV,

Expand All @@ -294,7 +246,8 @@ impl AeadCipher {

pub fn new(kind: CipherKind, key: &[u8]) -> Self {
let cipher = AeadCipherInner::new(kind, key);
let nlen = std::cmp::min(cipher.ac_n_max(), Self::N_MAX);
let nlen = cipher.ac_n_len();
debug_assert!(nlen <= Self::N_MAX);
let nonce = [0u8; Self::N_MAX];

Self {
Expand Down
58 changes: 28 additions & 30 deletions src/v1/aeadcipher/xchacha20_poly1305/xchacha20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl XChacha20 {
pub const BLOCK_LEN: usize = 64;
pub const NONCE_LEN: usize = 24;

#[allow(dead_code)]
const CHACHA20_NONCE_LEN: usize = 12;
const STATE_LEN: usize = 16; // len in doubleword (32-bits)

Expand Down Expand Up @@ -90,11 +91,11 @@ impl XChacha20 {
let mut initial_state = self.initial_state;

// Nonce (128-bits, little-endian)
initial_state[12] = u32::from_le_bytes([nonce[ 0], nonce[ 1], nonce[ 2], nonce[ 3]]);
initial_state[13] = u32::from_le_bytes([nonce[ 4], nonce[ 5], nonce[ 6], nonce[ 7]]);
initial_state[14] = u32::from_le_bytes([nonce[ 8], nonce[ 9], nonce[10], nonce[11]]);
initial_state[12] = u32::from_le_bytes([nonce[0], nonce[1], nonce[2], nonce[3]]);
initial_state[13] = u32::from_le_bytes([nonce[4], nonce[5], nonce[6], nonce[7]]);
initial_state[14] = u32::from_le_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]);
initial_state[15] = u32::from_le_bytes([nonce[12], nonce[13], nonce[14], nonce[15]]);

// 20 rounds (diagonal rounds)
diagonal_rounds(&mut initial_state);

Expand All @@ -115,25 +116,25 @@ impl XChacha20 {
#[inline]
fn in_place(&self, init_block_counter: u32, nonce: &[u8], plaintext_or_ciphertext: &mut [u8]) {
debug_assert_eq!(nonce.len(), Self::NONCE_LEN);

let mut initial_state = self.initial_state;
let subkey = self.hchacha20(&nonce[..16]);

// NOTE: 使用 HChaCha20 生成的 256-bits Key.
initial_state[ 4] = subkey[0];
initial_state[ 5] = subkey[1];
initial_state[ 6] = subkey[2];
initial_state[ 7] = subkey[3];
initial_state[ 8] = subkey[4];
initial_state[ 9] = subkey[5];
initial_state[4] = subkey[0];
initial_state[5] = subkey[1];
initial_state[6] = subkey[2];
initial_state[7] = subkey[3];
initial_state[8] = subkey[4];
initial_state[9] = subkey[5];
initial_state[10] = subkey[6];
initial_state[11] = subkey[7];

// ChaCha20 Counter (32-bits, little-endian)
initial_state[12] = init_block_counter;

// ChaCha20 Nonce (96-bits, little-endian)
//
//
// NOTE: 重新组装 12 Bytes 的 Chacha20 Nonce
// [0, 0, 0, 0] + nonce[16..24]
// ------------ -------------
Expand All @@ -142,15 +143,12 @@ impl XChacha20 {
if cfg!(target_endian = "little") {
let tmp = &nonce[16..24]; // 8 Bytes
unsafe {
let data: &[u32] = std::slice::from_raw_parts(
tmp.as_ptr() as *const u32,
2,
);
let data: &[u32] = std::slice::from_raw_parts(tmp.as_ptr() as *const u32, 2);
initial_state[14..16].copy_from_slice(data);
}
} else {
initial_state[14] = u32::from_le_bytes([nonce[ 4], nonce[ 5], nonce[ 6], nonce[ 7]]);
initial_state[15] = u32::from_le_bytes([nonce[ 8], nonce[ 9], nonce[10], nonce[11]]);
initial_state[14] = u32::from_le_bytes([nonce[4], nonce[5], nonce[6], nonce[7]]);
initial_state[15] = u32::from_le_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]);
}

let mut chunks = plaintext_or_ciphertext.chunks_exact_mut(Self::BLOCK_LEN);
Expand Down Expand Up @@ -284,16 +282,16 @@ fn state_to_keystream(
state: &[u32; XChacha20::STATE_LEN],
keystream: &mut [u8; XChacha20::BLOCK_LEN],
) {
keystream[ 0.. 4].copy_from_slice(&state[ 0].to_le_bytes());
keystream[ 4.. 8].copy_from_slice(&state[ 1].to_le_bytes());
keystream[ 8..12].copy_from_slice(&state[ 2].to_le_bytes());
keystream[12..16].copy_from_slice(&state[ 3].to_le_bytes());
keystream[16..20].copy_from_slice(&state[ 4].to_le_bytes());
keystream[20..24].copy_from_slice(&state[ 5].to_le_bytes());
keystream[24..28].copy_from_slice(&state[ 6].to_le_bytes());
keystream[28..32].copy_from_slice(&state[ 7].to_le_bytes());
keystream[32..36].copy_from_slice(&state[ 8].to_le_bytes());
keystream[36..40].copy_from_slice(&state[ 9].to_le_bytes());
keystream[0..4].copy_from_slice(&state[0].to_le_bytes());
keystream[4..8].copy_from_slice(&state[1].to_le_bytes());
keystream[8..12].copy_from_slice(&state[2].to_le_bytes());
keystream[12..16].copy_from_slice(&state[3].to_le_bytes());
keystream[16..20].copy_from_slice(&state[4].to_le_bytes());
keystream[20..24].copy_from_slice(&state[5].to_le_bytes());
keystream[24..28].copy_from_slice(&state[6].to_le_bytes());
keystream[28..32].copy_from_slice(&state[7].to_le_bytes());
keystream[32..36].copy_from_slice(&state[8].to_le_bytes());
keystream[36..40].copy_from_slice(&state[9].to_le_bytes());
keystream[40..44].copy_from_slice(&state[10].to_le_bytes());
keystream[44..48].copy_from_slice(&state[11].to_le_bytes());
keystream[48..52].copy_from_slice(&state[12].to_le_bytes());
Expand Down
12 changes: 0 additions & 12 deletions src/v1/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,6 @@ pub const fn available_ciphers() -> &'static [&'static str] {
#[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",
#[cfg(feature = "v1-aead-extra")]
"xchacha20-ietf-poly1305",
#[cfg(feature = "v1-aead-extra")]
"sm4-gcm",
Expand Down
Loading

0 comments on commit cb54882

Please sign in to comment.