oauth1_request/signature_method/
hmac_sha1.rs

1//! The `HMAC-SHA1` signature method ([RFC 5849 section 3.4.2.][rfc]).
2//!
3//! [rfc]: https://tools.ietf.org/html/rfc5849#section-3.4.2
4//!
5//! This module is only available when `hmac-sha1` feature is activated.
6
7use core::fmt::{self, Debug, Display, Formatter, Write};
8
9use digest::core_api::BlockSizeUser;
10use digest::generic_array::sequence::GenericSequence;
11use digest::generic_array::GenericArray;
12use digest::OutputSizeUser;
13use hmac::{Hmac, Mac};
14use sha1::{Digest, Sha1};
15
16use super::digest_common::{Base64PercentEncodeDisplay, UpdateSign};
17use super::{write_signing_key, Sign, SignatureMethod};
18
19/// The `HMAC-SHA1` signature method.
20#[derive(Clone, Copy, Default)]
21pub struct HmacSha1 {
22    _priv: (),
23}
24
25/// A type that signs a signature base string with the HMAC-SHA1 signature algorithm.
26#[derive(Clone, Debug)]
27pub struct HmacSha1Sign {
28    inner: UpdateSign<Hmac<Sha1>>,
29}
30
31/// A signature produced by an `HmacSha1Sign`.
32pub struct HmacSha1Signature {
33    inner: Base64PercentEncodeDisplay<GenericArray<u8, <Sha1 as OutputSizeUser>::OutputSize>>,
34}
35
36/// The `HMAC-SHA1` signature method with a default configuration.
37pub const HMAC_SHA1: HmacSha1 = HmacSha1::new();
38
39#[derive(Clone)]
40enum SigningKey {
41    Key {
42        buf: GenericArray<u8, <Sha1 as BlockSizeUser>::BlockSize>,
43        pos: usize,
44    },
45    Digest(Sha1),
46}
47
48impl HmacSha1 {
49    /// Creates a new `HmacSha1`.
50    pub const fn new() -> Self {
51        HmacSha1 { _priv: () }
52    }
53}
54
55impl Debug for HmacSha1 {
56    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57        #[derive(Debug)]
58        struct HmacSha1;
59        HmacSha1.fmt(f)
60    }
61}
62
63impl SignatureMethod for HmacSha1 {
64    type Sign = HmacSha1Sign;
65
66    fn sign_with(self, client_secret: &str, token_secret: Option<&str>) -> HmacSha1Sign {
67        let mut key = SigningKey::new();
68        write_signing_key(&mut key, client_secret, token_secret).unwrap();
69        HmacSha1Sign {
70            inner: UpdateSign(key.into_hmac()),
71        }
72    }
73}
74
75impl Sign for HmacSha1Sign {
76    type Signature = HmacSha1Signature;
77
78    fn get_signature_method_name(&self) -> &'static str {
79        "HMAC-SHA1"
80    }
81
82    fn request_method(&mut self, method: &str) {
83        self.inner.request_method(method);
84    }
85
86    fn uri<T: Display>(&mut self, uri: T) {
87        self.inner.uri(uri);
88    }
89
90    fn parameter<V: Display>(&mut self, key: &str, value: V) {
91        self.inner.parameter(key, value);
92    }
93
94    fn delimiter(&mut self) {
95        self.inner.delimiter();
96    }
97
98    fn end(self) -> HmacSha1Signature {
99        HmacSha1Signature {
100            inner: Base64PercentEncodeDisplay(self.inner.0.finalize().into_bytes()),
101        }
102    }
103}
104
105impl Display for HmacSha1Signature {
106    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
107        self.inner.fmt(f)
108    }
109}
110
111impl SigningKey {
112    fn new() -> Self {
113        SigningKey::Key {
114            buf: GenericArray::generate(|_| 0),
115            pos: 0,
116        }
117    }
118
119    fn write(&mut self, input: &[u8]) {
120        *self = match *self {
121            SigningKey::Key {
122                ref mut buf,
123                ref mut pos,
124            } => {
125                if input.len() > buf.len() - *pos {
126                    let mut digest = Sha1::default();
127                    digest.update(&buf[..*pos]);
128                    digest.update(input);
129                    SigningKey::Digest(digest)
130                } else {
131                    buf[*pos..(*pos + input.len())].copy_from_slice(input);
132                    *pos += input.len();
133                    return;
134                }
135            }
136            SigningKey::Digest(ref mut digest) => {
137                digest.update(input);
138                return;
139            }
140        };
141    }
142
143    fn into_hmac(self) -> Hmac<Sha1> {
144        match self {
145            SigningKey::Key { ref buf, pos } => Hmac::new_from_slice(&buf[..pos]).unwrap(),
146            SigningKey::Digest(digest) => Hmac::new_from_slice(&digest.finalize()).unwrap(),
147        }
148    }
149}
150
151impl Write for SigningKey {
152    fn write_str(&mut self, s: &str) -> fmt::Result {
153        self.write(s.as_bytes());
154        Ok(())
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    extern crate alloc;
161
162    use alloc::vec::Vec;
163
164    use digest::generic_array::typenum::Unsigned;
165
166    use super::*;
167
168    #[test]
169    fn signing_key() {
170        let mut sk = SigningKey::new();
171        let mut k = Vec::new();
172
173        for _ in 0..=<Sha1 as BlockSizeUser>::BlockSize::to_usize() + 1 {
174            sk.write(&[1]);
175            k.extend(&[1]);
176
177            let mut skm = sk.clone().into_hmac();
178            let mut m = Hmac::<Sha1>::new_from_slice(&k).unwrap();
179            skm.update(b"test");
180            m.update(b"test");
181
182            assert_eq!(skm.finalize().into_bytes(), m.finalize().into_bytes());
183        }
184    }
185}