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}