Skip to content

Commit f3f26b5

Browse files
committed
support (non-standard) sm4-gcm, sm4-ccm in v1-aead-extra
- 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.
1 parent 092840c commit f3f26b5

File tree

6 files changed

+412
-8
lines changed

6 files changed

+412
-8
lines changed

Diff for: Cargo.toml

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shadowsocks-crypto"
3-
version = "0.5.2"
3+
version = "0.5.3"
44
authors = ["luozijun <[email protected]>", "ty <[email protected]>"]
55
edition = "2021"
66
license = "MIT"
@@ -12,14 +12,20 @@ rust-version = "1.61"
1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313

1414
[features]
15-
default = [
16-
"v1",
17-
"v1-aead",
18-
]
15+
default = ["v1", "v1-aead"]
1916
v1 = []
2017
v1-stream = ["v1", "chacha20", "aes", "ctr", "camellia"]
2118
v1-aead = ["v1", "aes-gcm", "chacha20poly1305", "hkdf", "sha1"]
22-
v1-aead-extra = ["v1-aead", "aes-gcm-siv", "ccm", "aes"]
19+
v1-aead-extra = [
20+
"v1-aead",
21+
"aes-gcm-siv",
22+
"ccm",
23+
"aes",
24+
"sm4",
25+
"ghash",
26+
"aead",
27+
"subtle",
28+
]
2329
v2 = ["aes", "aes-gcm", "blake3", "chacha20poly1305", "bytes"]
2430
v2-extra = ["v2", "chacha20poly1305/reduced-round"]
2531

@@ -42,6 +48,10 @@ aes = { version = "0.8", optional = true }
4248
ctr = { version = "0.9", optional = true }
4349
bytes = { version = "1.3", optional = true }
4450
camellia = { version = "0.1", optional = true }
51+
sm4 = { version = "0.5", optional = true }
52+
ghash = { version = "0.5", optional = true }
53+
aead = { version = "0.5", optional = true }
54+
subtle = { version = "2.5", optional = true }
4555

4656
#[target.'cfg(all(unix, any(target_arch = "x86", target_arch = "x86_64")))'.dependencies]
4757
#md-5 = { version = "0.10", features = ["asm"] }

Diff for: src/kind.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Cipher Kind
22
33
#[cfg(feature = "v1-aead-extra")]
4-
use crate::v1::aeadcipher::{Aes128Ccm, Aes128GcmSiv, Aes256Ccm, Aes256GcmSiv, XChaCha20Poly1305};
4+
use crate::v1::aeadcipher::{Aes128Ccm, Aes128GcmSiv, Aes256Ccm, Aes256GcmSiv, Sm4Ccm, Sm4Gcm, XChaCha20Poly1305};
55
#[cfg(feature = "v1-aead")]
66
use crate::v1::aeadcipher::{Aes128Gcm, Aes256Gcm, ChaCha20Poly1305};
77

