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