general cleanup, json output, no cursed code

This commit is contained in:
eta 2022-12-30 20:27:10 +00:00
parent 90e1c41858
commit d4918a2b00
7 changed files with 75 additions and 408 deletions

277
Cargo.lock generated
View File

@ -14,12 +14,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64ct"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
[[package]]
name = "bitvec"
version = "1.0.1"
@ -32,82 +26,12 @@ dependencies = [
"wyz",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "const-oid"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "der"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"const-oid",
"crypto-common",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hex"
version = "0.4.3"
@ -124,41 +48,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "lazy_static"
version = "1.4.0"
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"spin",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libm"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "num-bigint-dig"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"autocfg",
"num-integer",
"num-iter",
"num-traits",
"rand",
"smallvec",
"zeroize",
]
[[package]]
@ -171,17 +68,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -189,46 +75,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "pem-rfc7468"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
dependencies = [
"base64ct",
]
[[package]]
name = "pkcs1"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
dependencies = [
"der",
"pkcs8",
"spki",
"zeroize",
]
[[package]]
name = "pkcs8"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
dependencies = [
"der",
"spki",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.49"
@ -253,57 +101,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rsa"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c"
dependencies = [
"byteorder",
"digest",
"num-bigint-dig",
"num-integer",
"num-iter",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core",
"signature",
"smallvec",
"subtle",
"zeroize",
]
[[package]]
name = "rsp6-decoder"
version = "0.1.0"
@ -311,8 +108,7 @@ dependencies = [
"anyhow",
"bitvec",
"hex",
"rand",
"rsa",
"num-bigint",
"serde",
"serde_json",
"time",
@ -355,44 +151,6 @@ dependencies = [
"serde",
]
[[package]]
name = "signature"
version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
dependencies = [
"digest",
"rand_core",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spki"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.107"
@ -416,6 +174,7 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
@ -436,30 +195,12 @@ dependencies = [
"time-core",
]
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wyz"
version = "0.5.1"
@ -468,9 +209,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "zeroize"
version = "1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"

View File

@ -9,8 +9,7 @@ edition = "2021"
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0"
serde_json = "1.0"
rsa = "0.7"
rand = "0.8"
num-bigint = "0.4"
bitvec = "1.0"
hex = { version = "0.4", features = ["serde"] }
time = { version = "0.3", features = ["macros", "serde"] }
time = { version = "0.3", features = ["macros", "serde", "serde-human-readable"] }

View File

@ -426,16 +426,6 @@
}
],
"TT": [
{
"is_private": false,
"is_test": true,
"issuer_id": "TT",
"modulus_hex": "C8D75C530F796A893FC2FECE2AE2037A815502CFB32C2C0CF25FE0FEDDE58D5DDA86FF673C17BC1272333D4D3D14E7A71ECAF054A6DE808968CDEF40CB1A5FAF8AE248E46497611F948ACBB12996C38FB22F9427D8D932B11F78958E53EF9275EAED2153C411192CF8BC804DF7784BE07EF68ED06738C238F96ACEA5EF740C35",
"public_exponent_hex": "10001",
"public_key_x509": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI11xTD3lqiT/C/s4q4gN6gVUC\nz7MsLAzyX+D+3eWNXdqG/2c8F7wScjM9TT0U56ceyvBUpt6AiWjN70DLGl+viuJI\n5GSXYR+UisuxKZbDj7IvlCfY2TKxH3iVjlPvknXq7SFTxBEZLPi8gE33eEvgfvaO\n0Gc4wjj5as6l73QMNQIDAQAB\n-----END PUBLIC KEY-----",
"valid_from": "2009-01-01 00:00:00",
"valid_until": "2040-12-31 00:00:00"
},
{
"is_private": false,
"is_test": false,
@ -445,6 +435,16 @@
"public_key_x509": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXdAIcK60T3DHB7IGS4ITWDYLc\nMnAWhi6V7Qk61BzJCCpgFWMcjmuBSKFeyYVuKl4WUZ5S7cDD3yg2k1BV71Phc4KT\nJWRk8K1K48Aa483ZEMscvcCrNcGtjPCpN2s5Id7j0fwm/6NAnE3I+BOl4ybXjGOr\nqaWRINdAQ9uhFBBHrwIDAQAB\n-----END PUBLIC KEY-----",
"valid_from": "2010-01-01 00:00:00",
"valid_until": "2040-12-31 00:00:00"
},
{
"is_private": false,
"is_test": true,
"issuer_id": "TT",
"modulus_hex": "C8D75C530F796A893FC2FECE2AE2037A815502CFB32C2C0CF25FE0FEDDE58D5DDA86FF673C17BC1272333D4D3D14E7A71ECAF054A6DE808968CDEF40CB1A5FAF8AE248E46497611F948ACBB12996C38FB22F9427D8D932B11F78958E53EF9275EAED2153C411192CF8BC804DF7784BE07EF68ED06738C238F96ACEA5EF740C35",
"public_exponent_hex": "10001",
"public_key_x509": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI11xTD3lqiT/C/s4q4gN6gVUC\nz7MsLAzyX+D+3eWNXdqG/2c8F7wScjM9TT0U56ceyvBUpt6AiWjN70DLGl+viuJI\n5GSXYR+UisuxKZbDj7IvlCfY2TKxH3iVjlPvknXq7SFTxBEZLPi8gE33eEvgfvaO\n0Gc4wjj5as6l73QMNQIDAQAB\n-----END PUBLIC KEY-----",
"valid_from": "2009-01-01 00:00:00",
"valid_until": "2040-12-31 00:00:00"
}
],
"TV": [

View File

@ -1,52 +0,0 @@
pub fn slice_bool(a: &[u8], index: usize) -> bool {
(a[index / 8] >> (7 - (index % 8))) == 1
}
pub fn slice_int(a: &[u8], start_bit: usize, length_bits: usize) -> u32 {
let i = start_bit as u32;
let i2 = length_bits as u32;
let mut i3: u32 = (i as u32) & 7;
let mut i4: u32 = i3;
let mut i5: u32 = (i as u32) >> 3;
let mut i6: u32 = (1 << (8 - i3)) - 1;
let mut i7: u32 = 0;
let mut i8: u32 = 0;
while (i2 - i7) > 0 {
i8 |= (i6 & a[i5 as usize] as u32) << ((i4 + 24) - i7);
i7 += 8 - i4;
i6 = 255;
i4 = 0;
i5 += 1;
}
i8 >> (32 - i2)
}
pub fn slice_base64(a: &[u8], start_bit: usize, chars: usize) -> String {
let mut sb = String::new();
let mut i = start_bit as u32;
let mut i2 = chars as u32;
let mut i3: u32 = i & 7;
let mut i4: u32 = i3;
let mut i5: u32 = i >> 3;
let mut i6: u32 = (1 << (8 - i3)) - 1;
let mut i7: u32 = 0;
let mut i8: u32 = 0;
while i2 > 0 {
if i7 >= 6 {
sb.push(((i8 >> 26) + 32) as u8 as char);
i8 <<= 6;
i7 -= 6;
i2 -= 1;
} else {
i8 |= (i6 & a[i5 as usize] as u32) << ((i4 + 24) - i7);
i7 += 8 - i4;
i6 = 255;
i4 = 0;
i5 += 1
}
}
sb
}

View File

@ -1,29 +1,38 @@
//! Decoding and using the RSA public keys needed to decrypt the ticket data.
use rsa::{BigUint, RsaPublicKey};
use num_bigint::BigUint;
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
static KEYS_JSON: &'static str = include_str!("../keys.json");
fn deserialize_exponent_hex<'de, D>(deserializer: D) -> Result<u64, D::Error>
fn deserialize_hex_as_biguint<'de, D>(deserializer: D) -> Result<BigUint, D::Error>
where
D: Deserializer<'de>,
{
let buf = String::deserialize(deserializer)?;
u64::from_str_radix(&buf, 16).map_err(serde::de::Error::custom)
BigUint::parse_bytes(buf.as_bytes(), 16).ok_or(serde::de::Error::custom(format!(
"failed to parse exponent hex"
)))
}
#[derive(Deserialize, Debug)]
pub struct IssuerKey {
is_private: bool,
is_test: bool,
issuer_id: String,
#[serde(rename = "modulus_hex", deserialize_with = "hex::serde::deserialize")]
pub modulus: Vec<u8>,
pub public_exponent_hex: String,
public_key_x509: Option<String>,
pub is_private: bool,
pub is_test: bool,
pub issuer_id: String,
#[serde(
rename = "modulus_hex",
deserialize_with = "deserialize_hex_as_biguint"
)]
pub modulus: BigUint,
#[serde(
rename = "public_exponent_hex",
deserialize_with = "deserialize_hex_as_biguint"
)]
pub public_exponent: BigUint,
pub public_key_x509: Option<String>,
}
#[derive(Debug)]

