rsp6_decoder/
lib.rs

1use crate::keys::IssuerKeyStore;
2use crate::payload::Rsp6Ticket;
3use anyhow::anyhow;
4use num_bigint::BigUint;
5
6pub mod keys;
7pub mod payload;
8#[cfg(feature = "wasm")]
9pub mod wasm;
10
11fn base26_decode(input: &str) -> BigUint {
12    let mut out = BigUint::new(Vec::new());
13    for val in input.as_bytes().iter().rev() {
14        out *= 26u32;
15        out += *val - b'A';
16    }
17    BigUint::from_bytes_le(&out.to_bytes_be())
18}
19
20fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
21    if tkt.is_empty() {
22        return None;
23    }
24    match tkt[0] {
25        1 => {
26            // PKCS#1 v1
27            let tkt = &tkt[1..];
28            let mut iter = tkt.iter();
29            loop {
30                match iter.next()? {
31                    0 => {
32                        return Some(iter.as_slice());
33                    }
34                    255 => {}
35                    _ => return None,
36                }
37            }
38        }
39        2 => {
40            // PKCS#1 v2
41            let tkt = &tkt[1..];
42            let mut iter = tkt.iter();
43            loop {
44                match iter.next()? {
45                    0 => {
46                        return Some(iter.as_slice());
47                    }
48                    _ => {}
49                }
50            }
51        }
52        _ => None,
53    }
54}
55
56pub fn decode_ticket(iks: &IssuerKeyStore, ticket_str: &str) -> anyhow::Result<Rsp6Ticket> {
57    if ticket_str.len() < 16 {
58        return Err(anyhow!("ticket too short"));
59    }
60    if &ticket_str[0..2] != "06" {
61        return Err(anyhow!(
62            "ticket isn't a RSP6 ticket: magic was {}",
63            &ticket_str[0..2]
64        ));
65    }
66    let issuer_id = &ticket_str[13..15];
67    let ticket = base26_decode(&ticket_str[15..]);
68    let keys = iks
69        .keys
70        .get(issuer_id)
71        .ok_or_else(|| anyhow!("unknown issuer ID {}", issuer_id))?;
72    for key in keys {
73        let message = ticket.modpow(&key.public_exponent, &key.modulus);
74        let message = message.to_bytes_be();
75        if let Some(unpadded) = strip_padding(&message) {
76            let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into(), ticket_str[11..13].into())?;
77            return Ok(ticket);
78        }
79    }
80    Err(anyhow!(
81        "all signature unwrap attempts failed (tried {} keys for issuer {})",
82        keys.len(),
83        issuer_id
84    ))
85}
86
87#[cfg(test)]
88mod test {
89    #[test]
90    fn test_base26() {
91        assert_eq!(
92            super::base26_decode("RANEBZCYPNQVMMYJBOJBONYSIYXTREYFSHTZFZEXWTVBNXJBFVOFBMXVQPZTFWVYSWYKINRXRVDCCUWUERKQZKYBPVIIAPJOOFJJXUBFGNVXGXTCFPBHXYVPEKWIURBEOYTYNZUXWVIXHAODACOQLZEQKRUNGWSJHIIWOYSNXJKVYWIGLWCIZKAHFKKAKRDUQSQBGEJMOFCSHSKXSFDDKYCFQI").to_bytes_be(),
93            [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]
94        )
95    }
96}