shadowsocks_crypto/v1/
cipher.rs1use crate::kind::{CipherCategory, CipherKind};
2
3#[cfg(feature = "v1-aead")]
4use super::aeadcipher::AeadCipher;
5use super::dummy::DummyCipher;
6#[cfg(feature = "v1-stream")]
7use super::streamcipher::StreamCipher;
8
9#[deprecated(since = "0.5.8", note = "prefer utils::random_iv_or_salt")]
10pub use crate::utils::random_iv_or_salt;
11
12pub fn openssl_bytes_to_key(password: &[u8], key: &mut [u8]) {
14 use md5::{Digest, Md5};
15
16 let key_len = key.len();
17
18 let mut last_digest = None;
19
20 let mut offset = 0usize;
21 while offset < key_len {
22 let mut m = Md5::new();
23 if let Some(digest) = last_digest {
24 m.update(&digest);
25 }
26
27 m.update(password);
28
29 let digest = m.finalize();
30
31 let amt = std::cmp::min(key_len - offset, digest.len());
32 key[offset..offset + amt].copy_from_slice(&digest[..amt]);
33
34 offset += amt;
35 last_digest = Some(digest);
36 }
37}
38
39#[allow(clippy::large_enum_variant)]
41pub enum Cipher {
42 Dummy(DummyCipher),
43 #[cfg(feature = "v1-stream")]
44 Stream(StreamCipher),
45 #[cfg(feature = "v1-aead")]
46 Aead(AeadCipher),
47}
48
49macro_rules! cipher_method_forward {
50 (ref $self:expr, $method:ident $(, $param:expr),*) => {
51 match *$self {
52 Cipher::Dummy(ref c) => c.$method($($param),*),
53 #[cfg(feature = "v1-stream")]
54 Cipher::Stream(ref c) => c.$method($($param),*),
55 #[cfg(feature = "v1-aead")]
56 Cipher::Aead(ref c) => c.$method($($param),*),
57 }
58 };
59
60 (mut $self:expr, $method:ident $(, $param:expr),*) => {
61 match *$self {
62 Cipher::Dummy(ref mut c) => c.$method($($param),*),
63 #[cfg(feature = "v1-stream")]
64 Cipher::Stream(ref mut c) => c.$method($($param),*),
65 #[cfg(feature = "v1-aead")]
66 Cipher::Aead(ref mut c) => c.$method($($param),*),
67 }
68 };
69}
70
71impl Cipher {
72 pub fn new(kind: CipherKind, key: &[u8], iv_or_salt: &[u8]) -> Cipher {
77 let category = kind.category();
78
79 match category {
80 CipherCategory::None => {
81 let _ = key;
82 let _ = iv_or_salt;
83
84 Cipher::Dummy(DummyCipher::new())
85 }
86 #[cfg(feature = "v1-stream")]
87 CipherCategory::Stream => Cipher::Stream(StreamCipher::new(kind, key, iv_or_salt)),
88 #[cfg(feature = "v1-aead")]
89 CipherCategory::Aead => {
90 use cfg_if::cfg_if;
91
92 const SUBKEY_INFO: &'static [u8] = b"ss-subkey";
93 const MAX_KEY_LEN: usize = 64;
94
95 let ikm = key;
96 let mut okm = [0u8; MAX_KEY_LEN];
97
98 cfg_if! {
99 if #[cfg(feature = "ring")] {
100 use ring_compat::ring::hkdf::{Salt, HKDF_SHA1_FOR_LEGACY_USE_ONLY, KeyType};
101
102 struct CryptoKeyType(usize);
103
104 impl KeyType for CryptoKeyType {
105 #[inline]
106 fn len(&self) -> usize {
107 self.0
108 }
109 }
110
111 let salt = Salt::new(HKDF_SHA1_FOR_LEGACY_USE_ONLY, iv_or_salt);
112 let prk = salt.extract(ikm);
113 let rokm = prk
114 .expand(&[SUBKEY_INFO], CryptoKeyType(ikm.len()))
115 .expect("HKDF-SHA1-EXPAND");
116
117 rokm.fill(&mut okm[..ikm.len()]).expect("HKDF-SHA1-FILL");
118 } else {
119 use hkdf::Hkdf;
120 use sha1::Sha1;
121
122 let hk = Hkdf::<Sha1>::new(Some(iv_or_salt), ikm);
123 hk.expand(SUBKEY_INFO, &mut okm).expect("HKDF-SHA1");
124 }
125 }
126
127 let subkey = &okm[..ikm.len()];
128 Cipher::Aead(AeadCipher::new(kind, subkey))
129 }
130 #[allow(unreachable_patterns)]
131 _ => unimplemented!("Category {:?} is not v1 protocol", category),
132 }
133 }
134
135 pub fn category(&self) -> CipherCategory {
137 cipher_method_forward!(ref self, category)
138 }
139
140 pub fn kind(&self) -> CipherKind {
142 cipher_method_forward!(ref self, kind)
143 }
144
145 pub fn tag_len(&self) -> usize {
147 cipher_method_forward!(ref self, tag_len)
148 }
149
150 pub fn encrypt_packet(&mut self, pkt: &mut [u8]) {
155 cipher_method_forward!(mut self, encrypt, pkt)
156 }
157
158 #[must_use]
163 pub fn decrypt_packet(&mut self, pkt: &mut [u8]) -> bool {
164 cipher_method_forward!(mut self, decrypt, pkt)
165 }
166}
167
168#[test]
169fn test_cipher_new_none() {
170 let key = [2u8; 16];
171 let salt = [1u8; 16];
172 let kind = CipherKind::NONE;
173
174 let cipher = Cipher::new(kind, &key, &salt);
175 assert_eq!(cipher.tag_len(), 0);
176}
177
178#[cfg(feature = "v1-aead")]
179#[test]
180fn test_cipher_new_aead() {
181 let key = [2u8; 16];
182 let salt = [1u8; 16];
183 let kind = CipherKind::AES_128_GCM;
184
185 let cipher = Cipher::new(kind, &key, &salt);
186 assert_eq!(cipher.tag_len(), 16);
187}
188
189#[cfg(feature = "v1-stream")]
190#[test]
191fn test_cipher_new_stream() {
192 let key = [2u8; 32];
193 let iv = [1u8; 12];
194 let kind = CipherKind::CHACHA20;
195
196 let cipher = Cipher::new(kind, &key, &iv);
197 assert_eq!(cipher.tag_len(), 0);
198}
199
200#[test]
201fn test_send() {
202 fn test<C: Send>() {}
203 test::<Cipher>();
204}
205
206#[test]
207fn test_sync() {
208 fn test<C: Sync>() {}
209 test::<Cipher>();
210}