View File

@ -1,16 +1,8 @@
use crate::cursed::{slice_base64, slice_bool, slice_int};
use crate::keys::IssuerKeyStore;
use crate::payload::{CapitalismDateTime, Rsp6Ticket};
use crate::payload::Rsp6Ticket;
use anyhow::anyhow;
use bitvec::field::BitField;
use bitvec::order::Msb0;
use bitvec::view::BitView;
use rsa::BigUint;
use std::fs;
use std::io::Read;
use time::PrimitiveDateTime;
use num_bigint::BigUint;
mod cursed;
mod keys;
mod payload;
@ -23,27 +15,6 @@ fn base26_decode(input: &str) -> BigUint {
BigUint::from_bytes_le(&out.to_bytes_be())
}
/*
fn base26_decode(input: &str) -> Vec<u8> {
let mut alphabets = input
.chars()
.map(|x| (u32::from(x) - u32::from('A')) as u8)
.collect::<Vec<_>>();
let length = (((alphabets.len() * 500) + 851) - 1) / 851;
let mut ret = vec![0u8; length];
for c in ret.iter_mut() {
let mut k = 0;
for j in (0..=alphabets.len()).rev() {
let a = (k * 26) + alphabets[j];
alphabets[j] = a;
k = a;
}
*c = k;
}
ret
}
*/
fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
if tkt.is_empty() || tkt[0] != 1 {
return None;
@ -63,8 +34,8 @@ fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
fn main() -> anyhow::Result<()> {
let iks = IssuerKeyStore::new();
println!("[+] Loaded {} public keys!", iks.keys.len());
println!("[+] mmm, give me a tasty ticket on stdin please");
eprintln!("[+] Loaded {} public keys!", iks.keys.len());
eprintln!("[+] mmm, give me a tasty ticket on stdin please");
let ticket_str = std::io::stdin()
.lines()
.next()
@ -80,33 +51,33 @@ fn main() -> anyhow::Result<()> {
}
let issuer_id = &ticket_str[13..15];
let ticket_reference = format!("{}{}", issuer_id, &ticket_str[2..11]);
println!("[+] RSP6 ticket, reference {}", ticket_reference);
println!("[+] Ticket issuer: {}", issuer_id);
eprintln!("[+] RSP6 ticket, reference {}", ticket_reference);
eprintln!("[+] Ticket issuer: {}", issuer_id);
let ticket = base26_decode(&ticket_str[15..]);
let keys = iks
.keys
.get(issuer_id)
.ok_or_else(|| anyhow!("unknown issuer ID {}", issuer_id))?;
for key in keys {
let modulus = BigUint::from_bytes_be(&key.modulus);
let exponent = BigUint::parse_bytes(key.public_exponent_hex.as_bytes(), 16)
.ok_or_else(|| anyhow!("failed to parse exponent in key"))?;
let message = ticket.modpow(&exponent, &modulus).to_bytes_be();
let message = ticket
.modpow(&key.public_exponent, &key.modulus)
.to_bytes_be();
if let Some(unpadded) = strip_padding(&message) {
println!("done! {:?}", unpadded);
let ticket_ref_inner = cursed::slice_base64(unpadded, 8, 9);
let extra_bit = cursed::slice_base64(unpadded, 62, 1);
eprintln!("[+] decrypt done!");
let ticket_ref_inner = Rsp6Ticket::base64(unpadded, 8, 62);
let extra_bit = Rsp6Ticket::base64(unpadded, 62, 68);
let inner_data = format!("{}{}", ticket_ref_inner, extra_bit);
let outer_data = &ticket_str[2..12];
if inner_data != outer_data {
eprintln!("mismatch: {} vs {}", inner_data, outer_data);
// return Err(anyhow!("failed to validate inner and outer data"));
eprintln!("[-] checksum mismatch: {} vs {}", inner_data, outer_data);
} else {
eprintln!("[+] checksum ok");
}
println!("[+] ticket validated!");
println!("[+] ticket: {:#?}", Rsp6Ticket::decode(unpadded));
let ticket = Rsp6Ticket::decode(unpadded)?;
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
return Ok(());
} else {
println!("failed decrypt: {:?}", message);
eprintln!("[-] failed decrypt: {:?}", message);
}
}
Err(anyhow!("no valid decryptions"))

View File

@ -1,15 +1,15 @@
//! Decoding the actual inner decrypted payload bit.
use crate::{slice_base64, slice_bool};
use anyhow::anyhow;
use bitvec::field::BitField;
use bitvec::order::Msb0;
use bitvec::slice::BitSlice;
use bitvec::view::BitView;
use serde::Serialize;
use time::macros::datetime;
use time::{Date, Duration, PrimitiveDateTime, Time};
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Serialize)]
pub enum CouponType {
Single = 0,
Season = 1,
@ -17,7 +17,7 @@ pub enum CouponType {
ReturnInbound = 3,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize)]
pub struct TicketPurchaseDetails {
pub purchase_time: PrimitiveDateTime,
pub price_pence: u32,
@ -25,7 +25,7 @@ pub struct TicketPurchaseDetails {
pub days_of_validity: u16,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize)]
pub struct Reservation {
pub retail_service_id: String,
pub coach: char,
@ -54,7 +54,7 @@ impl Reservation {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize)]
pub struct Rsp6Ticket {
pub manually_inspect: bool,
pub ticket_reference: String,
@ -71,7 +71,8 @@ pub struct Rsp6Ticket {
pub discount_code: u16,
pub route_code: u32,
pub start_date: Date,
pub depart_time: Option<Time>,
pub depart_time_flag: u8,
pub depart_time: Time,
pub passenger_id: Option<String>,
pub passenger_name: Option<String>,
pub passenger_gender: Option<u8>,
@ -84,10 +85,13 @@ pub struct Rsp6Ticket {
}
impl Rsp6Ticket {
fn base64(tkt: &[u8], from: usize, to: usize) -> String {
pub fn base64(tkt: &[u8], from: usize, to: usize) -> String {
let chars = (to - from) / 6;
assert_eq!(chars * 6, to - from);
slice_base64(tkt, from, chars)
tkt.view_bits::<Msb0>()[from..to]
.chunks(6)
.map(|x| char::from(x.load_be::<u8>() + 32))
.collect()
}
fn decode_limited_duration(dur: u8) -> Option<Duration> {
@ -131,19 +135,19 @@ impl Rsp6Ticket {
pub fn decode(tkt: &[u8]) -> anyhow::Result<Self> {
let bit_tkt = tkt.view_bits::<Msb0>();
let manually_inspect = slice_bool(tkt, 0);
let manually_inspect = bit_tkt[0];
let ticket_reference = Self::base64(tkt, 8, 62);
let checksum = Self::base64(tkt, 62, 68).chars().next().unwrap();
let version: u8 = bit_tkt[68..72].load_be();
let standard_class = slice_bool(tkt, 72);
let standard_class = bit_tkt[72];
let lennon_ticket_type = Self::base64(tkt, 73, 91);
let fare = Self::base64(tkt, 91, 109);
let origin_nlc = Self::base64(tkt, 109, 133);
let destination_nlc = Self::base64(tkt, 133, 157);
let retailer_id = Self::base64(tkt, 157, 181);
let is_child = slice_bool(tkt, 181);
let is_child = bit_tkt[181];
let coupon_type = match bit_tkt[182..184].load_be::<u8>() {
0 => CouponType::Single,
1 => CouponType::Season,
@ -158,8 +162,8 @@ impl Rsp6Ticket {
let start_time_secs: u32 = bit_tkt[225..236].load_be();
let start_time: PrimitiveDateTime =
CapitalismDateTime::new(start_time_days, start_time_secs).into();
let depart_time_flag: u8 = bit_tkt[236..238].load_be(); // FIXME
let depart_time = Some(start_time.time());
let depart_time_flag: u8 = bit_tkt[236..238].load_be();
let depart_time = start_time.time();
let start_date = start_time.date();
let passenger_id = Self::decode_passenger_id(bit_tkt[238..255].load_be());
@ -170,10 +174,10 @@ impl Rsp6Ticket {
let restriction_code = Self::base64(tkt, 329, 347);
let restriction_code = (!restriction_code.trim().is_empty()).then_some(restriction_code);
let bidirectional = slice_bool(tkt, 372);
let bidirectional = bit_tkt[372];
let limited_duration = Self::decode_limited_duration(bit_tkt[379..383].load_be());
let is_full_ticket = slice_bool(tkt, 384);
let is_full_ticket = bit_tkt[384];
let purchase_details = if is_full_ticket {
let purchase_time_days: u32 = bit_tkt[390..404].load_be();
@ -225,6 +229,7 @@ impl Rsp6Ticket {
route_code,
start_date,
depart_time,
depart_time_flag,
passenger_id,
passenger_name,
passenger_gender,