1use aes::Aes128;
25use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
26
27use crate::packet::{PacketType, StreamingAuthHeader};
28
29const STREAMING_AUTH_HEADER_SIZE: usize = 4;
30const STREAMING_AUTH_PAYLOAD_SIZE: usize = 32;
31const STREAMING_AUTH_RESULT_FLAG: u16 = 1;
32const STREAMING_AUTH_GRANTED_FLAG: u16 = 1 << 1;
33const AES_BLOCK_SIZE: usize = 16;
34pub(crate) const STREAMING_AUTH_RESPONSE_ID: u8 = 0;
35
36pub const STREAMING_AUTH_KEY_ENC: &[u8; 16] = b"Fa0b4tA25f4R038a";
38
39pub const STREAMING_AUTH_KEY_DEC: &[u8; 16] = b"FX0b4tA25f4R038a";
41
42pub const MEMORY_READ_KEY: &[u8; 16] = b"Lh2yfB7n6X7d9a5Z";
44
45pub const HARDWARE_ID_ADDRESS: u32 = 0x40010450;
47
48pub const HARDWARE_ID_SIZE: usize = 12;
50
51pub const DEVICE_INFO_ADDRESS: u32 = 0x00000420;
53
54pub const FIRMWARE_INFO_ADDRESS: u32 = 0x00004420;
56
57pub const CALIBRATION_ADDRESS: u32 = 0x03000C00;
59
60pub const INFO_BLOCK_SIZE: usize = 64;
62
63#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct HardwareId {
72 pub bytes: [u8; HARDWARE_ID_SIZE],
73}
74
75impl HardwareId {
76 pub fn from_bytes(bytes: [u8; HARDWARE_ID_SIZE]) -> Self {
78 Self { bytes }
79 }
80
81 pub fn serial_prefix(&self) -> Option<String> {
83 let prefix = &self.bytes[0..6];
84 if prefix.iter().all(|&b| b.is_ascii_alphanumeric()) {
85 Some(String::from_utf8_lossy(prefix).to_string())
86 } else {
87 None
88 }
89 }
90
91 pub fn device_id(&self) -> u16 {
93 u16::from_le_bytes([self.bytes[8], self.bytes[9]])
94 }
95
96 pub fn as_bytes(&self) -> &[u8; HARDWARE_ID_SIZE] {
98 &self.bytes
99 }
100}
101
102impl std::fmt::Display for HardwareId {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(f, "{}", hex::encode(self.bytes))
105 }
106}
107
108#[derive(Debug, Clone, Default)]
110pub struct DeviceInfo {
111 pub model: String,
113 pub hw_version: String,
115 pub mfg_date: String,
117 pub fw_version: String,
119 pub fw_date: String,
121 pub serial_id: String,
123 pub uuid: String,
125}
126
127impl DeviceInfo {
128 pub fn parse_device_info(&mut self, data: &[u8]) {
136 if data.len() >= 64 {
137 self.model = extract_string(data, 0x10, 0x1C);
138 self.hw_version = extract_string(data, 0x1C, 0x28);
139 self.mfg_date = extract_string(data, 0x28, 0x40);
140 }
141 }
142
143 pub fn parse_firmware_info(&mut self, data: &[u8]) {
151 if data.len() >= 64 {
152 let magic = u32::from_le_bytes(data[0..4].try_into().unwrap_or([0xFF; 4]));
154 if magic != 0xFFFFFFFF {
155 self.fw_version = extract_string(data, 0x1C, 0x28);
156 self.fw_date = extract_string(data, 0x28, 0x34);
157 }
158 }
159 }
160
161 pub fn parse_calibration(&mut self, data: &[u8]) {
167 if data.len() >= 64 {
168 self.serial_id = extract_string(data, 0x00, 0x07).trim().to_string();
169 self.uuid = extract_string(data, 0x07, 0x27);
170 }
171 }
172}
173
174fn extract_string(data: &[u8], start: usize, end: usize) -> String {
176 if start >= data.len() || end > data.len() || start >= end {
177 return String::new();
178 }
179 let slice = &data[start..end];
180 let len = slice.iter().position(|&b| b == 0).unwrap_or(slice.len());
182 String::from_utf8_lossy(&slice[..len]).to_string()
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct StreamingAuthResult {
188 pub success: bool,
190 pub attribute: u16,
192 pub auth_level: u8,
194 pub decrypted_payload: [u8; 32],
196}
197
198impl StreamingAuthResult {
199 pub fn adcqueue_enabled(&self) -> bool {
201 (self.attribute & STREAMING_AUTH_GRANTED_FLAG) != 0
202 }
203}
204
205#[derive(Debug, Clone)]
210pub struct InitResult {
211 pub device_info: DeviceInfo,
213 pub hardware_id: HardwareId,
215 pub auth: StreamingAuthResult,
217}
218
219impl InitResult {
220 pub fn is_authenticated(&self) -> bool {
222 self.auth.success && self.auth.adcqueue_enabled()
223 }
224}
225
226pub fn build_memory_read_payload(address: u32, size: u32) -> [u8; 32] {
235 let mut plaintext = [0xFFu8; 32];
237
238 plaintext[0..4].copy_from_slice(&address.to_le_bytes());
240
241 plaintext[4..8].copy_from_slice(&size.to_le_bytes());
243
244 plaintext[8..12].copy_from_slice(&0xFFFFFFFFu32.to_le_bytes());
246
247 let crc = crc32fast::hash(&plaintext[0..12]);
249 plaintext[12..16].copy_from_slice(&crc.to_le_bytes());
250
251 aes_ecb_encrypt(&plaintext, MEMORY_READ_KEY)
255}
256
257pub fn build_memory_read_packet(address: u32, size: u32, tid: u8) -> Vec<u8> {
267 let ciphertext = build_memory_read_payload(address, size);
268
269 let mut packet = Vec::with_capacity(36);
271 packet.push(0x44); packet.push(tid); packet.push(0x01); packet.push(0x01); packet.extend_from_slice(&ciphertext);
276
277 packet
278}
279
280pub fn build_streaming_auth_payload(hardware_id: &HardwareId) -> [u8; 32] {
288 let mut plaintext = [0u8; 32];
290
291 let timestamp = std::time::SystemTime::now()
293 .duration_since(std::time::UNIX_EPOCH)
294 .map(|d| d.as_millis() as u64)
295 .unwrap_or(0);
296 plaintext[0..8].copy_from_slice(×tamp.to_le_bytes());
297
298 plaintext[8..20].copy_from_slice(hardware_id.as_bytes());
300
301 let random_bytes: [u8; 12] = rand::random();
303 plaintext[20..32].copy_from_slice(&random_bytes);
304
305 aes_ecb_encrypt(&plaintext, STREAMING_AUTH_KEY_ENC)
307}
308
309pub fn encrypt_streaming_auth_response_payload(plaintext: &[u8; 32]) -> [u8; 32] {
317 aes_ecb_encrypt(plaintext, STREAMING_AUTH_KEY_DEC)
318}
319
320pub fn build_streaming_auth_packet(hardware_id: &HardwareId, tid: u8) -> Vec<u8> {
329 let ciphertext = build_streaming_auth_payload(hardware_id);
330
331 let mut packet = Vec::with_capacity(36);
333 packet.push(0x4C); packet.push(tid); packet.push(0x00); packet.push(0x02); packet.extend_from_slice(&ciphertext);
338
339 packet
340}
341
342pub fn parse_streaming_auth_response(response: &[u8]) -> Option<StreamingAuthResult> {
350 if response.len() < STREAMING_AUTH_HEADER_SIZE + STREAMING_AUTH_PAYLOAD_SIZE {
351 return None;
352 }
353
354 let (header_bytes, payload) = response.split_at(STREAMING_AUTH_HEADER_SIZE);
355 let header = StreamingAuthHeader::from_bytes(header_bytes.try_into().ok()?);
356 parse_streaming_auth_response_parts(header, payload)
357}
358
359pub(crate) fn parse_streaming_auth_response_parts(
360 header: StreamingAuthHeader,
361 payload: &[u8],
362) -> Option<StreamingAuthResult> {
363 if header.packet_type() != u8::from(PacketType::StreamingAuth)
364 || header.reserved_flag()
365 || header.id() != STREAMING_AUTH_RESPONSE_ID
366 || header.attribute() & STREAMING_AUTH_RESULT_FLAG == 0
367 {
368 return None;
369 }
370
371 parse_streaming_auth_response_payload(payload, header.attribute())
372}
373
374pub fn parse_streaming_auth_response_payload(payload: &[u8], attribute: u16) -> Option<StreamingAuthResult> {
376 let encrypted: [u8; STREAMING_AUTH_PAYLOAD_SIZE] = payload.get(..STREAMING_AUTH_PAYLOAD_SIZE)?.try_into().ok()?;
377 let decrypted = aes_ecb_decrypt(&encrypted, STREAMING_AUTH_KEY_DEC);
378 let success = (attribute & STREAMING_AUTH_GRANTED_FLAG) != 0;
379
380 Some(StreamingAuthResult {
381 success,
382 attribute,
383 auth_level: u8::from(success),
384 decrypted_payload: decrypted,
385 })
386}
387
388fn aes_ecb_encrypt(plaintext: &[u8; 32], key: &[u8; 16]) -> [u8; 32] {
390 let cipher = Aes128::new(key.into());
391 let mut output = *plaintext;
392
393 for block in output.chunks_exact_mut(AES_BLOCK_SIZE) {
394 encrypt_aes_block(&cipher, block);
395 }
396
397 output
398}
399
400pub fn decrypt_memory_read_response(ciphertext: &[u8]) -> Option<Vec<u8>> {
405 if ciphertext.len() < AES_BLOCK_SIZE {
406 return None;
407 }
408
409 let cipher = Aes128::new(MEMORY_READ_KEY.into());
411 let mut output = ciphertext.to_vec();
412
413 for block in output.chunks_mut(AES_BLOCK_SIZE) {
414 if block.len() == AES_BLOCK_SIZE {
415 decrypt_aes_block(&cipher, block);
416 }
417 }
418
419 Some(output)
420}
421
422fn aes_ecb_decrypt(ciphertext: &[u8; 32], key: &[u8; 16]) -> [u8; 32] {
424 let cipher = Aes128::new(key.into());
425 let mut output = *ciphertext;
426
427 for block in output.chunks_exact_mut(AES_BLOCK_SIZE) {
428 decrypt_aes_block(&cipher, block);
429 }
430
431 output
432}
433
434fn encrypt_aes_block(cipher: &Aes128, bytes: &mut [u8]) {
435 let block: &mut [u8; AES_BLOCK_SIZE] = bytes.try_into().expect("AES chunk has the block size");
436 cipher.encrypt_block(block.into());
437}
438
439fn decrypt_aes_block(cipher: &Aes128, bytes: &mut [u8]) {
440 let block: &mut [u8; AES_BLOCK_SIZE] = bytes.try_into().expect("AES chunk has the block size");
441 cipher.decrypt_block(block.into());
442}
443
444pub fn aes_ecb_decrypt_block(ciphertext: &[u8; 16], key: &[u8; 16]) -> [u8; 16] {
448 let cipher = Aes128::new(key.into());
449 let mut output = *ciphertext;
450 decrypt_aes_block(&cipher, &mut output);
451 output
452}
453
454pub fn aes_ecb_decrypt_blocks(ciphertext: &[u8], key: &[u8; 16]) -> Result<Vec<u8>, crate::error::KMError> {
463 if !ciphertext.len().is_multiple_of(AES_BLOCK_SIZE) {
464 return Err(crate::error::KMError::InvalidPacket(format!(
465 "AES ciphertext must be a multiple of {AES_BLOCK_SIZE} bytes, got {}",
466 ciphertext.len()
467 )));
468 }
469
470 let cipher = Aes128::new(key.into());
471 let mut output = ciphertext.to_vec();
472
473 for block in output.chunks_exact_mut(AES_BLOCK_SIZE) {
474 decrypt_aes_block(&cipher, block);
475 }
476
477 Ok(output)
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483
484 const HARDCODED_MEMORY_READ: &str = "4402010133f8860c0054288cdc7e52729826872dd18b539a39c407d5c063d91102e36a9e";
487 const HARDCODED_STREAMING_AUTH: &str = "4c0600025538815b69a452c83e54ef1d70f3bc9ae6aac1b12a6ac07c20fde58c7bf517ca";
489
490 #[test]
491 fn test_hardware_id_parsing() {
492 let bytes: [u8; 12] = [
494 0x30, 0x37, 0x31, 0x4b, 0x42, 0x50, 0x0d, 0xff, 0x11, 0x0a, 0xff, 0xff, ];
499
500 let hw_id = HardwareId::from_bytes(bytes);
501 assert_eq!(hw_id.serial_prefix(), Some("071KBP".to_string()));
502 assert_eq!(hw_id.device_id(), 2577);
503 }
504
505 #[test]
506 fn test_aes_roundtrip() {
507 let plaintext = [0x42u8; 32];
508 let encrypted = aes_ecb_encrypt(&plaintext, STREAMING_AUTH_KEY_ENC);
509 assert_ne!(encrypted, plaintext);
511 assert_eq!(encrypted.len(), 32);
512 }
513
514 #[test]
515 fn test_memory_read_packet_structure() {
516 let packet = build_memory_read_packet(HARDWARE_ID_ADDRESS, HARDWARE_ID_SIZE as u32, 0x02);
517 assert_eq!(packet.len(), 36);
518 assert_eq!(packet[0], 0x44); assert_eq!(packet[1], 0x02); assert_eq!(packet[2], 0x01); assert_eq!(packet[3], 0x01); }
523
524 #[test]
525 fn test_streaming_auth_packet_structure() {
526 let hw_id = HardwareId::from_bytes([0x30, 0x37, 0x31, 0x4b, 0x42, 0x50, 0x0d, 0xff, 0x11, 0x0a, 0xff, 0xff]);
527 let packet = build_streaming_auth_packet(&hw_id, 0x06);
528 assert_eq!(packet.len(), 36);
529 assert_eq!(packet[0], 0x4C); assert_eq!(packet[1], 0x06); assert_eq!(packet[2], 0x00); assert_eq!(packet[3], 0x02); }
534
535 #[test]
536 fn test_streaming_auth_response_uses_header_result() {
537 use crate::{Packet, RawPacket};
538 use bytes::Bytes;
539
540 let plaintext = [0x5a; STREAMING_AUTH_PAYLOAD_SIZE];
541 let encrypted = aes_ecb_encrypt(&plaintext, STREAMING_AUTH_KEY_DEC);
542
543 let response = |attribute| {
544 let header = StreamingAuthHeader::new()
545 .with_packet_type(PacketType::StreamingAuth.into())
546 .with_reserved_flag(false)
547 .with_id(0)
548 .with_attribute(attribute);
549 let mut bytes = Vec::from(header.into_bytes());
550 bytes.extend_from_slice(&encrypted);
551 bytes
552 };
553
554 let success = parse_streaming_auth_response(&response(0x0203)).unwrap();
555 assert!(success.success);
556 assert_eq!(success.auth_level, 1);
557 assert_eq!(success.attribute, 0x0203);
558 assert_eq!(success.decrypted_payload, plaintext);
559
560 let serialized = Bytes::from(
561 Packet::StreamingAuthResponse(success.clone())
562 .to_raw_packet(99)
563 .unwrap(),
564 );
565 let reparsed = RawPacket::try_from(serialized).unwrap();
566 assert_eq!(
567 Packet::try_from(reparsed).unwrap(),
568 Packet::StreamingAuthResponse(success)
569 );
570
571 let raw_failure = RawPacket::try_from(Bytes::from(response(0x0201))).unwrap();
572 let Packet::StreamingAuthResponse(failure) = Packet::try_from(raw_failure).unwrap() else {
573 panic!("expected StreamingAuthResponse");
574 };
575 assert!(!failure.success);
576 assert_eq!(failure.auth_level, 0);
577 assert_eq!(failure.attribute, 0x0201);
578 assert_eq!(failure.decrypted_payload, plaintext);
579 }
580
581 #[test]
582 fn test_decrypt_hardcoded_memory_read() {
583 let packet = hex::decode(HARDCODED_MEMORY_READ).unwrap();
586
587 assert_eq!(packet[0], 0x44, "MemoryRead type");
589 assert_eq!(packet[1], 0x02, "TID=2");
590 assert_eq!(packet[2], 0x01, "Attribute low");
591 assert_eq!(packet[3], 0x01, "Attribute high");
592
593 let ciphertext: [u8; 32] = packet[4..36].try_into().unwrap();
595 let plaintext = aes_ecb_decrypt(&ciphertext, MEMORY_READ_KEY);
596
597 let address = u32::from_le_bytes([plaintext[0], plaintext[1], plaintext[2], plaintext[3]]);
599 let size = u32::from_le_bytes([plaintext[4], plaintext[5], plaintext[6], plaintext[7]]);
601
602 println!("Decrypted MemoryRead: address=0x{:08X}, size={}", address, size);
603 println!("Plaintext: {:02x?}", plaintext);
604
605 assert_eq!(address, 0x420, "Hardcoded reads from 0x420");
607 assert_eq!(size, 64, "Hardcoded reads 64 bytes");
608 }
609
610 #[test]
611 fn test_memory_read_for_hardware_id() {
612 let generated = build_memory_read_packet(HARDWARE_ID_ADDRESS, HARDWARE_ID_SIZE as u32, 0x02);
614
615 assert_eq!(generated[0], 0x44, "MemoryRead type");
617 assert_eq!(generated[1], 0x02, "TID=2");
618 assert_eq!(generated[2], 0x01, "Attribute low");
619 assert_eq!(generated[3], 0x01, "Attribute high");
620
621 let ciphertext: [u8; 32] = generated[4..36].try_into().unwrap();
623 let plaintext = aes_ecb_decrypt(&ciphertext, MEMORY_READ_KEY);
624
625 let address = u32::from_le_bytes([plaintext[0], plaintext[1], plaintext[2], plaintext[3]]);
626 let size = u32::from_le_bytes([plaintext[4], plaintext[5], plaintext[6], plaintext[7]]);
627
628 assert_eq!(address, HARDWARE_ID_ADDRESS, "Should read from 0x40010450");
629 assert_eq!(size, HARDWARE_ID_SIZE as u32, "Should read 12 bytes");
630
631 let expected_crc = crc32fast::hash(&plaintext[0..12]);
633 let actual_crc = u32::from_le_bytes([plaintext[12], plaintext[13], plaintext[14], plaintext[15]]);
634 assert_eq!(actual_crc, expected_crc, "CRC32 should be valid");
635 }
636
637 #[test]
638 fn test_decrypt_hardcoded_streaming_auth() {
639 let packet = hex::decode(HARDCODED_STREAMING_AUTH).unwrap();
641
642 assert_eq!(packet[0], 0x4C, "StreamingAuth type");
644 assert_eq!(packet[1], 0x06, "TID=6");
645 assert_eq!(packet[2], 0x00, "Attribute low");
646 assert_eq!(packet[3], 0x02, "Attribute high");
647
648 let ciphertext: [u8; 32] = packet[4..36].try_into().unwrap();
650 let plaintext = aes_ecb_decrypt(&ciphertext, STREAMING_AUTH_KEY_ENC);
651
652 let timestamp = u64::from_le_bytes(plaintext[0..8].try_into().unwrap());
654 let hardware_id_bytes: [u8; 12] = plaintext[8..20].try_into().unwrap();
655 let hardware_id = HardwareId::from_bytes(hardware_id_bytes);
656
657 println!("Decrypted StreamingAuth:");
658 println!(" Timestamp: {} ms", timestamp);
659 println!(" HardwareID: {}", hardware_id);
660 println!(" Serial prefix: {:?}", hardware_id.serial_prefix());
661 println!(" Device ID: {}", hardware_id.device_id());
662 println!(" Plaintext: {:02x?}", plaintext);
663
664 assert_eq!(hardware_id.serial_prefix(), Some("071KBP".to_string()));
666 assert_eq!(hardware_id.device_id(), 2577);
667 assert!(timestamp > 0, "Timestamp should be non-zero");
668 }
669
670 #[test]
671 fn test_streaming_auth_with_known_hardware_id() {
672 let hw_id = HardwareId::from_bytes([
675 0x30, 0x37, 0x31, 0x4b, 0x42, 0x50, 0x0d, 0xff, 0x11, 0x0a, 0xff, 0xff, ]);
680
681 let packet = build_streaming_auth_packet(&hw_id, 0x06);
683
684 let hardcoded = hex::decode(HARDCODED_STREAMING_AUTH).unwrap();
686 assert_eq!(&packet[0..4], &hardcoded[0..4], "Headers must match");
687
688 let ciphertext: [u8; 32] = packet[4..36].try_into().unwrap();
690 let plaintext = aes_ecb_decrypt(&ciphertext, STREAMING_AUTH_KEY_ENC);
691
692 let extracted_hw: [u8; 12] = plaintext[8..20].try_into().unwrap();
694 assert_eq!(extracted_hw, hw_id.bytes, "HardwareID must be embedded correctly");
695
696 let timestamp = u64::from_le_bytes(plaintext[0..8].try_into().unwrap());
698 let now = std::time::SystemTime::now()
699 .duration_since(std::time::UNIX_EPOCH)
700 .unwrap()
701 .as_millis() as u64;
702 assert!(timestamp > now - 3600000, "Timestamp should be recent");
703 assert!(timestamp <= now + 1000, "Timestamp should not be in future");
704 }
705}