Skip to content

Commit

Permalink
support (non-standard) sm4-gcm, sm4-ccm in v1-aead-extra
Browse files Browse the repository at this point in the history
- https://datatracker.ietf.org/doc/html/rfc8998
- shadowsocks/shadowsocks-libev#2424

If anyone interested in trying these non-standard ciphers, please take
some time to get some background knowledge about the SM Cipher Suites.
  • Loading branch information
zonyitoo committed Oct 23, 2023
1 parent 092840c commit f3f26b5
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 8 deletions.
22 changes: 16 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shadowsocks-crypto"
version = "0.5.2"
version = "0.5.3"
authors = ["luozijun <[email protected]>", "ty <[email protected]>"]
edition = "2021"
license = "MIT"
Expand All @@ -12,14 +12,20 @@ rust-version = "1.61"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = [
"v1",
"v1-aead",
]
default = ["v1", "v1-aead"]
v1 = []
v1-stream = ["v1", "chacha20", "aes", "ctr", "camellia"]
v1-aead = ["v1", "aes-gcm", "chacha20poly1305", "hkdf", "sha1"]
v1-aead-extra = ["v1-aead", "aes-gcm-siv", "ccm", "aes"]
v1-aead-extra = [
"v1-aead",
"aes-gcm-siv",
"ccm",
"aes",
"sm4",
"ghash",
"aead",
"subtle",
]
v2 = ["aes", "aes-gcm", "blake3", "chacha20poly1305", "bytes"]
v2-extra = ["v2", "chacha20poly1305/reduced-round"]

Expand All @@ -42,6 +48,10 @@ aes = { version = "0.8", optional = true }
ctr = { version = "0.9", optional = true }
bytes = { version = "1.3", optional = true }
camellia = { version = "0.1", optional = true }
sm4 = { version = "0.5", optional = true }
ghash = { version = "0.5", optional = true }
aead = { version = "0.5", optional = true }
subtle = { version = "2.5", optional = true }

#[target.'cfg(all(unix, any(target_arch = "x86", target_arch = "x86_64")))'.dependencies]
#md-5 = { version = "0.10", features = ["asm"] }
Expand Down
35 changes: 33 additions & 2 deletions src/kind.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Cipher Kind

#[cfg(feature = "v1-aead-extra")]
use crate::v1::aeadcipher::{Aes128Ccm, Aes128GcmSiv, Aes256Ccm, Aes256GcmSiv, XChaCha20Poly1305};
use crate::v1::aeadcipher::{Aes128Ccm, Aes128GcmSiv, Aes256Ccm, Aes256GcmSiv, Sm4Ccm, Sm4Gcm, XChaCha20Poly1305};
#[cfg(feature = "v1-aead")]
use crate::v1::aeadcipher::{Aes128Gcm, Aes256Gcm, ChaCha20Poly1305};

