mirror of
https://git.eta.st/eta/rsp6-decoder.git
synced 2025-07-17 11:08:40 +00:00
104 lines
3.7 KiB
Rust
104 lines
3.7 KiB
Rust
use crate::keys::IssuerKeyStore;
|
|
use crate::payload::Rsp6Ticket;
|
|
use anyhow::anyhow;
|
|
use num_bigint::BigUint;
|
|
|
|
mod keys;
|
|
mod payload;
|
|
|
|
fn base26_decode(input: &str) -> BigUint {
|
|
let mut out = BigUint::new(Vec::new());
|
|
for val in input.as_bytes().iter().rev() {
|
|
out *= 26u32;
|
|
out += *val - b'A';
|
|
}
|
|
BigUint::from_bytes_le(&out.to_bytes_be())
|
|
}
|
|
|
|
fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
|
|
if tkt.is_empty() {
|
|
return None;
|
|
}
|
|
match tkt[0] {
|
|
1 => {
|
|
// PKCS#1 v1
|
|
let tkt = &tkt[1..];
|
|
let mut iter = tkt.iter();
|
|
loop {
|
|
match iter.next()? {
|
|
0 => {
|
|
return Some(iter.as_slice());
|
|
}
|
|
255 => {}
|
|
_ => return None,
|
|
}
|
|
}
|
|
}
|
|
2 => {
|
|
// PKCS#1 v2
|
|
let tkt = &tkt[1..];
|
|
let mut iter = tkt.iter();
|
|
loop {
|
|
match iter.next()? {
|
|
0 => {
|
|
return Some(iter.as_slice());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let iks = IssuerKeyStore::new();
|
|
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()
|
|
.ok_or_else(|| anyhow!("that was a bit rude, give me an actual ticket"))??;
|
|
if ticket_str.len() < 16 {
|
|
return Err(anyhow!("ticket too short"));
|
|
}
|
|
if &ticket_str[0..2] != "06" {
|
|
return Err(anyhow!(
|
|
"ticket isn't a RSP6 ticket: magic was {}",
|
|
&ticket_str[0..2]
|
|
));
|
|
}
|
|
let issuer_id = &ticket_str[13..15];
|
|
let ticket_reference = format!("{}{}", issuer_id, &ticket_str[2..11]);
|
|
eprintln!("[+] RSP6 ticket, reference {}", ticket_reference);
|
|
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 message = ticket.modpow(&key.public_exponent, &key.modulus);
|
|
let message = message.to_bytes_be();
|
|
if let Some(unpadded) = strip_padding(&message) {
|
|
eprintln!("[+] decrypt done!");
|
|
let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into(), ticket_str[11..13].into())?;
|
|
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
|
|
return Ok(());
|
|
} else {
|
|
eprintln!("[-] failed decrypt: {:?}", message);
|
|
}
|
|
}
|
|
Err(anyhow!("no valid decryptions"))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
#[test]
|
|
fn test_base26() {
|
|
assert_eq!(
|
|
super::base26_decode("RANEBZCYPNQVMMYJBOJBONYSIYXTREYFSHTZFZEXWTVBNXJBFVOFBMXVQPZTFWVYSWYKINRXRVDCCUWUERKQZKYBPVIIAPJOOFJJXUBFGNVXGXTCFPBHXYVPEKWIURBEOYTYNZUXWVIXHAODACOQLZEQKRUNGWSJHIIWOYSNXJKVYWIGLWCIZKAHFKKAKRDUQSQBGEJMOFCSHSKXSFDDKYCFQI").to_bytes_be(),
|
|
[53, 242, 184, 141, 14, 99, 169, 215, 200, 223, 85, 250, 45, 253, 184, 100, 225, 124, 82, 70, 138, 222, 246, 185, 192, 129, 247, 218, 24, 26, 249, 112, 74, 225, 71, 139, 27, 50, 218, 11, 93, 238, 232, 163, 151, 68, 159, 146, 80, 133, 11, 45, 57, 245, 163, 117, 218, 11, 187, 246, 18, 147, 88, 171, 133, 216, 166, 47, 232, 246, 198, 170, 99, 36, 120, 114, 73, 207, 19, 218, 202, 146, 158, 223, 107, 234, 171, 172, 20, 189, 133, 246, 192, 248, 57, 111, 65, 65, 135, 64, 241, 99, 87, 107, 75, 40, 224, 223, 100, 53, 180, 212, 53, 200, 172, 117, 127, 248, 193, 0, 147, 167, 222, 81, 135, 158, 135, 137]
|
|
)
|
|
}
|
|
}
|