Skip to content

Commit 6454af4

Browse files
committed
Support non-standard AEAD ciphers with feature v1-aead-extra
- Enable new ciphers with v1-aead-extra - Support xchacha20-ietf-poly1305 with libsodium fixes #8
1 parent ffcd541 commit 6454af4

File tree

6 files changed

+415
-111
lines changed

6 files changed

+415
-111
lines changed

Cargo.toml

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
[package]
22
name = "shadowsocks-crypto"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["luozijun <[email protected]>"]
55
edition = "2018"
66
license = "MIT"
7-
keywords = [ "Cryptography" ]
8-
description = "Shadowsocks Crypto"
9-
repository = "https://github.com/shadowsocks/shadowsocks-crypto"
7+
keywords = ["Cryptography"]
8+
description = "Shadowsocks Crypto"
9+
repository = "https://github.com/shadowsocks/shadowsocks-crypto"
1010
documentation = "https://docs.rs/shadowsocks-crypto"
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

1313

1414
[dependencies]
15-
rand = "0.8"
15+
rand = "0.8"
1616
crypto2 = "0.1"
17+
libsodium-sys = { version = "0.2", optional = true }
18+
libc = { version = "0.2", optional = true }
1719

1820

1921
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
20-
ring = { version = "0.16", optional = true }
22+
ring = { version = "0.16", optional = true }
2123

2224
[target.'cfg(any(target_arch = "arm", target_arch = "aarch64"))'.dependencies]
23-
ring = { version = "0.16", optional = true }
25+
ring = { version = "0.16", optional = true }
2426

2527

2628
[dev-dependencies]
@@ -36,7 +38,8 @@ default = [
3638
std = [
3739
"crypto2/std",
3840
]
39-
nightly = [ ]
40-
v1 = [ ]
41-
v1-stream = [ "v1" ]
42-
v1-aead = [ "v1" ]
41+
nightly = []
42+
v1 = []
43+
v1-stream = ["v1"]
44+
v1-aead = ["v1"]
45+
v1-aead-extra = ["v1-aead", "libsodium-sys", "libc"]

src/v1/aeadcipher.rs renamed to src/v1/aeadcipher/mod.rs

+75-23
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@ pub use crypto2::aeadcipher::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305};
2222
),
2323
feature = "ring"
2424
))]
25-
pub use super::ring::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305};
26-
use super::CipherKind;
25+
pub use crate::v1::ring::{Aes128Gcm, Aes256Gcm, Chacha20Poly1305};
26+
use crate::v1::CipherKind;
27+
28+
#[cfg(feature = "v1-aead-extra")]
29+
mod sodium;
30+
#[cfg(feature = "v1-aead-extra")]
31+
pub use self::sodium::XChacha20Poly1305;
2732

2833
trait AeadCipherExt {
2934
fn ac_kind(&self) -> CipherKind;
@@ -74,6 +79,7 @@ macro_rules! impl_aead_cipher {
7479
};
7580
}
7681

