Split into modules

This commit is contained in:
Richard Stöckl
2023-08-21 15:57:51 +02:00
parent 063e04290a
commit 274bf0ed47
2 changed files with 138 additions and 129 deletions

View File

@@ -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: 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<Raw> {
type Error = String;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
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<PCD<Raw>> for PCD<Partitioned> {
fn from(value: PCD<Raw>) -> 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<PCD<Partitioned>> for PCD<Extended> {
fn from(value: PCD<Partitioned>) -> PCD<Extended> {
let part = value.state;
PCD {
state: Extended {
header: part.header,
pgt: part.pgt,
header_duplicate: part.header,
card_data: part.card_data,
}
}
}
}
impl PCD<Extended> {
fn checksum(&self) -> Result<u16, String> {
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<PCD<Encrypted>, 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<Encrypted> {
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
}

134
src/pcd.rs Normal file
View File

@@ -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: 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<Raw> {
type Error = String;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
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<PCD<Raw>> for PCD<Partitioned> {
fn from(value: PCD<Raw>) -> 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<PCD<Partitioned>> for PCD<Extended> {
fn from(value: PCD<Partitioned>) -> PCD<Extended> {
let part = value.state;
PCD {
state: Extended {
header: part.header,
pgt: part.pgt,
header_duplicate: part.header,
card_data: part.card_data,
}
}
}
}
impl PCD<Extended> {
pub fn checksum(&self) -> Result<u16, String> {
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<PCD<Encrypted>, 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<Encrypted> {
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
}