sphinx_auther/
token.rs

1use crate::{recover_pubkey, sign_message, verify_message};
2
3use anyhow::{anyhow, Result};
4use base64::{decode_config, encode_config, URL_SAFE};
5use secp256k1::{PublicKey, SecretKey};
6use std::convert::TryInto;
7
8#[derive(Debug)]
9pub struct Token(u32, Option<[u8; 65]>);
10
11fn now() -> u32 {
12    use std::time::{SystemTime, UNIX_EPOCH};
13    let start = SystemTime::now();
14    let since_the_epoch = start
15        .duration_since(UNIX_EPOCH)
16        .expect("Time went backwards");
17    since_the_epoch.as_secs() as u32
18}
19
20pub fn u32_to_bytes(input: u32) -> [u8; 4] {
21    input.to_be_bytes()
22}
23pub fn bytes_to_u32(bytes: [u8; 4]) -> u32 {
24    u32::from_be_bytes(bytes)
25}
26
27pub fn base64_encode(input: &[u8]) -> String {
28    encode_config(input, URL_SAFE)
29}
30pub fn base64_decode(input: &str) -> Result<Vec<u8>> {
31    let r = decode_config(input, URL_SAFE)?;
32    Ok(r)
33}
34
35impl Token {
36    /// Creates a new token with current timestamp
37    pub fn new() -> Self {
38        Self(now(), None)
39    }
40    pub fn set_sig(&mut self, sig: [u8; 65]) {
41        self.1 = Some(sig)
42    }
43    pub fn from_base64(s: &str) -> Result<Self> {
44        let bytes = base64_decode(s)?;
45        if bytes.len() != 69 {
46            return Err(anyhow!("wrong length".to_string()));
47        }
48        let ts: [u8; 4] = bytes[..4].try_into()?;
49        let sig: [u8; 65] = bytes[4..].try_into()?;
50        Ok(Self(bytes_to_u32(ts), Some(sig)))
51    }
52    pub fn expected_len(&self) -> usize {
53        69
54    }
55    /// Sign a lightning token
56    pub fn sign(&self, secret_key: &SecretKey) -> Result<Vec<u8>> {
57        let mut ts = u32_to_bytes(self.0).to_vec();
58        let sig = sign_message(&ts, secret_key)?;
59        ts.extend(sig);
60        assert_eq!(ts.len(), self.expected_len());
61        Ok(ts)
62    }
63    /// Sign a lightning token
64    pub fn sign_to_base64(&self, secret_key: &SecretKey) -> Result<String> {
65        let s = self.sign(secret_key)?;
66        Ok(base64_encode(&s))
67    }
68    /// Verify signed token
69    pub fn verify(&self, public_key: &PublicKey) -> Result<()> {
70        if let None = self.1 {
71            return Err(anyhow!("no sig".to_string()));
72        }
73        let msg = u32_to_bytes(self.0);
74        verify_message(&msg.to_vec(), &self.1.unwrap(), public_key)
75    }
76    /// Recover pubkey from signed token
77    pub fn recover(&self) -> Result<PublicKey> {
78        if let None = self.1 {
79            return Err(anyhow!("no sig".to_string()));
80        }
81        let msg = u32_to_bytes(self.0);
82        recover_pubkey(&msg.to_vec(), &self.1.unwrap())
83    }
84    /// Recover pubkey from signed token, and check timestamp
85    pub fn recover_within(&self, secs: u32) -> Result<PublicKey> {
86        if let None = self.1 {
87            return Err(anyhow!("no sig".to_string()));
88        }
89        if self.0 < now() - secs {
90            return Err(anyhow!("expired".to_string()));
91        }
92        let msg = u32_to_bytes(self.0);
93        recover_pubkey(&msg.to_vec(), &self.1.unwrap())
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::token::*;
100    use secp256k1::{PublicKey, Secp256k1, SecretKey};
101
102    fn secret_key() -> SecretKey {
103        SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order")
104    }
105
106    fn a_token() -> String {
107        "YvM0wyAZBWdHaVsS4sqy-ub3X0JRx7zVTY9O6aL0q_CIV9zOKykO_grPE4DSelinHNX9pTFZ3wEoLhg5QT7EVZpOlj0x".to_string()
108    }
109
110    #[test]
111    fn test_token() {
112        let sk = secret_key();
113        let mut t = Token::new();
114        let res = t.sign(&sk).expect("couldnt make token");
115        println!("===> {}", base64_encode(&res));
116        let secp = Secp256k1::new();
117        let public_key = PublicKey::from_secret_key(&secp, &sk);
118        let sig: [u8; 65] = res[4..].try_into().expect("wrong sig length");
119        t.set_sig(sig);
120        t.verify(&public_key).expect("couldnt verify");
121    }
122
123    #[test]
124    fn test_decode() {
125        let sk = secret_key();
126        let secp = Secp256k1::new();
127        let public_key = PublicKey::from_secret_key(&secp, &sk);
128        let t = Token::from_base64(&a_token()).expect("couldnt parse base64");
129        t.verify(&public_key).expect("failed to verify");
130    }
131
132    #[test]
133    fn test_recover() {
134        let sk = secret_key();
135        let secp = Secp256k1::new();
136        let public_key = PublicKey::from_secret_key(&secp, &sk);
137        let t = Token::from_base64(&a_token()).expect("couldnt parse base64");
138        let pk2 = t.recover().expect("failed to verify");
139        assert_eq!(public_key, pk2);
140    }
141    #[test]
142    fn test_recover_within() {
143        let sk = secret_key();
144        let t1 = Token::new();
145        let res = t1.sign(&sk).expect("couldnt make token");
146        let token = base64_encode(&res);
147        let secp = Secp256k1::new();
148        let public_key = PublicKey::from_secret_key(&secp, &sk);
149        let t = Token::from_base64(&token).expect("couldnt parse base64");
150        let pk2 = t.recover_within(10).expect("failed to verify");
151        assert_eq!(public_key, pk2);
152    }
153
154    #[test]
155    fn test_check_timestamp() {
156        let sk = secret_key();
157        let t1 = Token::new();
158        let token = t1.sign_to_base64(&sk).expect("couldnt make token");
159        // let token = base64_encode(&res);
160        let t = Token::from_base64(&token).expect("couldnt parse base64");
161        std::thread::sleep(std::time::Duration::from_secs(2));
162        if t.recover_within(1).is_ok() {
163            panic!("should have expired")
164        }
165    }
166
167    #[test]
168    fn test_tribe() {
169        let pk = hex::decode("02290714deafd0cb33d2be3b634fc977a98a9c9fa1dd6c53cf17d99b350c08c67b")
170            .expect("hex fail");
171        let pubkey = PublicKey::from_slice(&pk[..]).expect("couldnt extract pubkey");
172        let tribe = "XuOp5B9kC3CcL52svtl_LJJJFbV1OTgnq7thtOjdKJMnOuETIw_hlLkVfonozVIwz5wADlya_i946GiKFZAgMto0cDuk";
173        let token = Token::from_base64(tribe).expect("couldnt parse base64");
174        token.verify(&pubkey).expect("nope verify");
175        let pk2 = token.recover().expect("recover failed");
176        assert_eq!(pubkey, pk2);
177    }
178}