@@ -230,6 +230,15 @@ pub enum CipherKind {
230230
/// AEAD_XCHACHA20_POLY1305
231231
XCHACHA20_POLY1305,
232232

233+
#[cfg(feature = "v1-aead-extra")]
234+
#[cfg_attr(docrs, doc(cfg(feature = "v1-aead-extra")))]
235+
/// SM4_GCM
236+
SM4_GCM,
237+
#[cfg(feature = "v1-aead-extra")]
238+
#[cfg_attr(docrs, doc(cfg(feature = "v1-aead-extra")))]
239+
/// SM4_GCM
240+
SM4_CCM,
241+
233242
#[cfg(feature = "v2")]
234243
#[cfg_attr(docrs, doc(cfg(feature = "v2")))]
235244
/// 2022-blake3-aes-128-gcm
@@ -302,7 +311,9 @@ impl CipherKind {
302311
AES_128_GCM | AES_256_GCM | CHACHA20_POLY1305 => true,
303312

304313
#[cfg(feature = "v1-aead-extra")]
305-
AES_128_CCM | AES_256_CCM | AES_128_GCM_SIV | AES_256_GCM_SIV | XCHACHA20_POLY1305 => true,
314+
AES_128_CCM | AES_256_CCM | AES_128_GCM_SIV | AES_256_GCM_SIV | XCHACHA20_POLY1305 | SM4_GCM | SM4_CCM => {
315+
true
316+
}
306317

307318
_ => false,
308319
}
@@ -426,6 +437,11 @@ impl CipherKind {
426437
#[cfg(feature = "v1-aead-extra")]
427438
XCHACHA20_POLY1305 => XChaCha20Poly1305::key_size(),
428439

440+
#[cfg(feature = "v1-aead-extra")]
441+
SM4_GCM => Sm4Gcm::key_size(),
442+
#[cfg(feature = "v1-aead-extra")]
443+
SM4_CCM => Sm4Ccm::key_size(),
444+
429445
#[cfg(feature = "v2")]
430446
AEAD2022_BLAKE3_AES_128_GCM => Aead2022Aes128Gcm::key_size(),
431447
#[cfg(feature = "v2")]
@@ -515,6 +531,11 @@ impl CipherKind {
515531
#[cfg(feature = "v1-aead-extra")]
516532
XCHACHA20_POLY1305 => XChaCha20Poly1305::tag_size(),
517533

534+
#[cfg(feature = "v1-aead-extra")]
535+
SM4_GCM => Sm4Gcm::tag_size(),
536+
#[cfg(feature = "v1-aead-extra")]
537+
SM4_CCM => Sm4Ccm::tag_size(),
538+
518539
#[cfg(feature = "v2")]
519540
AEAD2022_BLAKE3_AES_128_GCM => Aead2022Aes128Gcm::tag_size(),
520541
#[cfg(feature = "v2")]
@@ -669,6 +690,11 @@ impl core::fmt::Display for CipherKind {
669690
#[cfg(feature = "v1-aead-extra")]
670691
CipherKind::XCHACHA20_POLY1305 => "xchacha20-ietf-poly1305",
671692

693+
#[cfg(feature = "v1-aead-extra")]
694+
CipherKind::SM4_GCM => "sm4-gcm",
695+
#[cfg(feature = "v1-aead-extra")]
696+
CipherKind::SM4_CCM => "sm4-ccm",
697+
672698
#[cfg(feature = "v2")]
673699
CipherKind::AEAD2022_BLAKE3_AES_128_GCM => "2022-blake3-aes-128-gcm",
674700
#[cfg(feature = "v2")]
@@ -814,6 +840,11 @@ impl core::str::FromStr for CipherKind {
814840
#[cfg(feature = "v1-aead-extra")]
815841
"xchacha20-ietf-poly1305" => Ok(XCHACHA20_POLY1305),
816842

843+
#[cfg(feature = "v1-aead-extra")]
844+
"sm4-gcm" => Ok(SM4_GCM),
845+
#[cfg(feature = "v1-aead-extra")]
846+
"sm4-ccm" => Ok(SM4_CCM),
847+
817848
#[cfg(feature = "v2")]
818849
"2022-blake3-aes-128-gcm" => Ok(AEAD2022_BLAKE3_AES_128_GCM),
819850
#[cfg(feature = "v2")]

Diff for: src/v1/aeadcipher/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@ mod aes_gcm;
77
mod aes_gcm_siv;
88
mod chacha20_poly1305;
99
#[cfg(feature = "v1-aead-extra")]
10+
mod sm4_ccm;
11+
#[cfg(feature = "v1-aead-extra")]
12+
mod sm4_gcm;
13+
#[cfg(feature = "v1-aead-extra")]
14+
mod sm4_gcm_cipher;
15+
#[cfg(feature = "v1-aead-extra")]
1016
mod xchacha20_poly1305;
1117

1218
#[cfg(feature = "v1-aead-extra")]
1319
pub use self::{
1420
aes_ccm::{Aes128Ccm, Aes256Ccm},
1521
aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv},
22+
sm4_ccm::Sm4Ccm,
23+
sm4_gcm::Sm4Gcm,
1624
xchacha20_poly1305::XChaCha20Poly1305,
1725
};
1826
pub use self::{
@@ -34,6 +42,10 @@ enum AeadCipherVariant {
3442
Aes128Ccm(Aes128Ccm),
3543
#[cfg(feature = "v1-aead-extra")]
3644
Aes256Ccm(Aes256Ccm),
45+
#[cfg(feature = "v1-aead-extra")]
46+
Sm4Gcm(Sm4Gcm),
47+
#[cfg(feature = "v1-aead-extra")]
48+
Sm4Ccm(Sm4Ccm),
3749
}
3850

3951
impl AeadCipherVariant {
@@ -52,6 +64,10 @@ impl AeadCipherVariant {
5264
CipherKind::AES_128_CCM => AeadCipherVariant::Aes128Ccm(Aes128Ccm::new(key)),
5365
#[cfg(feature = "v1-aead-extra")]
5466
CipherKind::AES_256_CCM => AeadCipherVariant::Aes256Ccm(Aes256Ccm::new(key)),
67+
#[cfg(feature = "v1-aead-extra")]
68+
CipherKind::SM4_GCM => AeadCipherVariant::Sm4Gcm(Sm4Gcm::new(key)),
69+
#[cfg(feature = "v1-aead-extra")]
70+
CipherKind::SM4_CCM => AeadCipherVariant::Sm4Ccm(Sm4Ccm::new(key)),
5571
_ => unreachable!("{:?} is not an AEAD cipher", kind),
5672
}
5773
}
@@ -71,6 +87,10 @@ impl AeadCipherVariant {
7187
AeadCipherVariant::Aes128Ccm(..) => Aes128Ccm::nonce_size(),
7288
#[cfg(feature = "v1-aead-extra")]
7389
AeadCipherVariant::Aes256Ccm(..) => Aes256Ccm::nonce_size(),
90+
#[cfg(feature = "v1-aead-extra")]
91+
AeadCipherVariant::Sm4Gcm(..) => Sm4Gcm::nonce_size(),
92+
#[cfg(feature = "v1-aead-extra")]
93+
AeadCipherVariant::Sm4Ccm(..) => Sm4Ccm::nonce_size(),
7494
}
7595
}
7696

@@ -89,6 +109,10 @@ impl AeadCipherVariant {
89109
AeadCipherVariant::Aes128Ccm(..) => CipherKind::AES_128_CCM,
90110
#[cfg(feature = "v1-aead-extra")]
91111
AeadCipherVariant::Aes256Ccm(..) => CipherKind::AES_256_CCM,
112+
#[cfg(feature = "v1-aead-extra")]
113+
AeadCipherVariant::Sm4Gcm(..) => CipherKind::SM4_GCM,
114+
#[cfg(feature = "v1-aead-extra")]
115+
AeadCipherVariant::Sm4Ccm(..) => CipherKind::SM4_CCM,
92116
}
93117
}
94118

@@ -107,6 +131,10 @@ impl AeadCipherVariant {
107131
AeadCipherVariant::Aes128Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
108132
#[cfg(feature = "v1-aead-extra")]
109133
AeadCipherVariant::Aes256Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
134+
#[cfg(feature = "v1-aead-extra")]
135+
AeadCipherVariant::Sm4Gcm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
136+
#[cfg(feature = "v1-aead-extra")]
137+
AeadCipherVariant::Sm4Ccm(ref mut c) => c.encrypt(nonce, plaintext_in_ciphertext_out),
110138
}
111139
}
112140

@@ -125,6 +153,10 @@ impl AeadCipherVariant {
125153
AeadCipherVariant::Aes128Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
126154
#[cfg(feature = "v1-aead-extra")]
127155
AeadCipherVariant::Aes256Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
156+
#[cfg(feature = "v1-aead-extra")]
157+
AeadCipherVariant::Sm4Gcm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
158+
#[cfg(feature = "v1-aead-extra")]
159+
AeadCipherVariant::Sm4Ccm(ref mut c) => c.decrypt(nonce, ciphertext_in_plaintext_out),
128160
}
129161
}
130162
}

