oauth1_request/
signature_method.rs

1//! Signature methods ([RFC 5849 section 3.4.][rfc]).
2//!
3//! [rfc]: https://tools.ietf.org/html/rfc5849#section-3.4
4//!
5//! The OAuth standard allows for servers to implement their own custom signature methods.
6//! So the module provides an abstraction over signature methods so that users can implement those
7//! custom methods by themselves.
8
9doc_auto_cfg! {
10    #[cfg(feature = "hmac-sha1")]
11    pub mod hmac_sha1;
12    pub mod plaintext;
13    #[cfg(feature = "rsa-sha1-06")]
14    pub mod rsa_sha1_06;
15    #[cfg(feature = "rsa-sha1-09")]
16    pub mod rsa_sha1_09;
17}
18
19#[cfg(any(
20    feature = "hmac-sha1",
21    feature = "rsa-sha1-06",
22    feature = "rsa-sha1-09"
23))]
24mod digest_common;
25#[cfg(feature = "either")]
26mod either;
27
28doc_auto_cfg! {
29    #[cfg(feature = "hmac-sha1")]
30    pub use self::hmac_sha1::HmacSha1;
31    #[cfg(feature = "hmac-sha1")]
32    pub use self::hmac_sha1::HMAC_SHA1;
33    pub use self::plaintext::Plaintext;
34    #[cfg(feature = "alloc")]
35    pub use self::plaintext::PLAINTEXT;
36    // TODO: Suffix the re-export with the version number on next breaking change.
37    #[cfg(feature = "rsa-sha1-06")]
38    pub use self::rsa_sha1_06::RsaSha1;
39    #[cfg(feature = "rsa-sha1-09")]
40    pub use self::rsa_sha1_09::RsaSha1 as Rsa09Sha1;
41}
42
43use core::fmt::{self, Display, Write};
44
45use crate::util::percent_encode;
46
47/// Types that represent a signature method.
48///
49/// This is used to construct a `Self::Sign` and carries configuration data for them.
50pub trait SignatureMethod {
51    /// The algorithm used by this signature method to sign a signature base string.
52    type Sign: Sign;
53
54    /// Creates a `Self::Sign` that signs a signature base string with the given shared-secrets.
55    fn sign_with(self, client_secret: &str, token_secret: Option<&str>) -> Self::Sign;
56}
57
58macro_rules! provide {
59    ($(#[doc = $doc:expr])+ $name:ident, $($rest:tt)*) => {
60        $(#[doc = $doc])+
61        fn $name<V: Display>(&mut self, value: V) {
62            self.parameter(concat!("oauth_", stringify!($name)), value);
63        }
64        provide! { $($rest)* }
65    };
66    ($name:ident, $($rest:tt)*) => {
67        provide! {
68            #[doc = concat!(
69"Feeds `self` with the `oauth_", stringify!($name), "` parameter part of the signature base string.
70
71The default implementation forwards to the `parameter` method with `\"oauth_",
72stringify!($name), "\"` as the first argument."
73            )]
74            $name, $($rest)*
75        }
76    };
77    () => {};
78}
79
80/// Algorithms to sign a signature base string ([RFC 5849 section 3.4.1.][rfc]).
81///
82/// [rfc]: https://tools.ietf.org/html/rfc5849#section-3.4.1
83///
84/// The type will be incrementally passed a signature base string by a `Serializer`. For example,
85/// a signature base string like the following (line breaks are for display purposes only):
86///
87/// ```text
88/// POST&
89/// http%3A%2F%2Fexample.com%2Frequest&
90/// a%3Dr%2520b
91/// %26
92/// a2%3Da
93/// %26
94/// oauth_consumer_key%3D9djdj82h48djs9d2
95/// %26
96/// oauth_nonce%3D7d8f3e4a
97/// %26
98/// oauth_signature_method%3DHMAC-SHA1
99/// %26
100/// oauth_timestamp%3D137131201
101/// %26
102/// oauth_token%3Dkkk9d7dh3k39sjv7
103/// %26
104/// z%3D
105/// ```
106///
107/// ...is represented by a series of method calls like the following (`sign` is the `Sign` object):
108///
109#[cfg_attr(feature = "alloc", doc = " ```")]
110#[cfg_attr(not(feature = "alloc"), doc = " ```ignore")]
111/// # use oauth1_request::signature_method::{Sign, SignatureMethod, PLAINTEXT};
112/// # let mut sign = PLAINTEXT.sign_with("", Some(""));
113/// sign.request_method("POST");
114/// sign.uri("http%3A%2F%2Fexample.com%2Frequest");
115/// sign.parameter("a", "r%2520b");
116/// sign.delimiter();
117/// sign.parameter("a2", "a");
118/// sign.delimiter();
119/// sign.consumer_key("9djdj82h48djs9d2");
120/// sign.delimiter();
121/// sign.nonce("7d8f3e4a");
122/// sign.delimiter();
123/// sign.signature_method();
124/// sign.delimiter();
125/// sign.timestamp(137131201);
126/// sign.delimiter();
127/// sign.token("kkk9d7dh3k39sjv7");
128/// sign.delimiter();
129/// sign.parameter("z", "");
130/// let _ = sign.end();
131/// ```
132pub trait Sign {
133    /// The URL-encoded representation of `oauth_signature` string the algorithm produces.
134    type Signature: Display;
135
136    /// Returns the `oauth_signature_method` string for the signature method associated with the
137    /// algorithm.
138    fn get_signature_method_name(&self) -> &'static str;
139
140    /// Feeds `self` with the HTTP request method part of the signature base string.
141    fn request_method(&mut self, method: &str);
142
143    /// Feeds `self` with the base string URI part of the signature base string.
144    fn uri<T: Display>(&mut self, uri: T);
145
146    /// Feeds `self` with a key-value parameter pair of the signature base string.
147    ///
148    /// Implementors can reproduce the part of the signature base string the arguments represent
149    /// by `format!("{}%3D{}", key, value)`.
150    fn parameter<V: Display>(&mut self, key: &str, value: V);
151
152    /// Feeds `self` with the delimiter (`%26`) between parameters.
153    fn delimiter(&mut self);
154
155    /// Finalizes the signing process and returns the resulting signature.
156    fn end(self) -> Self::Signature;
157
158    provide! { callback, consumer_key, nonce, }
159
160    /// Whether the signature method uses the `oauth_nonce` parameter.
161    ///
162    /// If this method returns `false`, `Serializer` implementations should not emit the
163    /// `oauth_nonce` part of the signature base string.
164    ///
165    /// The default implementation returns `true`.
166    fn use_nonce(&self) -> bool {
167        true
168    }
169
170    /// Feeds `self` with the `oauth_signature_method` parameter part of the
171    /// signature base string.
172    ///
173    /// The default implementation forwards to the `parameter` method with
174    /// `"oauth_signature_method"` and `self.get_signature_method_name()` as the arguments.
175    fn signature_method(&mut self) {
176        self.parameter("oauth_signature_method", self.get_signature_method_name());
177    }
178
179    /// Feeds `self` with the `oauth_timestamp` parameter part of the
180    /// signature base string.
181    ///
182    /// The default implementation forwards to the `parameter` method with
183    /// `"oauth_timestamp"` as the first argument.
184    fn timestamp(&mut self, value: u64) {
185        self.parameter("oauth_timestamp", value);
186    }
187
188    /// Whether the signature method uses the `oauth_nonce` parameter.
189    ///
190    /// If this method returns `false`, `Serializer` implementations should not emit the
191    /// `oauth_nonce` part of the signature base string.
192    ///
193    /// The default implementation returns `true`.
194    fn use_timestamp(&self) -> bool {
195        true
196    }
197
198    provide! { token, verifier, }
199
200    /// Feeds `self` with the `oauth_version` parameter part of the signature base string.
201    ///
202    /// The default implementation forwards to the `parameter` method with
203    /// `"oauth_version"` and `"1.0"` as the arguments.
204    fn version(&mut self) {
205        self.parameter("oauth_version", "1.0");
206    }
207}
208
209fn write_signing_key<W: Write>(
210    dst: &mut W,
211    client_secret: &str,
212    token_secret: Option<&str>,
213) -> fmt::Result {
214    write!(dst, "{}", percent_encode(client_secret))?;
215    dst.write_str("&")?;
216    if let Some(ts) = token_secret {
217        write!(dst, "{}", percent_encode(ts))?;
218    }
219    Ok(())
220}