cast/cmd/tempo/zone/
auth.rs1use alloy_primitives::{B256, hex, keccak256};
6use alloy_signer::Signer;
7use eyre::Result;
8use std::time::{SystemTime, UNIX_EPOCH};
9
10pub(super) const HEADER: &str = "x-authorization-token";
11
12pub(super) async fn sign_token(
14 signer: &impl Signer,
15 zone_id: u32,
16 chain_id: u64,
17) -> Result<String> {
18 let issued = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
19 let (fields, digest) = token_fields(zone_id, chain_id, issued, issued + 600);
20 let signature = signer.sign_hash(&digest).await?;
21 let mut token = signature.as_bytes().to_vec();
22 token.extend_from_slice(&fields);
23 Ok(hex::encode(token))
24}
25
26fn token_fields(zone_id: u32, chain_id: u64, issued: u64, expires: u64) -> ([u8; 29], B256) {
27 let mut message = [0u8; 61];
28 message[..12].copy_from_slice(b"TempoZoneRPC");
29 message[33..37].copy_from_slice(&zone_id.to_be_bytes());
30 message[37..45].copy_from_slice(&chain_id.to_be_bytes());
31 message[45..53].copy_from_slice(&issued.to_be_bytes());
32 message[53..61].copy_from_slice(&expires.to_be_bytes());
33 (message[32..].try_into().unwrap(), keccak256(message))
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use alloy_primitives::Signature;
40 use alloy_signer_local::PrivateKeySigner;
41
42 #[tokio::test]
43 async fn token_recovers_wallet_from_raw_digest() {
44 let signer = PrivateKeySigner::random();
45 let token = hex::decode(sign_token(&signer, 42, 1337).await.unwrap()).unwrap();
46 assert_eq!(token.len(), 94);
47 let fields = &token[65..];
48 assert_eq!(&fields[..13], &hex::decode("000000002a0000000000000539").unwrap());
49 let issued = u64::from_be_bytes(fields[13..21].try_into().unwrap());
50 let expires = u64::from_be_bytes(fields[21..29].try_into().unwrap());
51 assert_eq!(expires - issued, 600);
52 let (_, digest) = token_fields(42, 1337, issued, expires);
53 let signature = Signature::try_from(&token[..65]).unwrap();
54 assert_eq!(signature.recover_address_from_prehash(&digest).unwrap(), signer.address());
55 }
56
57 #[test]
58 fn matches_zones_wire_vector() {
59 let (fields, digest) = token_fields(
60 0x0102_0304,
61 0x0506_0708_090a_0b0c,
62 0x0d0e_0f10_1112_1314,
63 0x1516_1718_191a_1b1c,
64 );
65 assert_eq!(
66 hex::encode(fields),
67 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c"
68 );
69 assert_eq!(
70 digest,
71 "0xf827387a933f40dfedece81ba4933feaef89e98a269f52f4f54dda2f1dac4171"
72 .parse::<B256>()
73 .unwrap()
74 );
75 }
76}