oauth1_request_ios/signature_method/
hmac_sha256.rs

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