Diff for: src/v1/aeadcipher/sm4_ccm.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//! SM4_CCM
2+
//!
3+
//! https://datatracker.ietf.org/doc/html/rfc8998
4+
5+
use ccm::{
6+
aead::{generic_array::typenum::Unsigned, AeadCore, AeadInPlace, KeyInit, KeySizeUser},
7+
consts::{U12, U16},
8+
Ccm,
9+
Nonce,
10+
Tag,
11+
};
12+
use sm4::Sm4;
13+
14+
pub struct Sm4Ccm(Ccm<Sm4, U16, U12>);
15+
16+
impl Sm4Ccm {
17+
pub fn new(key: &[u8]) -> Sm4Ccm {
18+
Sm4Ccm(Ccm::new_from_slice(key).expect("Sm4Ccm"))
19+
}
20+
21+
pub fn key_size() -> usize {
22+
<Ccm<Sm4, U16, U12> as KeySizeUser>::KeySize::to_usize()
23+
}
24+
25+
pub fn nonce_size() -> usize {
26+
<Ccm<Sm4, U16, U12> as AeadCore>::NonceSize::to_usize()
27+
}
28+
29+
pub fn tag_size() -> usize {
30+
<Ccm<Sm4, U16, U12> as AeadCore>::TagSize::to_usize()
31+
}
32+
33+
pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
34+
let nonce = Nonce::from_slice(nonce);
35+
let (plaintext, out_tag) =
36+
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
37+
let tag = self
38+
.0
39+
.encrypt_in_place_detached(nonce, &[], plaintext)
40+
.expect("AES_128_CCM encrypt");
41+
out_tag.copy_from_slice(tag.as_slice())
42+
}
43+
44+
pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
45+
let nonce = Nonce::from_slice(nonce);
46+
let (ciphertext, in_tag) =
47+
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
48+
let in_tag = Tag::from_slice(in_tag);
49+
self.0.decrypt_in_place_detached(nonce, &[], ciphertext, in_tag).is_ok()
50+
}
51+
}
52+
53+
#[cfg(test)]
54+
mod test {
55+
use ccm::{
56+
aead::{Aead, KeyInit, Payload},
57+
consts::{U12, U16},
58+
Ccm,
59+
};
60+
use sm4::Sm4;
61+
62+
#[test]
63+
fn test_sm4_ccm() {
64+
let iv = hex::decode("00001234567800000000ABCD").unwrap();
65+
let key = hex::decode("0123456789ABCDEFFEDCBA9876543210").unwrap();
66+
let plain_text = hex::decode("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA").unwrap();
67+
let aad = hex::decode("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2").unwrap();
68+
let mut cipher_text = hex::decode("48AF93501FA62ADBCD414CCE6034D895DDA1BF8F132F042098661572E7483094FD12E518CE062C98ACEE28D95DF4416BED31A2F04476C18BB40C84A74B97DC5B").unwrap();
69+
let mut tag = hex::decode("16842D4FA186F56AB33256971FA110F4").unwrap();
70+
cipher_text.append(&mut tag); // postfix tag
71+
72+
let nonce = super::Nonce::from_slice(&iv);
73+
74+
let cipher = Ccm::<Sm4, U16, U12>::new_from_slice(&key).unwrap();
75+
let plain_text_payload = Payload {
76+
msg: &plain_text,
77+
aad: &aad,
78+
};
79+
let result = cipher.encrypt(nonce, plain_text_payload).unwrap();
80+
81+
assert_eq!(result, cipher_text);
82+
}
83+
}

