1use crate::{blowfish_from_int, BlowfishBlock, SilkroadEncryption, SilkroadSecurityError};
33use bitflags::bitflags;
34use blowfish::cipher::{BlockDecrypt, BlockEncrypt};
35use blowfish::BlowfishLE;
36use byteorder::{ByteOrder, LittleEndian};
37use rand::random;
38
39bitflags! {
40 pub struct SecurityFeature: u8 {
42 const CHECKS = 1;
44 const ENCRYPTION = 2;
46 }
47}
48
49impl Default for SecurityFeature {
50 fn default() -> Self {
51 SecurityFeature::all()
52 }
53}
54
55#[derive(Copy, Clone)]
56struct ActiveEncryptionData {
57 handshake_seed: u64,
58 value_x: u32,
59 value_p: u32,
60 value_a: u32,
61}
62
63#[derive(Default)]
64enum ActiveHandshakeState {
65 #[default]
66 Uninitialized,
67 HandshakeStarted {
68 encryption_seed: Option<ActiveEncryptionData>,
69 },
70 Challenged {
71 blowfish: Box<BlowfishLE>,
72 },
73 FinishedEmpty,
74}
75
76#[derive(Copy, Clone)]
77pub struct PassiveEncryptionInitializationData {
78 pub seed: u64,
79 pub handshake_seed: u64,
80 pub additional_seeds: [u32; 3],
81}
82
83#[derive(Copy, Clone)]
84pub struct CheckBytesInitialization {
85 pub count_seed: u32,
86 pub crc_seed: u32,
87}
88
89#[derive(Copy, Clone)]
90pub struct PassiveInitializationData {
91 pub checks: Option<CheckBytesInitialization>,
92 pub encryption_seed: Option<PassiveEncryptionInitializationData>,
93}
94
95#[derive(Default)]
123pub struct ActiveHandshake {
124 features: SecurityFeature,
125 state: ActiveHandshakeState,
126}
127
128impl ActiveHandshake {
129 pub fn initialize(
138 &mut self,
139 features: SecurityFeature,
140 ) -> Result<PassiveInitializationData, SilkroadSecurityError> {
141 if !matches!(self.state, ActiveHandshakeState::Uninitialized) {
142 return Err(SilkroadSecurityError::AlreadyInitialized);
143 }
144
145 let check_init = if features.contains(SecurityFeature::CHECKS) {
146 Some((u32::from(random::<u8>()), u32::from(random::<u8>())))
147 } else {
148 None
149 };
150
151 if features.contains(SecurityFeature::ENCRYPTION) {
152 let seed = random::<u64>();
153 let handshake_seed = random::<u64>();
154 let value_x = random::<u32>() & 0x7FFFFFFF;
155 let value_g = random::<u32>() & 0x7FFFFFFF;
156 let value_p = random::<u32>() & 0x7FFFFFFF;
157 let value_a = g_pow_x_mod_p(value_p.into(), value_x, value_g);
158 self.state = ActiveHandshakeState::HandshakeStarted {
159 encryption_seed: Some(ActiveEncryptionData {
160 handshake_seed,
161 value_x,
162 value_p,
163 value_a,
164 }),
165 };
166
167 Ok(PassiveInitializationData {
168 checks: check_init.map(|(crc, count)| CheckBytesInitialization {
169 count_seed: count,
170 crc_seed: crc,
171 }),
172 encryption_seed: Some(PassiveEncryptionInitializationData {
173 seed,
174 handshake_seed,
175 additional_seeds: [value_g, value_p, value_a],
176 }),
177 })
178 } else {
179 self.state = ActiveHandshakeState::FinishedEmpty;
180 Ok(PassiveInitializationData {
181 checks: check_init.map(|(crc, count)| CheckBytesInitialization {
182 count_seed: count,
183 crc_seed: crc,
184 }),
185 encryption_seed: None,
186 })
187 }
188 }
189
190 #[allow(unused)]
195 fn initialize_with(&mut self, encryption_data: Option<ActiveEncryptionData>) {
196 self.state = ActiveHandshakeState::HandshakeStarted {
197 encryption_seed: encryption_data,
198 }
199 }
200
201 pub fn start_challenge(
211 &mut self,
212 value_b: u32,
213 client_key: u64,
214 ) -> Result<u64, SilkroadSecurityError> {
215 let ActiveHandshakeState::HandshakeStarted { encryption_seed } = self.state else {
216 return Err(SilkroadSecurityError::SecurityUninitialized);
217 };
218
219 let Some(encryption_setup) = encryption_seed else {
220 return Err(SilkroadSecurityError::SecurityUninitialized);
221 };
222
223 let value_k = g_pow_x_mod_p(
224 encryption_setup.value_p.into(),
225 encryption_setup.value_x,
226 value_b,
227 );
228 let new_key = to_u64(encryption_setup.value_a, value_b);
229 let new_key = transform_key(new_key, value_k, LOBYTE(LOWORD(value_k)) & 0x03);
230 let blowfish = blowfish_from_int(new_key);
231
232 let mut key_bytes: [u8; 8] = client_key.to_le_bytes();
233 blowfish.decrypt_block(BlowfishBlock::from_mut_slice(&mut key_bytes));
234
235 let client_key = LittleEndian::read_u64(&key_bytes);
236 let new_key = to_u64(value_b, encryption_setup.value_a);
237 let new_key = transform_key(new_key, value_k, LOBYTE(LOWORD(value_b)) & 0x07);
238 if new_key != client_key {
239 return Err(SilkroadSecurityError::KeyExchangeMismatch {
240 received: client_key,
241 calculated: new_key,
242 });
243 }
244
245 let new_key = to_u64(encryption_setup.value_a, value_b);
246 let new_key = transform_key(new_key, value_k, LOBYTE(LOWORD(value_k)) & 0x03);
247 let blowfish = blowfish_from_int(new_key);
248
249 let challenge_key = to_u64(encryption_setup.value_a, value_b);
250 let challenge_key = transform_key(
251 challenge_key,
252 value_k,
253 LOBYTE(LOWORD(encryption_setup.value_a)) & 0x07,
254 );
255 let mut key_bytes: [u8; 8] = challenge_key.to_le_bytes();
256 blowfish.encrypt_block(BlowfishBlock::from_mut_slice(&mut key_bytes));
257 let encrypted_challenge = LittleEndian::read_u64(&key_bytes);
258
259 let handshake_seed = transform_key(encryption_setup.handshake_seed, value_k, 3);
260
261 self.state = ActiveHandshakeState::Challenged {
262 blowfish: Box::new(blowfish_from_int(handshake_seed)),
263 };
264
265 Ok(encrypted_challenge)
266 }
267
268 pub fn finish(self) -> Result<Option<SilkroadEncryption>, SilkroadSecurityError> {
276 match self.state {
277 ActiveHandshakeState::Challenged { blowfish } => {
278 Ok(Some(SilkroadEncryption { blowfish }))
279 }
280 ActiveHandshakeState::Uninitialized if self.features.is_empty() => Ok(None),
281 ActiveHandshakeState::FinishedEmpty => Ok(None),
282 ActiveHandshakeState::HandshakeStarted { encryption_seed }
283 if encryption_seed.is_none() =>
284 {
285 Ok(None)
286 }
287 _ => Err(SilkroadSecurityError::InitializationUnfinished),
288 }
289 }
290}
291
292struct PassiveEncryptionData {
293 blowfish: Box<BlowfishLE>,
294 local_public: u32,
295 remote_public: u32,
296 shared_secret: u32,
297 initial_seed: u64,
298}
299
300#[derive(Default)]
301enum PassiveHandshakeState {
302 #[default]
303 Uninitialized,
304 AuthStarted {
305 encryption_seed: Option<PassiveEncryptionData>,
306 },
307 Challenging {
308 blowfish: Box<BlowfishLE>,
309 },
310}
311
312#[derive(Default)]
339pub struct PassiveHandshake {
340 state: PassiveHandshakeState,
341}
342
343impl PassiveHandshake {
344 pub fn initialize(
356 &mut self,
357 init: Option<PassiveEncryptionInitializationData>,
358 ) -> Result<Option<(u64, u32)>, SilkroadSecurityError> {
359 if !matches!(self.state, PassiveHandshakeState::Uninitialized) {
360 return Err(SilkroadSecurityError::InitializationUnfinished);
361 }
362
363 let (encryption_data, challenge) = if let Some(encryption_setup) = &init {
364 let value_g = encryption_setup.additional_seeds[0];
365 let value_p = encryption_setup.additional_seeds[1];
366 let value_a = encryption_setup.additional_seeds[2];
367 let local_private = random::<u32>();
368 let remote_public = g_pow_x_mod_p(value_p as i64, local_private, value_g);
369 let shared_secret = g_pow_x_mod_p(value_p as i64, local_private, value_a);
370 let key = transform_key(
371 to_u64(value_a, remote_public),
372 shared_secret,
373 LOBYTE(LOWORD(shared_secret)) & 0x03,
374 );
375 let blowfish = blowfish_from_int(key);
376 let challenge = transform_key(
377 to_u64(remote_public, value_a),
378 shared_secret,
379 LOBYTE(LOWORD(remote_public)) & 0x07,
380 );
381 let mut challenge_bytes: [u8; 8] = challenge.to_le_bytes();
382 blowfish.encrypt_block(BlowfishBlock::from_mut_slice(&mut challenge_bytes));
383 let encrypted_challenge = u64::from_le_bytes(challenge_bytes);
384 (
385 Some(PassiveEncryptionData {
386 blowfish: Box::new(blowfish),
387 local_public: value_a,
388 remote_public,
389 shared_secret,
390 initial_seed: encryption_setup.handshake_seed,
391 }),
392 Some((encrypted_challenge, remote_public)),
393 )
394 } else {
395 (None, None)
396 };
397
398 self.state = PassiveHandshakeState::AuthStarted {
399 encryption_seed: encryption_data,
400 };
401
402 Ok(challenge)
403 }
404
405 pub fn finish(&mut self, challenge: u64) -> Result<(), SilkroadSecurityError> {
413 let PassiveHandshakeState::AuthStarted {
414 encryption_seed: Some(ref encryption_data),
415 } = self.state
416 else {
417 return Err(SilkroadSecurityError::InitializationUnfinished);
418 };
419
420 let expected = to_u64(encryption_data.local_public, encryption_data.remote_public);
421 let expected_key = transform_key(
422 expected,
423 encryption_data.shared_secret,
424 LOBYTE(LOWORD(encryption_data.local_public)) & 0x07,
425 );
426 let mut expected_key_bytes: [u8; 8] = expected_key.to_le_bytes();
427 encryption_data
428 .blowfish
429 .encrypt_block(BlowfishBlock::from_mut_slice(&mut expected_key_bytes));
430 let encrypted_key = u64::from_le_bytes(expected_key_bytes);
431 if encrypted_key != challenge {
432 return Err(SilkroadSecurityError::KeyExchangeMismatch {
433 received: challenge,
434 calculated: encrypted_key,
435 });
436 }
437 let transformed_key = transform_key(
438 encryption_data.initial_seed,
439 encryption_data.shared_secret,
440 3,
441 );
442 let blowfish = Box::new(blowfish_from_int(transformed_key));
443
444 self.state = PassiveHandshakeState::Challenging { blowfish };
445
446 Ok(())
447 }
448
449 pub fn done(self) -> Result<Option<SilkroadEncryption>, SilkroadSecurityError> {
458 match self.state {
459 PassiveHandshakeState::AuthStarted { encryption_seed } if encryption_seed.is_some() => {
460 Err(SilkroadSecurityError::InitializationUnfinished)
461 }
462 PassiveHandshakeState::Challenging { blowfish } => {
463 Ok(Some(SilkroadEncryption { blowfish }))
464 }
465 _ => Ok(None),
466 }
467 }
468}
469
470#[allow(unused_parens)]
471fn transform_key(val: u64, key: u32, key_byte: u8) -> u64 {
472 let mut stream = val.to_le_bytes();
473
474 stream[0] ^= (stream[0]
475 .wrapping_add(LOBYTE(LOWORD(key)))
476 .wrapping_add(key_byte));
477 stream[1] ^= (stream[1]
478 .wrapping_add(HIBYTE(LOWORD(key)))
479 .wrapping_add(key_byte));
480 stream[2] ^= (stream[2]
481 .wrapping_add(LOBYTE(HIWORD(key)))
482 .wrapping_add(key_byte));
483 stream[3] ^= (stream[3]
484 .wrapping_add(HIBYTE(HIWORD(key)))
485 .wrapping_add(key_byte));
486 stream[4] ^= (stream[4]
487 .wrapping_add(LOBYTE(LOWORD(key)))
488 .wrapping_add(key_byte));
489 stream[5] ^= (stream[5]
490 .wrapping_add(HIBYTE(LOWORD(key)))
491 .wrapping_add(key_byte));
492 stream[6] ^= (stream[6]
493 .wrapping_add(LOBYTE(HIWORD(key)))
494 .wrapping_add(key_byte));
495 stream[7] ^= (stream[7]
496 .wrapping_add(HIBYTE(HIWORD(key)))
497 .wrapping_add(key_byte));
498
499 LittleEndian::read_u64(&stream)
500}
501
502#[allow(non_snake_case)]
503fn LOWORD(a: u32) -> u16 {
504 (a & 0xFFFF) as u16
505}
506
507#[allow(non_snake_case)]
508fn HIWORD(a: u32) -> u16 {
509 ((a >> 16) & 0xFFFF) as u16
510}
511
512#[allow(non_snake_case)]
513fn LOBYTE(a: u16) -> u8 {
514 (a & 0xFF) as u8
515}
516
517#[allow(non_snake_case)]
518fn HIBYTE(a: u16) -> u8 {
519 ((a >> 8) & 0xFF) as u8
520}
521
522fn g_pow_x_mod_p(p: i64, mut x: u32, g: u32) -> u32 {
523 let mut current: i64 = 1;
524 let mut mult: i64 = g as i64;
525
526 while x != 0 {
527 if (x & 1) > 0 {
528 current = (mult * current) % p;
529 }
530 x >>= 1;
531 mult = (mult * mult) % p;
532 }
533 current as u32
534}
535
536fn to_u64(low: u32, high: u32) -> u64 {
537 ((high as u64) << 32) | low as u64
538}
539
540#[cfg(test)]
541mod test {
542 use super::*;
543
544 #[test]
545 fn test_equal() {
546 let mut server_handshake = ActiveHandshake::default();
547 let mut client_handshake = PassiveHandshake::default();
548
549 let init = server_handshake
550 .initialize(SecurityFeature::all())
551 .expect("should be able to initialize");
552 assert!(init.encryption_seed.is_some());
553 assert!(init.checks.is_some());
554 let (key, value_b) = client_handshake
555 .initialize(init.encryption_seed)
556 .expect("should accept initialization")
557 .unwrap();
558 let response = server_handshake
559 .start_challenge(value_b, key)
560 .expect("should accept challenge");
561 client_handshake
562 .finish(response)
563 .expect("should do challenge");
564 let active_encryption = server_handshake
565 .finish()
566 .expect("server should be finished.")
567 .unwrap();
568 let passive_encryption = client_handshake
569 .done()
570 .expect("client should be finished.")
571 .unwrap();
572
573 let encrypted = active_encryption
574 .encrypt(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
575 .expect("Should be able to encrypt");
576
577 let decrypted = passive_encryption
578 .decrypt(&encrypted)
579 .expect("Should be able to decrypt");
580
581 assert_eq!(
582 &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
583 decrypted.as_ref()
584 );
585 }
586
587 #[test]
588 fn finishes_encoding() {
589 let handshake_seed =
590 LittleEndian::read_u64(&[0xbf, 0x89, 0x96, 0x76, 0xae, 0x97, 0x5e, 0x17]);
591 let _value_g = LittleEndian::read_u32(&[0x95, 0x0b, 0xf5, 0x20]);
592 let value_p = LittleEndian::read_u32(&[0x0d, 0xf4, 0x13, 0x52]);
593 let value_x = 189993144; let value_a = LittleEndian::read_u32(&[0x36, 0x44, 0x96, 0x24]);
595
596 let mut security = ActiveHandshake::default();
597 security.initialize_with(Some(ActiveEncryptionData {
598 handshake_seed,
599 value_x,
600 value_p,
601 value_a,
602 }));
603
604 let value_b = LittleEndian::read_u32(&[0x7a, 0x04, 0x39, 0x43]);
605 let key = LittleEndian::read_u64(&[0x69, 0x02, 0xec, 0x3f, 0x16, 0xbb, 0x18, 0x64]);
606
607 let result = security.start_challenge(value_b, key).unwrap();
608
609 let result_expected_bytes = &[0xbe, 0x6f, 0x5e, 0xd4, 0x19, 0x79, 0x7d, 0x26];
610 let result_expected = LittleEndian::read_u64(result_expected_bytes);
611
612 assert_eq!(result, result_expected);
613 assert!(security.finish().is_ok());
614 }
615}