1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use openssl::{hash::MessageDigest, memcmp, pkey::PKey, rand::rand_bytes, sign::Signer};
use smallvec::{smallvec, SmallVec};

use crate::{Error, Result, SigningKey, VerificationKey};

#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HmacAlgorithm {
    HS256,
    HS384,
    HS512,
}

impl HmacAlgorithm {
    fn name(self) -> &'static str {
        use HmacAlgorithm::*;
        match self {
            HS256 => "HS256",
            HS384 => "HS384",
            HS512 => "HS512",
        }
    }

    fn digest(self) -> MessageDigest {
        use HmacAlgorithm::*;
        match self {
            HS256 => MessageDigest::sha256(),
            HS384 => MessageDigest::sha384(),
            HS512 => MessageDigest::sha512(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct HmacKey {
    k: SmallVec<[u8; 32]>,
    algorithm: HmacAlgorithm,
}

impl HmacKey {
    #[inline]
    pub fn generate(algorithm: HmacAlgorithm) -> Result<Self> {
        let len = match algorithm {
            HmacAlgorithm::HS256 => 32,
            HmacAlgorithm::HS384 => 48,
            HmacAlgorithm::HS512 => 64,
        };

        let mut k = smallvec![0u8; len];
        rand_bytes(&mut k)?;

        Ok(Self { k, algorithm })
    }

    /// The key should have enough entropy. At least 32-byte of full entropy is
    /// recommended.
    #[inline]
    pub fn from_bytes(k: &[u8], algorithm: HmacAlgorithm) -> Self {
        Self {
            k: k.into(),
            algorithm,
        }
    }

    #[inline]
    pub fn serialize(&self) -> &[u8] {
        &self.k
    }
}

impl SigningKey for HmacKey {
    fn sign(&self, v: &[u8]) -> Result<SmallVec<[u8; 64]>> {
        let pk = PKey::hmac(&self.k)?;
        let mut signer = Signer::new(self.algorithm.digest(), pk.as_ref())?;

        let mut sig = smallvec![0u8; signer.len()?];
        signer.sign_oneshot(&mut sig, v)?;
        Ok(sig)
    }

    #[inline]
    fn alg(&self) -> &'static str {
        self.algorithm.name()
    }
}

impl VerificationKey for HmacKey {
    fn verify(&self, v: &[u8], sig: &[u8], alg: &str) -> Result<()> {
        if alg != self.algorithm.name() {
            return Err(Error::VerificationError);
        }

        let expected = self.sign(v)?;

        if memcmp::eq(sig, &expected) {
            Ok(())
        } else {
            Err(Error::VerificationError)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn conversion() -> Result<()> {
        let k = HmacKey::generate(HmacAlgorithm::HS384)?;
        assert_eq!(SigningKey::alg(&k), "HS384");
        let k1 = k.clone();
        let k1 = k1.serialize();
        HmacKey::from_bytes(k1, HmacAlgorithm::HS256);
        println!("{:?}", k);
        Ok(())
    }

    #[test]
    fn sign_and_verify() -> Result<()> {
        for alg in [
            HmacAlgorithm::HS256,
            HmacAlgorithm::HS384,
            HmacAlgorithm::HS512,
        ] {
            let k = HmacKey::from_bytes(b"key", alg);
            let sig = k.sign(b"...")?;
            assert!(k.verify(b"...", &sig, alg.name()).is_ok());
            assert!(k.verify(b"...", &sig, "WRONG ALG").is_err());
            assert!(k.verify(b"....", &sig, alg.name()).is_err());
        }
        Ok(())
    }
}