1use std::fmt;
2
3fn parse_hex(hex: impl AsRef<str>) -> Option<Vec<u8>> {
4 let hex = hex.as_ref().as_bytes();
5 if hex.len() % 2 != 0 {
6 return None;
7 }
8
9 hex.chunks_exact(2)
10 .map(|byte| {
11 let (a, b) = match byte {
12 &[a, b] => (a, b),
13 _ => unreachable!(),
14 };
15 let a = match a {
16 b'0'..=b'9' => a - b'0',
17 b'A'..=b'F' => a - b'A' + 10,
18 b'a'..=b'f' => a - b'a' + 10,
19 _ => return None,
20 };
21 let b = match b {
22 b'0'..=b'9' => b - b'0',
23 b'A'..=b'F' => b - b'A' + 10,
24 b'a'..=b'f' => b - b'a' + 10,
25 _ => return None,
26 };
27 Some(a << 4 | b)
28 })
29 .collect::<Option<Vec<_>>>()
30}
31
32#[derive(Clone)]
33pub struct Application {
34 public_key: ring::signature::UnparsedPublicKey<Vec<u8>>,
35}
36
37impl Application {
38 pub fn from_public_key(public_key: impl AsRef<str>) -> crate::Result<Self> {
39 let public_key = parse_hex(public_key).ok_or(crate::Error::KeyFormat)?;
40 let public_key =
41 ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, public_key);
42 Ok(Self { public_key })
43 }
44
45 pub fn verify(&self, body: &[u8], timestamp: &str, signature: &str) -> crate::Result<()> {
46 let timestamp = timestamp.as_bytes().to_vec();
47 let signature = parse_hex(signature);
48 let signature = if let Some(v) = signature {
49 v
50 } else {
51 return Err(crate::Error::SignatureFormat);
52 };
53
54 let mut message = timestamp;
55 message.extend_from_slice(body);
56 self.public_key
57 .verify(&message, &signature)
58 .map_err(|_| crate::Error::Verification)
59 }
60}
61
62impl fmt::Debug for Application {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("Application")
65 .field("public_key", &format_args!("(...)"))
66 .finish()
67 }
68}
69
70#[derive(Debug)]
71#[non_exhaustive]
72pub enum Error {
73 KeyFormat,
74 SignatureFormat,
75 Verification,
76}
77
78impl Error {
79 pub fn http_status(&self) -> u16 {
80 match self {
81 Error::KeyFormat | Error::SignatureFormat => 400,
82 Error::Verification => 401,
83 }
84 }
85}
86
87impl std::error::Error for Error {}
88
89impl fmt::Display for Error {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Error::KeyFormat => f.write_str("invalid public key format"),
93 Error::SignatureFormat => f.write_str("invalid signature format"),
94 Error::Verification => f.write_str("verification failed"),
95 }
96 }
97}
98
99pub type Result<T> = std::result::Result<T, Error>;