Diff for: src/v1/aeadcipher/sm4_gcm.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! SM4-GCM
2+
3+
use aead::{AeadCore, AeadInPlace, Key, KeyInit, KeySizeUser};
4+
use sm4::cipher::Unsigned;
5+
6+
use super::sm4_gcm_cipher::{Nonce, Sm4Gcm as CryptoSm4Gcm, Tag};
7+
8+
pub struct Sm4Gcm(CryptoSm4Gcm);
9+
10+
impl Sm4Gcm {
11+
pub fn new(key: &[u8]) -> Sm4Gcm {
12+
let key = Key::<CryptoSm4Gcm>::from_slice(key);
13+
Sm4Gcm(CryptoSm4Gcm::new(key))
14+
}
15+
16+
pub fn key_size() -> usize {
17+
<CryptoSm4Gcm as KeySizeUser>::KeySize::to_usize()
18+
}
19+
20+
pub fn nonce_size() -> usize {
21+
<CryptoSm4Gcm as AeadCore>::NonceSize::to_usize()
22+
}
23+
24+
pub fn tag_size() -> usize {
25+
<CryptoSm4Gcm as AeadCore>::TagSize::to_usize()
26+
}
27+
28+
pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
29+
let nonce = Nonce::from_slice(nonce);
30+
let (plaintext, out_tag) =
31+
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
32+
let tag = self
33+
.0
34+
.encrypt_in_place_detached(nonce, &[], plaintext)
35+
.expect("SM4_GCM encrypt");
36+
out_tag.copy_from_slice(tag.as_slice())
37+
}
38+
39+
pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
40+
let nonce = Nonce::from_slice(nonce);
41+
let (ciphertext, in_tag) =
42+
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
43+
let in_tag = Tag::from_slice(in_tag);
44+
self.0.decrypt_in_place_detached(nonce, &[], ciphertext, in_tag).is_ok()
45+
}
46+
}

0 commit comments

Comments
 (0)