1use rc4::{KeyInit, Rc4 as Rc4Cipher, StreamCipher};
7use ring::agreement::{self, EphemeralPrivateKey, UnparsedPublicKey};
8use ring::rand::SystemRandom;
9use sha1::{Digest, Sha1};
10use std::sync::Mutex;
11
12use crate::error::{Aria2Error, Result};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum CryptoMethod {
17 Plain = 0x0001,
19 Rc4 = 0x0002,
21 Aes128Cbc = 0x0003,
23}
24
25impl CryptoMethod {
26 pub fn from_u16(value: u16) -> Option<Self> {
28 match value {
29 0x0001 => Some(CryptoMethod::Plain),
30 0x0002 => Some(CryptoMethod::Rc4),
31 0x0003 => Some(CryptoMethod::Aes128Cbc),
32 _ => None,
33 }
34 }
35
36 pub fn to_u16(self) -> u16 {
38 self as u16
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
44#[allow(clippy::large_enum_variant)]
45pub enum MseState {
46 Idle,
48 MethodSelectionSent,
50 KeyExchangeInProgress,
52 VerificationPending,
54 Established(MseCryptoContext),
56 Failed(String),
58}
59
60pub struct MseCryptoContext {
62 send_key: Vec<u8>,
63 recv_key: Vec<u8>,
64 crypto_method: CryptoMethod,
65 rc4_send: Option<Rc4Cipher>,
66 rc4_recv: Option<Rc4Cipher>,
67}
68
69impl std::fmt::Debug for MseCryptoContext {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.debug_struct("MseCryptoContext")
72 .field("send_key", &self.send_key)
73 .field("recv_key", &self.recv_key)
74 .field("crypto_method", &self.crypto_method)
75 .field("rc4_send", &self.rc4_send.is_some())
76 .field("rc4_recv", &self.rc4_recv.is_some())
77 .finish()
78 }
79}
80
81impl Clone for MseCryptoContext {
82 fn clone(&self) -> Self {
83 match self.crypto_method {
85 CryptoMethod::Rc4 => {
86 Self::new(&self.send_key, &self.recv_key, self.crypto_method)
88 }
89 _ => Self {
90 send_key: self.send_key.clone(),
91 recv_key: self.recv_key.clone(),
92 crypto_method: self.crypto_method,
93 rc4_send: None,
94 rc4_recv: None,
95 },
96 }
97 }
98}
99
100impl PartialEq for MseCryptoContext {
101 fn eq(&self, other: &Self) -> bool {
102 self.send_key == other.send_key
103 && self.recv_key == other.recv_key
104 && self.crypto_method == other.crypto_method
105 }
106}
107
108impl MseCryptoContext {
109 pub fn new(send_key: &[u8], recv_key: &[u8], method: CryptoMethod) -> Self {
114 match method {
115 CryptoMethod::Rc4 => {
116 let mut rc4_send = Rc4Cipher::new_from_slice(send_key).unwrap();
118 let mut discard = vec![0u8; 1024];
119 rc4_send.apply_keystream(&mut discard);
120
121 let mut rc4_recv = Rc4Cipher::new_from_slice(recv_key).unwrap();
123 let mut discard = vec![0u8; 1024];
124 rc4_recv.apply_keystream(&mut discard);
125
126 Self {
127 send_key: send_key.to_vec(),
128 recv_key: recv_key.to_vec(),
129 crypto_method: method,
130 rc4_send: Some(rc4_send),
131 rc4_recv: Some(rc4_recv),
132 }
133 }
134 _ => Self {
135 send_key: send_key.to_vec(),
136 recv_key: recv_key.to_vec(),
137 crypto_method: method,
138 rc4_send: None,
139 rc4_recv: None,
140 },
141 }
142 }
143
144 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
146 match self.crypto_method {
147 CryptoMethod::Rc4 => {
148 if let Some(ref mut rc4) = self.rc4_send {
149 let mut data = plaintext.to_vec();
150 rc4.apply_keystream(&mut data);
151 Ok(data)
152 } else {
153 Err(Aria2Error::Fatal(crate::error::FatalError::Config(
154 "RC4 cipher not initialized".to_string(),
155 )))
156 }
157 }
158 CryptoMethod::Plain | CryptoMethod::Aes128Cbc => {
159 Ok(plaintext.to_vec())
161 }
162 }
163 }
164
165 pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>> {
167 match self.crypto_method {
168 CryptoMethod::Rc4 => {
169 if let Some(ref mut rc4) = self.rc4_recv {
170 let mut data = ciphertext.to_vec();
171 rc4.apply_keystream(&mut data);
172 Ok(data)
173 } else {
174 Err(Aria2Error::Fatal(crate::error::FatalError::Config(
175 "RC4 cipher not initialized".to_string(),
176 )))
177 }
178 }
179 CryptoMethod::Plain | CryptoMethod::Aes128Cbc => {
180 Ok(ciphertext.to_vec())
182 }
183 }
184 }
185
186 pub fn crypto_method(&self) -> CryptoMethod {
188 self.crypto_method
189 }
190
191 pub fn is_encrypted(&self) -> bool {
193 self.crypto_method != CryptoMethod::Plain
194 }
195}
196
197impl Default for MseCryptoContext {
199 fn default() -> Self {
200 Self {
201 send_key: vec![],
202 recv_key: vec![],
203 crypto_method: CryptoMethod::Plain,
204 rc4_send: None,
205 rc4_recv: None,
206 }
207 }
208}
209
210pub struct MseHandshakeManager {
212 state: Mutex<MseState>,
216 local_dh_private_key: Option<EphemeralPrivateKey>,
217 local_dh_pubkey: Vec<u8>,
218 remote_dh_pubkey: Option<Vec<u8>>,
219 pub(crate) shared_secret: Option<Vec<u8>>,
220 info_hash: [u8; 20],
221 pad_length: u16,
222}
223
224impl MseHandshakeManager {
225 pub fn new(info_hash: [u8; 20]) -> Result<Self> {
227 let rng = SystemRandom::new();
228
229 let private_key = EphemeralPrivateKey::generate(&agreement::X25519, &rng).map_err(|e| {
231 Aria2Error::Fatal(crate::error::FatalError::Config(format!(
232 "Failed to generate DH key: {}",
233 e
234 )))
235 })?;
236
237 let public_key = private_key.compute_public_key().map_err(|e| {
239 Aria2Error::Fatal(crate::error::FatalError::Config(format!(
240 "Failed to compute public key: {}",
241 e
242 )))
243 })?;
244
245 let pubkey_slice = public_key.as_ref();
246 let mut local_dh_pubkey = vec![0u8; pubkey_slice.len()];
247 local_dh_pubkey.copy_from_slice(pubkey_slice);
248
249 use rand::RngCore;
251 let mut rng_core = rand::thread_rng();
252 let pad_length = rng_core.next_u32() as u16 % 513;
253
254 Ok(Self {
255 state: Mutex::new(MseState::Idle),
256 local_dh_private_key: Some(private_key),
257 local_dh_pubkey,
258 remote_dh_pubkey: None,
259 shared_secret: None,
260 info_hash,
261 pad_length,
262 })
263 }
264
265 pub fn build_method_selection(&self) -> Vec<u8> {
269 b"\x13MSegadd".to_vec()
270 }
271
272 pub fn parse_remote_method_selection(data: &[u8]) -> Result<CryptoMethod> {
276 if data.is_empty() {
277 return Err(Aria2Error::Parse("Empty method selection".to_string()));
278 }
279
280 if data == b"\x13MSegadd" {
282 return Ok(CryptoMethod::Rc4); }
284
285 if data == b"\x00" || data[0] == 0x00 {
287 return Ok(CryptoMethod::Plain);
288 }
289
290 Err(Aria2Error::Parse(format!(
291 "Invalid method selection: {:?}",
292 data
293 )))
294 }
295
296 pub fn build_key_exchange_payload(&self, crypto_methods: &[CryptoMethod]) -> Result<Vec<u8>> {
308 let mut payload = Vec::new();
309
310 payload.extend_from_slice(&self.pad_length.to_be_bytes());
312
313 payload.extend_from_slice(&self.pad_length.to_be_bytes());
315
316 let mut crypto_provisions: u16 = 0;
318 for method in crypto_methods {
319 crypto_provisions |= method.to_u16();
320 }
321 payload.extend_from_slice(&crypto_provisions.to_be_bytes());
322
323 use rand::RngCore;
325 let mut rng_core = rand::thread_rng();
326 let mut pad_data = vec![0u8; self.pad_length as usize];
327 rng_core.fill_bytes(&mut pad_data);
328 payload.extend_from_slice(&pad_data);
329
330 payload.extend_from_slice(&self.local_dh_pubkey);
332
333 Ok(payload)
334 }
335
336 pub fn process_remote_key_exchange(&mut self, data: &[u8]) -> Result<()> {
338 if data.len() < 6 + 32 {
342 return Err(Aria2Error::Parse(
343 "Key exchange payload too short".to_string(),
344 ));
345 }
346
347 let _pad_d = u16::from_be_bytes([data[0], data[1]]);
349 let _pad_len = u16::from_be_bytes([data[2], data[3]]);
350 let _crypto_pro = u16::from_be_bytes([data[4], data[5]]);
351 let pad_end = 6 + (_pad_len as usize);
355 if data.len() < pad_end + 32 {
356 return Err(Aria2Error::Parse(
357 "Key exchange payload truncated".to_string(),
358 ));
359 }
360
361 let remote_pubkey_start = data.len() - 32;
363 let remote_pubkey = &data[remote_pubkey_start..];
364
365 self.remote_dh_pubkey = Some(remote_pubkey.to_vec());
367
368 let peer_public_key = UnparsedPublicKey::new(&agreement::X25519, remote_pubkey);
370
371 if let Some(private_key) = self.local_dh_private_key.take() {
372 let shared_secret: Vec<u8> = agreement::agree_ephemeral(
373 private_key,
374 &peer_public_key,
375 |shared_secret: &[u8]| shared_secret.to_vec(),
376 )
377 .map_err(|e| {
378 Aria2Error::Fatal(crate::error::FatalError::Config(format!(
379 "DH key agreement failed: {}",
380 e
381 )))
382 })?;
383
384 self.shared_secret = Some(shared_secret);
385 } else {
386 return Err(Aria2Error::Fatal(crate::error::FatalError::Config(
387 "Local DH private key not set".to_string(),
388 )));
389 }
390
391 Ok(())
392 }
393
394 pub fn build_verification_payload(&self, selected_method: CryptoMethod) -> Result<Vec<u8>> {
402 let skey = self.compute_skey()?;
403 let mut payload = Vec::new();
404
405 payload.extend_from_slice(&skey);
407
408 payload.extend_from_slice(&0x0001u16.to_be_bytes());
410
411 payload.extend_from_slice(&selected_method.to_u16().to_be_bytes());
413
414 payload.extend_from_slice(&0x0000u16.to_be_bytes());
416
417 Ok(payload)
418 }
419
420 pub fn process_remote_verification(&mut self, data: &[u8]) -> Result<MseCryptoContext> {
422 if data.len() < 26 {
423 return Err(Aria2Error::Parse(
424 "Verification payload too short".to_string(),
425 ));
426 }
427
428 let remote_skey = &data[..20];
430 let vc = u16::from_be_bytes([data[20], data[21]]);
431 let crypto_select = u16::from_be_bytes([data[22], data[23]]);
432 if vc != 0x0001 {
436 return Err(Aria2Error::Parse(format!("Invalid VC value: {:#06x}", vc)));
437 }
438
439 let expected_skey = self.compute_skey()?;
441 if remote_skey != expected_skey.as_slice() {
442 return Err(Aria2Error::Checksum("SKEY verification failed".to_string()));
443 }
444
445 let selected_method = CryptoMethod::from_u16(crypto_select).ok_or_else(|| {
447 Aria2Error::Parse(format!(
448 "Unknown crypto method selected: {:#06x}",
449 crypto_select
450 ))
451 })?;
452
453 let shared_secret = self.shared_secret.as_ref().ok_or_else(|| {
455 Aria2Error::Fatal(crate::error::FatalError::Config(
456 "Shared secret not computed".to_string(),
457 ))
458 })?;
459
460 let (send_key, recv_key) = Self::derive_keys(&expected_skey, shared_secret);
461
462 let ctx = MseCryptoContext::new(&send_key, &recv_key, selected_method);
464
465 Ok(ctx)
466 }
467
468 pub(crate) fn compute_skey(&self) -> Result<[u8; 20]> {
470 let shared_secret = self.shared_secret.as_ref().ok_or_else(|| {
471 Aria2Error::Fatal(crate::error::FatalError::Config(
472 "Shared secret not computed yet".to_string(),
473 ))
474 })?;
475
476 let mut hasher = Sha1::new();
477 hasher.update(self.info_hash);
478 hasher.update(shared_secret);
479 let result = hasher.finalize();
480
481 let mut skey = [0u8; 20];
482 skey.copy_from_slice(&result);
483 Ok(skey)
484 }
485
486 pub(crate) fn derive_keys(skey: &[u8], shared_secret: &[u8]) -> (Vec<u8>, Vec<u8>) {
493 let mut hasher_a = Sha1::new();
495 hasher_a.update(skey);
496 hasher_a.update(b"keyA");
497 hasher_a.update(shared_secret);
498 let result_a = hasher_a.finalize();
499
500 let mut hasher_b = Sha1::new();
502 hasher_b.update(skey);
503 hasher_b.update(b"keyB");
504 hasher_b.update(shared_secret);
505 let result_b = hasher_b.finalize();
506
507 (result_a[..16].to_vec(), result_b[..16].to_vec())
509 }
510
511 pub fn plaintext_fallback() -> MseCryptoContext {
513 MseCryptoContext::default()
514 }
515
516 pub fn state(&self) -> MseState {
518 self.state.lock().unwrap_or_else(|e| e.into_inner()).clone()
519 }
520
521 pub fn set_state(&self, state: MseState) {
523 *self.state.lock().unwrap_or_else(|e| e.into_inner()) = state;
524 }
525
526 pub fn local_dh_pubkey(&self) -> &[u8] {
528 &self.local_dh_pubkey
529 }
530
531 pub fn shared_secret(&self) -> Option<&[u8]> {
533 self.shared_secret.as_deref()
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[test]
542 fn test_crypto_method_conversions() {
543 assert_eq!(CryptoMethod::from_u16(0x0001), Some(CryptoMethod::Plain));
544 assert_eq!(CryptoMethod::from_u16(0x0002), Some(CryptoMethod::Rc4));
545 assert_eq!(
546 CryptoMethod::from_u16(0x0003),
547 Some(CryptoMethod::Aes128Cbc)
548 );
549 assert_eq!(CryptoMethod::from_u16(0x9999), None);
550
551 assert_eq!(CryptoMethod::Plain.to_u16(), 0x0001);
552 assert_eq!(CryptoMethod::Rc4.to_u16(), 0x0002);
553 assert_eq!(CryptoMethod::Aes128Cbc.to_u16(), 0x0003);
554 }
555}