diff --git a/Cargo.lock b/Cargo.lock index 4720d50..5413d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2e4fc39..06354c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } \ No newline at end of file +time = { version = "0.3", features = ["macros", "serde", "serde-human-readable"] } \ No newline at end of file diff --git a/keys.json b/keys.json index dfeb381..c254f92 100644 --- a/keys.json +++ b/keys.json @@ -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": [ diff --git a/src/cursed.rs b/src/cursed.rs deleted file mode 100644 index 129b5ad..0000000 --- a/src/cursed.rs +++ /dev/null @@ -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 -} diff --git a/src/keys.rs b/src/keys.rs index 415e639..67a9d3d 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -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 +fn deserialize_hex_as_biguint<'de, D>(deserializer: D) -> Result 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, - pub public_exponent_hex: String, - public_key_x509: Option, + 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, } #[derive(Debug)] diff --git a/src/main.rs b/src/main.rs index 42f6783..9e194f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 { - let mut alphabets = input - .chars() - .map(|x| (u32::from(x) - u32::from('A')) as u8) - .collect::>(); - 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")) diff --git a/src/payload.rs b/src/payload.rs index 4bf8b2d..c352942 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -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