Expand Down Expand Up @@ -230,6 +230,15 @@ pub enum CipherKind {
/// AEAD_XCHACHA20_POLY1305
XCHACHA20_POLY1305,

#[cfg(feature = "v1-aead-extra")]
#[cfg_attr(docrs, doc(cfg(feature = "v1-aead-extra")))]
/// SM4_GCM
SM4_GCM,
#[cfg(feature = "v1-aead-extra")]
#[cfg_attr(docrs, doc(cfg(feature = "v1-aead-extra")))]
/// SM4_GCM
SM4_CCM,

#[cfg(feature = "v2")]
#[cfg_attr(docrs, doc(cfg(feature = "v2")))]
/// 2022-blake3-aes-128-gcm
Expand Down Expand Up @@ -302,7 +311,9 @@ impl CipherKind {
AES_128_GCM | AES_256_GCM | CHACHA20_POLY1305 => true,

#[cfg(feature = "v1-aead-extra")]
AES_128_CCM | AES_256_CCM | AES_128_GCM_SIV | AES_256_GCM_SIV | XCHACHA20_POLY1305 => true,
AES_128_CCM | AES_256_CCM | AES_128_GCM_SIV | AES_256_GCM_SIV | XCHACHA20_POLY1305 | SM4_GCM | SM4_CCM => {
true
}

_ => false,
}
Expand Down Expand Up @@ -426,6 +437,11 @@ impl CipherKind {
#[cfg(feature = "v1-aead-extra")]
XCHACHA20_POLY1305 => XChaCha20Poly1305::key_size(),

#[cfg(feature = "v1-aead-extra")]
SM4_GCM => Sm4Gcm::key_size(),
#[cfg(feature = "v1-aead-extra")]
SM4_CCM => Sm4Ccm::key_size(),

#[cfg(feature = "v2")]
AEAD2022_BLAKE3_AES_128_GCM => Aead2022Aes128Gcm::key_size(),
#[cfg(feature = "v2")]
Expand Down Expand Up @@ -515,6 +531,11 @@ impl CipherKind {
#[cfg(feature = "v1-aead-extra")]
XCHACHA20_POLY1305 => XChaCha20Poly1305::tag_size(),

#[cfg(feature = "v1-aead-extra")]
SM4_GCM => Sm4Gcm::tag_size(),
#[cfg(feature = "v1-aead-extra")]
SM4_CCM => Sm4Ccm::tag_size(),

#[cfg(feature = "v2")]
AEAD2022_BLAKE3_AES_128_GCM => Aead2022Aes128Gcm::tag_size(),
#[cfg(feature = "v2")]
Expand Down Expand Up @@ -669,6 +690,11 @@ impl core::fmt::Display for CipherKind {
#[cfg(feature = "v1-aead-extra")]
CipherKind::XCHACHA20_POLY1305 => "xchacha20-ietf-poly1305",

#[cfg(feature = "v1-aead-extra")]
CipherKind::SM4_GCM => "sm4-gcm",
#[cfg(feature = "v1-aead-extra")]
CipherKind::SM4_CCM => "sm4-ccm",

#[cfg(feature = "v2")]
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => "2022-blake3-aes-128-gcm",
#[cfg(feature = "v2")]
Expand Down Expand Up @@ -814,6 +840,11 @@ impl core::str::FromStr for CipherKind {
#[cfg(feature = "v1-aead-extra")]
"xchacha20-ietf-poly1305" => Ok(XCHACHA20_POLY1305),

#[cfg(feature = "v1-aead-extra")]
"sm4-gcm" => Ok(SM4_GCM),
#[cfg(feature = "v1-aead-extra")]
"sm4-ccm" => Ok(SM4_CCM),

#[cfg(feature = "v2")]
"2022-blake3-aes-128-gcm" => Ok(AEAD2022_BLAKE3_AES_128_GCM),
#[cfg(feature = "v2")]
Expand Down
32 changes: 32 additions & 0 deletions src/v1/aeadcipher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ mod aes_gcm;
mod aes_gcm_siv;
mod chacha20_poly1305;
#[cfg(feature = "v1-aead-extra")]
mod sm4_ccm;
#[cfg(feature = "v1-aead-extra")]
mod sm4_gcm;
#[cfg(feature = "v1-aead-extra")]
mod sm4_gcm_cipher;
#[cfg(feature = "v1-aead-extra")]
mod xchacha20_poly1305;

#[cfg(feature = "v1-aead-extra")]
pub use self::{
aes_ccm::{Aes128Ccm, Aes256Ccm},
aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv},
sm4_ccm::Sm4Ccm,
sm4_gcm::Sm4Gcm,
xchacha20_poly1305::XChaCha20Poly1305,
};
pub use self::{
Expand All @@ -34,6 +42,10 @@ enum AeadCipherVariant {
Aes128Ccm(Aes128Ccm),
#[cfg(feature = "v1-aead-extra")]
Aes256Ccm(Aes256Ccm),
#[cfg(feature = "v1-aead-extra")]
Sm4Gcm(Sm4Gcm),
#[cfg(feature = "v1-aead-extra")]
Sm4Ccm(Sm4Ccm),
}

impl AeadCipherVariant {
Expand All @@ -52,6 +64,10 @@ impl AeadCipherVariant {
CipherKind::AES_128_CCM => AeadCipherVariant::Aes128Ccm(Aes128Ccm::new(key)),
#[cfg(feature = "v1-aead-extra")]
CipherKind::AES_256_CCM => AeadCipherVariant::Aes256Ccm(Aes256Ccm::new(key)),
#[cfg(feature = "v1-aead-extra")]
CipherKind::SM4_GCM => AeadCipherVariant::Sm4Gcm(Sm4Gcm::new(key)),
#[cfg(feature = "v1-aead-extra")]
CipherKind::SM4_CCM => AeadCipherVariant::Sm4Ccm(Sm4Ccm::new(key)),
_ => unreachable!("{:?} is not an AEAD cipher", kind),
}
}
Expand All @@ -71,6 +87,10 @@ impl AeadCipherVariant {
AeadCipherVariant::Aes128Ccm(..) => Aes128Ccm::nonce_size(),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Aes256Ccm(..) => Aes256Ccm::nonce_size(),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Gcm(..) => Sm4Gcm::nonce_size(),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Ccm(..) => Sm4Ccm::nonce_size(),
}
}

Expand All @@ -89,6 +109,10 @@ impl AeadCipherVariant {
AeadCipherVariant::Aes128Ccm(..) => CipherKind::AES_128_CCM,
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Aes256Ccm(..) => CipherKind::AES_256_CCM,
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Gcm(..) => CipherKind::SM4_GCM,
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Ccm(..) => CipherKind::SM4_CCM,
}
}

Expand All @@ -107,6 +131,10 @@ impl AeadCipherVariant {
AeadCipherVariant::Aes128Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Aes256Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Gcm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
}
}

Expand All @@ -125,6 +153,10 @@ impl AeadCipherVariant {
AeadCipherVariant::Aes128Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Aes256Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Gcm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
#[cfg(feature = "v1-aead-extra")]
AeadCipherVariant::Sm4Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
}
}
}
Expand Down
83 changes: 83 additions & 0 deletions src/v1/aeadcipher/sm4_ccm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! SM4_CCM
//!
//! https://datatracker.ietf.org/doc/html/rfc8998

