1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! The `hawk` crate provides support for [Hawk](https://github.com/hueniverse/hawk)
//! authentictation. It is a low-level crate, used by higher-level crates to integrate with various
//! Rust HTTP libraries.  For example `hyper-hawk` integrates Hawk with Hyper.
//!
//! # Examples
//!
//! ## Hawk Client
//!
//! A client can attach a Hawk Authorization header to requests by providing credentials to a
//! Request instance, which will generate the header.
//!
//! ```
//! use hawk::{RequestBuilder, Credentials, Key, SHA256, PayloadHasher};
//! use std::time::{Duration, UNIX_EPOCH};
//!
//! // provide the Hawk id and key
//! let credentials = Credentials {
//!     id: "test-client".to_string(),
//!     key: Key::new(vec![99u8; 32], SHA256).unwrap(),
//! };
//!
//! let payload_hash = PayloadHasher::hash("text/plain", SHA256, "request-body").unwrap();
//!
//! // provide the details of the request to be authorized
//!  let request = RequestBuilder::new("POST", "example.com", 80, "/v1/users")
//!     .hash(&payload_hash[..])
//!     .request();
//!
//! // Get the resulting header, including the calculated MAC; this involves a random
//! // nonce, so the MAC will be different on every request.
//! let header = request.make_header(&credentials).unwrap();
//!
//! // the header would the be attached to the request
//! assert_eq!(header.id.unwrap(), "test-client");
//! assert_eq!(header.mac.unwrap().len(), 32);
//! assert_eq!(header.hash.unwrap().len(), 32);
//! ```
//!
//! A client that wishes to use a bewit (URL parameter) can do so as follows:
//!
//! ```
//! use hawk::{RequestBuilder, Credentials, Key, SHA256, Bewit};
//! use std::time::Duration;
//! use std::borrow::Cow;
//!
//! let credentials = Credentials {
//!     id: "me".to_string(),
//!     key: Key::new("tok", SHA256).unwrap(),
//! };
//!
//! let client_req = RequestBuilder::new("GET", "mysite.com", 443, "/resource").request();
//! let client_bewit = client_req
//!     .make_bewit_with_ttl(&credentials, Duration::from_secs(10))
//!     .unwrap();
//! let request_path = format!("/resource?bewit={}", client_bewit.to_str());
//! // .. make the request
//! ```
//!
//! ## Hawk Server
//!
//! To act as a server, parse the Hawk Authorization header from the request, generate a new
//! Request instance, and use the request to validate the header.
//!
//! ```
//! use hawk::{RequestBuilder, Header, Key, SHA256};
//! use hawk::mac::Mac;
//! use std::time::{Duration, UNIX_EPOCH};
//!
//! let mac = Mac::from(vec![7, 22, 226, 240, 84, 78, 49, 75, 115, 144, 70,
//!                          106, 102, 134, 144, 128, 225, 239, 95, 132, 202,
//!                          154, 213, 118, 19, 63, 183, 108, 215, 134, 118, 115]);
//! // get the header (usually from the received request; constructed directly here)
//! let hdr = Header::new(Some("dh37fgj492je"),
//!                       Some(UNIX_EPOCH + Duration::new(1353832234, 0)),
//!                       Some("j4h3g2"),
//!                       Some(mac),
//!                       Some("my-ext-value"),
//!                       Some(vec![1, 2, 3, 4]),
//!                       Some("my-app"),
//!                       Some("my-dlg")).unwrap();
//!
//! // build a request object based on what we know
//! let hash = vec![1, 2, 3, 4];
//! let request = RequestBuilder::new("GET", "localhost", 443, "/resource")
//!     .hash(&hash[..])
//!     .request();
//!
//! let key = Key::new(vec![99u8; 32], SHA256).unwrap();
//! let one_week_in_secs = 7 * 24 * 60 * 60;
//! if !request.validate_header(&hdr, &key, Duration::from_secs(5200 * one_week_in_secs)) {
//!     panic!("header validation failed. Is it 2117 already?");
//! }
//! ```
//!
//! A server which validates bewits looks like this:
//!
//! ```
//! use hawk::{RequestBuilder, Credentials, Key, SHA256, Bewit};
//! use std::time::Duration;
//! use std::borrow::Cow;
//!
//! let credentials = Credentials {
//!     id: "me".to_string(),
//!     key: Key::new("tok", SHA256).unwrap(),
//! };
//!
//! // simulate the client generation of a bewit
//! let client_req = RequestBuilder::new("GET", "mysite.com", 443, "/resource").request();
//! let client_bewit = client_req
//!     .make_bewit_with_ttl(&credentials, Duration::from_secs(10))
//!     .unwrap();
//! let request_path = format!("/resource?bewit={}", client_bewit.to_str());
//!
//! let mut maybe_bewit = None;
//! let server_req = RequestBuilder::new("GET", "mysite.com", 443, &request_path)
//!     .extract_bewit(&mut maybe_bewit).unwrap()
//!     .request();
//! let bewit = maybe_bewit.unwrap();
//! assert_eq!(bewit.id(), "me");
//! assert!(server_req.validate_bewit(&bewit, &credentials.key));
//! ```
//!
//! ## Features
//!
//! By default, the `use_ring` feature is enabled, which means that this crate will
//! use `ring` for all cryptographic operations.
//!
//! Alternatively, one can configure the crate with the `use_openssl`
//! feature to use the `openssl` crate.
//!
//! If no features are enabled, you must provide a custom implementation of the
//! [`hawk::crypto::Cryptographer`] trait to the `set_cryptographer` function, or
//! the cryptographic operations will panic.
//!
//! Attempting to configure both the `use_ring` and `use_openssl` features will
//! result in a build error.

#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;

mod header;
pub use crate::header::Header;

mod credentials;
pub use crate::credentials::{Credentials, DigestAlgorithm, Key};

mod request;
pub use crate::request::{Request, RequestBuilder};

mod response;
pub use crate::response::{Response, ResponseBuilder};

mod error;
pub use crate::error::*;

mod payload;
pub use crate::payload::PayloadHasher;

mod bewit;
pub use crate::bewit::Bewit;

mod b64;

pub mod mac;

pub mod crypto;

pub const SHA256: DigestAlgorithm = DigestAlgorithm::Sha256;
pub const SHA384: DigestAlgorithm = DigestAlgorithm::Sha384;
pub const SHA512: DigestAlgorithm = DigestAlgorithm::Sha512;