82+
#[cfg(feature = "v1-aead-extra")]
7783
macro_rules! impl_siv_cmac_cipher {
7884
($name:tt, $kind:tt) => {
7985
impl AeadCipherExt for $name {
@@ -119,34 +125,54 @@ macro_rules! impl_siv_cmac_cipher {
119125
};
120126
}
121127

128+
#[cfg(feature = "v1-aead-extra")]
122129
impl_aead_cipher!(Aes128Ccm, AES_128_CCM);
130+
#[cfg(feature = "v1-aead-extra")]
123131
impl_aead_cipher!(Aes256Ccm, AES_256_CCM);
132+
124133
impl_aead_cipher!(Aes128Gcm, AES_128_GCM);
125134
impl_aead_cipher!(Aes256Gcm, AES_256_GCM);
126135

136+
#[cfg(feature = "v1-aead-extra")]
127137
impl_aead_cipher!(Aes128GcmSiv, AES_128_GCM_SIV);
138+
#[cfg(feature = "v1-aead-extra")]
128139
impl_aead_cipher!(Aes256GcmSiv, AES_256_GCM_SIV);
129140

141+
#[cfg(feature = "v1-aead-extra")]
130142
impl_aead_cipher!(Aes128OcbTag128, AES_128_OCB_TAGLEN128);
143+
#[cfg(feature = "v1-aead-extra")]
131144
impl_aead_cipher!(Aes192OcbTag128, AES_192_OCB_TAGLEN128);
145+
#[cfg(feature = "v1-aead-extra")]
132146
impl_aead_cipher!(Aes256OcbTag128, AES_256_OCB_TAGLEN128);
133147

134148
impl_aead_cipher!(Chacha20Poly1305, CHACHA20_POLY1305);
135149

150+
#[cfg(feature = "v1-aead-extra")]
136151
impl_siv_cmac_cipher!(AesSivCmac256, AES_SIV_CMAC_256);
152+
#[cfg(feature = "v1-aead-extra")]
137153
impl_siv_cmac_cipher!(AesSivCmac384, AES_SIV_CMAC_384);
154+
#[cfg(feature = "v1-aead-extra")]
138155
impl_siv_cmac_cipher!(AesSivCmac512, AES_SIV_CMAC_512);
139156

157+
#[cfg(feature = "v1-aead-extra")]
158+
impl_aead_cipher!(XChacha20Poly1305, XCHACHA20_POLY1305);
159+
140160
macro_rules! aead_cipher_variant {
141-
($($name:ident @ $kind:ident,)+) => {
161+
($($(#[cfg($i_meta:meta)])? $name:ident @ $kind:ident,)+) => {
142162
enum AeadCipherInner {
143-
$($name($name),)+
163+
$(
164+
$(#[cfg($i_meta)])?
165+
$name($name),
166+
)+
144167
}
145168

146169
impl AeadCipherInner {
147170
fn new(kind: CipherKind, key: &[u8]) -> Self {
148171
match kind {
149-
$(CipherKind::$kind => AeadCipherInner::$name($name::new(key)),)+
172+
$(
173+
$(#[cfg($i_meta)])?
174+
CipherKind::$kind => AeadCipherInner::$name($name::new(key)),
175+
)+
150176
_ => unreachable!("unrecognized AEAD cipher kind {:?}", kind),
151177
}
152178
}
@@ -155,72 +181,98 @@ macro_rules! aead_cipher_variant {
155181
impl AeadCipherExt for AeadCipherInner {
156182
fn ac_kind(&self) -> CipherKind {
157183
match *self {
158-
$(AeadCipherInner::$name(ref c) => c.ac_kind(),)+
184+
$(
185+
$(#[cfg($i_meta)])?
186+
AeadCipherInner::$name(ref c) => c.ac_kind(),
187+
)+
159188
}
160189
}
161190

162191
fn ac_key_len(&self) -> usize {
163192
match *self {
164-
$(AeadCipherInner::$name(ref c) => c.ac_key_len(),)+
193+
$(
194+
$(#[cfg($i_meta)])?
195+
AeadCipherInner::$name(ref c) => c.ac_key_len(),
196+
)+
165197
}
166198
}
167199
fn ac_block_len(&self) -> usize {
168200
match *self {
169-
$(AeadCipherInner::$name(ref c) => c.ac_block_len(),)+
201+
$(
202+
$(#[cfg($i_meta)])?
203+
AeadCipherInner::$name(ref c) => c.ac_block_len(),
204+
)+
170205
}
171206
}
172207

173208
fn ac_tag_len(&self) -> usize {
174209
match *self {
175-
$(AeadCipherInner::$name(ref c) => c.ac_tag_len(),)+
210+
$(
211+
$(#[cfg($i_meta)])?
212+
AeadCipherInner::$name(ref c) => c.ac_tag_len(),
213+
)+
176214
}
177215
}
178216

179217
fn ac_n_min(&self) -> usize {
180218
match *self {
181-
$(AeadCipherInner::$name(ref c) => c.ac_n_min(),)+
219+
$(
220+
$(#[cfg($i_meta)])?
221+
AeadCipherInner::$name(ref c) => c.ac_n_min(),
222+
)+
182223
}
183224
}
184225
fn ac_n_max(&self) -> usize {
185226
match *self {
186-
$(AeadCipherInner::$name(ref c) => c.ac_n_max(),)+
227+
$(
228+
$(#[cfg($i_meta)])?
229+
AeadCipherInner::$name(ref c) => c.ac_n_max(),
230+
)+
187231
}
188232
}
189233

190234
fn ac_encrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
191235
match *self {
192-
$(AeadCipherInner::$name(ref c) => c.ac_encrypt_slice(nonce, plaintext_in_ciphertext_out),)+
236+
$(
237+
$(#[cfg($i_meta)])?
238+
AeadCipherInner::$name(ref c) => c.ac_encrypt_slice(nonce, plaintext_in_ciphertext_out),
239+
)+
193240
}
194241
}
195242

196243
fn ac_decrypt_slice(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) -> bool {
197244
match *self {
198-
$(AeadCipherInner::$name(ref c) => c.ac_decrypt_slice(nonce, plaintext_in_ciphertext_out),)+
245+
$(
246+
$(#[cfg($i_meta)])?
247+
AeadCipherInner::$name(ref c) => c.ac_decrypt_slice(nonce, plaintext_in_ciphertext_out),
248+
)+
199249
}
200250
}
201251
}
202252
};
203253
}
204254

205255
aead_cipher_variant! {
206-
Aes128Ccm @ AES_128_CCM,
207-
Aes256Ccm @ AES_256_CCM,
256+
#[cfg(feature = "v1-aead-extra")] Aes128Ccm @ AES_128_CCM,
257+
#[cfg(feature = "v1-aead-extra")] Aes256Ccm @ AES_256_CCM,
208258

209-
Aes128OcbTag128 @ AES_128_OCB_TAGLEN128,
210-
Aes192OcbTag128 @ AES_192_OCB_TAGLEN128,
211-
Aes256OcbTag128 @ AES_256_OCB_TAGLEN128,
259+
#[cfg(feature = "v1-aead-extra")] Aes128OcbTag128 @ AES_128_OCB_TAGLEN128,
260+
#[cfg(feature = "v1-aead-extra")] Aes192OcbTag128 @ AES_192_OCB_TAGLEN128,
261+
#[cfg(feature = "v1-aead-extra")] Aes256OcbTag128 @ AES_256_OCB_TAGLEN128,
212262

213263
Aes128Gcm @ AES_128_GCM,
214264
Aes256Gcm @ AES_256_GCM,
215265

216-
AesSivCmac256 @ AES_SIV_CMAC_256,
217-
AesSivCmac384 @ AES_SIV_CMAC_384,
218-
AesSivCmac512 @ AES_SIV_CMAC_512,
266+
#[cfg(feature = "v1-aead-extra")] AesSivCmac256 @ AES_SIV_CMAC_256,
267+
#[cfg(feature = "v1-aead-extra")] AesSivCmac384 @ AES_SIV_CMAC_384,
268+
#[cfg(feature = "v1-aead-extra")] AesSivCmac512 @ AES_SIV_CMAC_512,
219269

220-
Aes128GcmSiv @ AES_128_GCM_SIV,
221-
Aes256GcmSiv @ AES_256_GCM_SIV,
270+
#[cfg(feature = "v1-aead-extra")] Aes128GcmSiv @ AES_128_GCM_SIV,
271+
#[cfg(feature = "v1-aead-extra")] Aes256GcmSiv @ AES_256_GCM_SIV,
222272

223273
Chacha20Poly1305 @ CHACHA20_POLY1305,
274+
275+
#[cfg(feature = "v1-aead-extra")] XChacha20Poly1305 @ XCHACHA20_POLY1305,
224276
}
225277

226278
pub struct AeadCipher {

src/v1/aeadcipher/sodium.rs

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//! `libsodium` provided ciphers
2+
3+
use core::convert::TryInto;
4+
use core::ptr;
5+
6+
use libsodium_sys::{
7+
crypto_aead_xchacha20poly1305_ietf_ABYTES, crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
8+
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, crypto_aead_xchacha20poly1305_ietf_decrypt,
9+
crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
10+
crypto_aead_xchacha20poly1305_ietf_encrypt,
11+
crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
12+
};
13+
14+
use crypto2::streamcipher::Chacha20;
15+
16+
pub struct XChacha20Poly1305 {
17+
ek: [u8; Self::KEY_LEN],
18+
}
19+
20+
impl XChacha20Poly1305 {
21+
pub const KEY_LEN: usize = crypto_aead_xchacha20poly1305_ietf_KEYBYTES as usize;
22+
pub const NONCE_LEN: usize = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES as usize;
23+
pub const TAG_LEN: usize = crypto_aead_xchacha20poly1305_ietf_ABYTES as usize;
24+
pub const N_MIN: usize = Self::NONCE_LEN;
25+
pub const N_MAX: usize = Self::NONCE_LEN;
26+
pub const BLOCK_LEN: usize = Chacha20::BLOCK_LEN;
27+
28+
pub fn new(key: &[u8]) -> Self {
29+
XChacha20Poly1305 {
30+
ek: key
31+
.try_into()
32+
.expect("key.len() != XChacha20Poly1305::KEY_LEN"),
33+
}
34+
}
35+
36+
pub fn encrypt_slice(&self, nonce: &[u8], aad: &[u8], aead_pkt: &mut [u8]) {
37+
debug_assert_eq!(nonce.len(), Self::NONCE_LEN);
38+
debug_assert!(aead_pkt.len() >= Self::TAG_LEN);
39+
40+
unsafe {
41+
let mut clen: libc::c_ulonglong = 0;
42+
let ret = crypto_aead_xchacha20poly1305_ietf_encrypt(
43+
aead_pkt.as_mut_ptr() as *mut _,
44+
&mut clen,
45+
aead_pkt.as_ptr() as *const _,
46+
(aead_pkt.len() - Self::TAG_LEN) as libc::c_ulonglong,
47+
aad.as_ptr() as *const _,
48+
aad.len() as libc::c_ulonglong,
49+
ptr::null(),
50+
nonce.as_ptr() as *const _,
51+
self.ek.as_ptr() as *const _,
52+
);
53+
if ret == 0 || clen != aead_pkt.len() as libc::c_ulonglong {
54+
panic!(
55+
"crypto_aead_xchacha20poly1305_ietf_encrypt ret={} clen={}",
56+
ret, clen
57+
);
58+
}
59+
}
60+
}
61+
62+
pub fn decrypt_slice(&self, nonce: &[u8], aad: &[u8], aead_pkt: &mut [u8]) -> bool {
63+
debug_assert_eq!(nonce.len(), Self::NONCE_LEN);
64+
debug_assert!(aead_pkt.len() >= Self::TAG_LEN);
65+
66+
unsafe {
67+
let mut mlen: libc::c_ulonglong = 0;
68+
let ret = crypto_aead_xchacha20poly1305_ietf_decrypt(
69+
aead_pkt.as_mut_ptr() as *mut _,
70+
&mut mlen,
71+
ptr::null_mut(),
72+
aead_pkt.as_ptr() as *mut _,
73+
aead_pkt.len() as libc::c_ulonglong,
74+
aad.as_ptr() as *const _,
75+
aad.len() as libc::c_ulonglong,
76+
nonce.as_ptr() as *const _,
77+
self.ek.as_ptr(),
78+
);
79+
80+
ret == 0 && mlen == (aead_pkt.len() - Self::TAG_LEN) as libc::c_ulonglong
81+
}
82+
}
83+
84+
#[allow(dead_code)]
85+
pub fn encrypt_slice_detached(
86+
&self,
87+
nonce: &[u8],
88+
aad: &[u8],
89+
plaintext_in_ciphertext_out: &mut [u8],
90+
tag_out: &mut [u8],
91+
) {
92+
debug_assert_eq!(nonce.len(), Self::NONCE_LEN);
93+
debug_assert_eq!(tag_out.len(), Self::TAG_LEN);
94+
95+
unsafe {
96+
let mut maclen: libc::c_ulonglong = 0;
97+
let ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
98+
plaintext_in_ciphertext_out.as_mut_ptr() as *mut _,
99+
tag_out.as_mut_ptr() as *mut _,
100+
&mut maclen,
101+
plaintext_in_ciphertext_out.as_mut_ptr() as *mut _,
102+
plaintext_in_ciphertext_out.len() as libc::c_ulonglong,
103+
aad.as_ptr() as *const _,
104+
aad.len() as libc::c_ulonglong,
105+
ptr::null(),
106+
nonce.as_ptr() as *const _,
107+
self.ek.as_ptr() as *const _,
108+
);
109+
if ret == 0 || maclen != tag_out.len() as libc::c_ulonglong {
110+
panic!(
111+
"crypto_aead_xchacha20poly1305_ietf_encrypt_detached ret={} maclen={}",
112+
ret, maclen
113+
);
114+
}
115+
}
116+
}
117+
118+
#[allow(dead_code)]
119+
pub fn decrypt_slice_detached(
120+
&self,
121+
nonce: &[u8],
122+
aad: &[u8],
123+
ciphertext_in_plaintext_out: &mut [u8],
124+
tag_in: &[u8],
125+
) -> bool {
126+
debug_assert_eq!(nonce.len(), Self::NONCE_LEN);
127+
debug_assert_eq!(tag_in.len(), Self::TAG_LEN);
128+
129+
unsafe {
130+
let ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
131+
ciphertext_in_plaintext_out.as_mut_ptr() as *mut _,
132+
ptr::null_mut(),
133+
ciphertext_in_plaintext_out.as_ptr() as *const _,
134+
ciphertext_in_plaintext_out.len() as libc::c_ulonglong,
135+
tag_in.as_ptr() as *const _,
136+
aad.as_ptr() as *const _,
137+
aad.len() as libc::c_ulonglong,
138+
nonce.as_ptr() as *const _,
139+
self.ek.as_ptr() as *const _,
140+
);
141+
142+
ret == 0
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)