diff --git a/src/main.rs b/src/main.rs index faaab4c..5cd5bb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,15 @@ use std::{fs, thread}; use std::error::Error; -use std::fmt::format; -use std::io::{BufRead, Read}; -use std::ops::{Add, Deref}; +use std::ops::Add; use std::result::Result; use std::time::Duration; use crc::{Crc, CRC_32_ISO_HDLC}; -use rc4::{KeyInit, Rc4, StreamCipher}; use crate::GGID::{English, French, German, Italian, Japanese, Korean, Spanish}; +use crate::pcd::{Partitioned, PCD}; + +mod pcd; type MacAddress = [u8; 6]; @@ -140,128 +140,3 @@ impl TryFrom<&str> for GGID { } } } - -struct PCD { - state: State, -} - -struct Raw { - data: [u8; PCD_LENGTH], -} - -struct Encrypted { - data: [u8; PCD_EXTENDED_LENGTH], -} - -struct Partitioned { - pgt: [u8; PCD_PGT_LENGTH], - header: [u8; PCD_HEADER_LENGTH], - card_data: [u8; PCD_CARD_DATA_LENGTH], -} - -struct Extended { - header: [u8; PCD_HEADER_LENGTH], - pgt: [u8; PCD_PGT_LENGTH], - header_duplicate: [u8; PCD_HEADER_LENGTH], - card_data: [u8; PCD_CARD_DATA_LENGTH], -} - -const PCD_LENGTH: usize = PCD_PGT_LENGTH + PCD_HEADER_LENGTH + PCD_CARD_DATA_LENGTH; -// = (856)10 -const PCD_EXTENDED_LENGTH: usize = PCD_LENGTH + PCD_HEADER_LENGTH; -const PCD_PGT_LENGTH: usize = 0x104; -const PCD_HEADER_LENGTH: usize = 0x50; -const PCD_CARD_DATA_LENGTH: usize = 0x204; - -impl<'a> TryFrom<&'a [u8]> for PCD { - type Error = String; - - fn try_from(value: &'a [u8]) -> Result { - let sized_value: [u8; PCD_LENGTH] = <[u8; PCD_LENGTH]>::try_from(value).map_err(|_| format!("PCD size needs to be {}, but was: {}", PCD_LENGTH, value.len()))?; - Ok(PCD { state: Raw { data: sized_value } }) - } -} - -impl From> for PCD { - fn from(value: PCD) -> Self { - let sized_value = value.state.data; - PCD { - state: Partitioned { - pgt: <[u8; PCD_PGT_LENGTH]>::try_from(&sized_value[..PCD_PGT_LENGTH]).unwrap(), - header: <[u8; PCD_HEADER_LENGTH]>::try_from(&sized_value[PCD_PGT_LENGTH..PCD_HEADER_LENGTH + PCD_PGT_LENGTH]).unwrap(), - card_data: <[u8; PCD_CARD_DATA_LENGTH]>::try_from(&sized_value[PCD_HEADER_LENGTH + PCD_PGT_LENGTH..PCD_CARD_DATA_LENGTH + PCD_HEADER_LENGTH + PCD_PGT_LENGTH]).unwrap(), - } - } - } -} - -impl From> for PCD { - fn from(value: PCD) -> PCD { - let part = value.state; - PCD { - state: Extended { - header: part.header, - pgt: part.pgt, - header_duplicate: part.header, - card_data: part.card_data, - } - } - } -} - -impl PCD { - fn checksum(&self) -> Result { - let state = &self.state; - let mut checksum: u16 = 0; - let binding = [state.header.as_slice(), &state.pgt, &state.header_duplicate, &state.card_data].concat(); - let data = binding.as_slice(); - if data.len() % 2 != 0 { - return Err(format!("The data length {} is not dividable by 2", &data.len())) - } - for chunk in data.chunks_exact(2) { - let c0 = chunk[0]; - let c1 = chunk[1]; - checksum += u16::from_le_bytes([c0, c1]); - checksum = checksum.rotate_left(1); - } - Ok(checksum) - } - - fn encrypt(self, address: &MacAddress) -> Result, String> { - let checksum = self.checksum()?; - let state = self.state; - let data: &mut [u8] = &mut [state.header.as_slice(), &state.pgt, &state.header_duplicate, &state.card_data].concat(); - let key = key(address, checksum); - let mut rc4 = Rc4::new(&key.into()); - rc4.apply_keystream(data.as_mut()); - let imm_data: &[u8] = data; - let sized_data: [u8; PCD_EXTENDED_LENGTH] = <[u8; PCD_EXTENDED_LENGTH]>::try_from(imm_data).map_err(|_| format!("Encrypted data length is {} instead of {}", data.len(), PCD_LENGTH))?; - Ok(PCD::new(sized_data)) - } -} - -impl PCD { - fn new(data: [u8; PCD_EXTENDED_LENGTH]) -> Self { - PCD { - state: Encrypted { - data - } - } - } -} - -fn key(address: &MacAddress, checksum: u16) -> [u8; 8] { - let checksum_parts = checksum.to_le_bytes(); - let mut key = [address[0], address[1], checksum_parts[0], checksum_parts[1], address[4], address[5], address[2], address[3]]; - let mut hw_low = 0xa2u8; - let mut hw_high = 0x3fu8; - for i in 0..4 { - let i_low = 2 * i; - let i_high = i_low + 1; - key[i_low] ^= hw_low; - key[i_high] ^= hw_high; - hw_low = key[i_low]; - hw_high = key[i_high]; - } - key -} \ No newline at end of file diff --git a/src/pcd.rs b/src/pcd.rs new file mode 100644 index 0000000..455f81a --- /dev/null +++ b/src/pcd.rs @@ -0,0 +1,134 @@ +use rc4::{KeyInit, Rc4, StreamCipher}; + +use crate::MacAddress; + +const PCD_LENGTH: usize = PCD_PGT_LENGTH + PCD_HEADER_LENGTH + PCD_CARD_DATA_LENGTH; +// = (856)10 +const PCD_EXTENDED_LENGTH: usize = PCD_LENGTH + PCD_HEADER_LENGTH; +const PCD_PGT_LENGTH: usize = 0x104; +const PCD_HEADER_LENGTH: usize = 0x50; +const PCD_CARD_DATA_LENGTH: usize = 0x204; +const PCD_FRAGMENTS: usize = 0x0a; +const PCD_FRAGMENT_LENGTH: usize = PCD_EXTENDED_LENGTH / PCD_FRAGMENTS; + +pub struct PCD { + state: State, +} + +pub struct Raw { + data: [u8; PCD_LENGTH], +} + +pub struct Encrypted { + data: [u8; PCD_EXTENDED_LENGTH], +} + +pub struct Partitioned { + pgt: [u8; PCD_PGT_LENGTH], + header: [u8; PCD_HEADER_LENGTH], + card_data: [u8; PCD_CARD_DATA_LENGTH], +} + +pub struct Extended { + header: [u8; PCD_HEADER_LENGTH], + pgt: [u8; PCD_PGT_LENGTH], + header_duplicate: [u8; PCD_HEADER_LENGTH], + card_data: [u8; PCD_CARD_DATA_LENGTH], +} + +impl<'a> TryFrom<&'a [u8]> for PCD { + type Error = String; + + fn try_from(value: &'a [u8]) -> Result { + let sized_value: [u8; PCD_LENGTH] = <[u8; PCD_LENGTH]>::try_from(value).map_err(|_| format!("PCD size needs to be {}, but was: {}", PCD_LENGTH, value.len()))?; + Ok(PCD { state: Raw { data: sized_value } }) + } +} + +impl From> for PCD { + fn from(value: PCD) -> Self { + let sized_value = value.state.data; + PCD { + state: Partitioned { + pgt: <[u8; PCD_PGT_LENGTH]>::try_from(&sized_value[..PCD_PGT_LENGTH]).unwrap(), + header: <[u8; PCD_HEADER_LENGTH]>::try_from(&sized_value[PCD_PGT_LENGTH..PCD_HEADER_LENGTH + PCD_PGT_LENGTH]).unwrap(), + card_data: <[u8; PCD_CARD_DATA_LENGTH]>::try_from(&sized_value[PCD_HEADER_LENGTH + PCD_PGT_LENGTH..PCD_CARD_DATA_LENGTH + PCD_HEADER_LENGTH + PCD_PGT_LENGTH]).unwrap(), + } + } + } +} + +impl From> for PCD { + fn from(value: PCD) -> PCD { + let part = value.state; + PCD { + state: Extended { + header: part.header, + pgt: part.pgt, + header_duplicate: part.header, + card_data: part.card_data, + } + } + } +} + +impl PCD { + pub fn checksum(&self) -> Result { + let state = &self.state; + let mut checksum: u16 = 0; + let binding = [state.header.as_slice(), &state.pgt, &state.header_duplicate, &state.card_data].concat(); + let data = binding.as_slice(); + if data.len() % 2 != 0 { + return Err(format!("The data length {} is not dividable by 2", &data.len())); + } + for chunk in data.chunks_exact(2) { + let c0 = chunk[0]; + let c1 = chunk[1]; + checksum += u16::from_le_bytes([c0, c1]); + checksum = checksum.rotate_left(1); + } + Ok(checksum) + } + + pub fn encrypt(self, address: &MacAddress) -> Result, String> { + let checksum = self.checksum()?; + let state = self.state; + let data: &mut [u8] = &mut [state.header.as_slice(), &state.pgt, &state.header_duplicate, &state.card_data].concat(); + let key = key(address, checksum); + let mut rc4 = Rc4::new(&key.into()); + rc4.apply_keystream(data.as_mut()); + let imm_data: &[u8] = data; + let sized_data: [u8; PCD_EXTENDED_LENGTH] = <[u8; PCD_EXTENDED_LENGTH]>::try_from(imm_data).map_err(|_| format!("Encrypted data length is {} instead of {}", data.len(), PCD_LENGTH))?; + Ok(PCD::new(sized_data)) + } +} + +impl PCD { + fn new(data: [u8; PCD_EXTENDED_LENGTH]) -> Self { + PCD { + state: Encrypted { + data + } + } + } + + pub fn fragments(&self) -> Vec<[u8; PCD_FRAGMENT_LENGTH]> { + self.state.data.chunks_exact(PCD_EXTENDED_LENGTH / PCD_FRAGMENTS).map(|f| <[u8; PCD_FRAGMENT_LENGTH]>::try_from(f).unwrap()).collect() + } +} + +fn key(address: &MacAddress, checksum: u16) -> [u8; 8] { + let checksum_parts = checksum.to_le_bytes(); + let mut key = [address[0], address[1], checksum_parts[0], checksum_parts[1], address[4], address[5], address[2], address[3]]; + let mut hw_low = 0xa2u8; + let mut hw_high = 0x3fu8; + for i in 0..4 { + let i_low = 2 * i; + let i_high = i_low + 1; + key[i_low] ^= hw_low; + key[i_high] ^= hw_high; + hw_low = key[i_low]; + hw_high = key[i_high]; + } + key +} \ No newline at end of file