use ccm::{
aead::{generic_array::typenum::Unsigned, AeadCore, AeadInPlace, KeyInit, KeySizeUser},
consts::{U12, U16},
Ccm,
Nonce,
Tag,
};
use sm4::Sm4;

pub struct Sm4Ccm(Ccm<Sm4, U16, U12>);

impl Sm4Ccm {
pub fn new(key: &[u8]) -> Sm4Ccm {
Sm4Ccm(Ccm::new_from_slice(key).expect("Sm4Ccm"))
}

pub fn key_size() -> usize {
<Ccm<Sm4, U16, U12> as KeySizeUser>::KeySize::to_usize()
}

pub fn nonce_size() -> usize {
<Ccm<Sm4, U16, U12> as AeadCore>::NonceSize::to_usize()
}

pub fn tag_size() -> usize {
<Ccm<Sm4, U16, U12> as AeadCore>::TagSize::to_usize()
}

pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("AES_128_CCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}

pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
let (ciphertext, in_tag) =
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
let in_tag = Tag::from_slice(in_tag);
self.0.decrypt_in_place_detached(nonce, &[], ciphertext, in_tag).is_ok()
}
}

#[cfg(test)]
mod test {
use ccm::{
aead::{Aead, KeyInit, Payload},
consts::{U12, U16},
Ccm,
};
use sm4::Sm4;

#[test]
fn test_sm4_ccm() {
let iv = hex::decode("00001234567800000000ABCD").unwrap();
let key = hex::decode("0123456789ABCDEFFEDCBA9876543210").unwrap();
let plain_text = hex::decode("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA").unwrap();
let aad = hex::decode("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2").unwrap();
let mut cipher_text = hex::decode("48AF93501FA62ADBCD414CCE6034D895DDA1BF8F132F042098661572E7483094FD12E518CE062C98ACEE28D95DF4416BED31A2F04476C18BB40C84A74B97DC5B").unwrap();
let mut tag = hex::decode("16842D4FA186F56AB33256971FA110F4").unwrap();
cipher_text.append(&mut tag); // postfix tag

let nonce = super::Nonce::from_slice(&iv);

let cipher = Ccm::<Sm4, U16, U12>::new_from_slice(&key).unwrap();
let plain_text_payload = Payload {
msg: &plain_text,
aad: &aad,
};
let result = cipher.encrypt(nonce, plain_text_payload).unwrap();

assert_eq!(result, cipher_text);
}
}
46 changes: 46 additions & 0 deletions src/v1/aeadcipher/sm4_gcm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! SM4-GCM

use aead::{AeadCore, AeadInPlace, Key, KeyInit, KeySizeUser};
use sm4::cipher::Unsigned;

use super::sm4_gcm_cipher::{Nonce, Sm4Gcm as CryptoSm4Gcm, Tag};

pub struct Sm4Gcm(CryptoSm4Gcm);

impl Sm4Gcm {
pub fn new(key: &[u8]) -> Sm4Gcm {
let key = Key::<CryptoSm4Gcm>::from_slice(key);
Sm4Gcm(CryptoSm4Gcm::new(key))
}

pub fn key_size() -> usize {
<CryptoSm4Gcm as KeySizeUser>::KeySize::to_usize()
}

pub fn nonce_size() -> usize {
<CryptoSm4Gcm as AeadCore>::NonceSize::to_usize()
}

pub fn tag_size() -> usize {
<CryptoSm4Gcm as AeadCore>::TagSize::to_usize()
}

pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("SM4_GCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}

pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
let (ciphertext, in_tag) =
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
let in_tag = Tag::from_slice(in_tag);
self.0.decrypt_in_place_detached(nonce, &[], ciphertext, in_tag).is_ok()
}
}
Loading

0 comments on commit f3f26b5

Please sign in to comment.