disint_security/
lib.rs

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>;