This crate is used to create and verify HTTP Signatures, defined here. It has support for Hyper, Rocket, and Reqwest types. In the future, I might also support Iron middleware for verification.
Since this crate is built to modularly require dependencies, running the examples is not as straightforward as for other projects. To run hyper_server
and hyper_client
, the proper commands are cargo run --example hyper_server --features use_hyper
and cargo run --example hyper_client --features use_hyper
. The hyper examples are configured to talk to eachother by default. The server runs on port 3000, and the client POSTs on port 3000. The rocket server (cargo run --example rocket --features use_rocket
) runs on port 8000, and the reqwest client (cargo run --example reqwest --features use_reqwest
) GETs on port 8000.
Add this to your Cargo.toml
toml
[dependencies.http-signatures]
version = "0.1"
features = ["use_hyper"]
Use it when building a request as follows. ```rust let keyid = "some-username-or-something"; let privatekey = File::open("test/assets/private.der").unwrap();
let mut core = Core::new().unwrap(); let client = Client::new(&core.handle());
let json = r#"{"library":"hyper"}"#; let mut req = Request::new(Method::Post, "http://localhost:3000".parse().unwrap()); req.headersmut().set(ContentType::json()); req.headersmut().set(ContentLength(json.len() as u64)); req.set_body(json);
// Add the HTTP Signature req.withhttpsignature( keyid.into(), privatekey, SignatureAlgorithm::RSA(ShaSize::FiveTwelve), ).unwrap();
let post = client.request(req).and_then(|res| { println!("POST: {}", res.status());
res.body().concat2()
});
core.run(post).unwrap(); ```
This is a very basic example server outline that should give you a general idea of how to set up a Hyper server that verifies HTTP Signatures. This is not meant to be code that actually works. ```rust
struct MyKeyGetter {
key: Vec
impl MyKeyGetter {
fn new(filename: &str) -> Result
Ok(MyKeyGetter { key })
}
}
impl GetKey for MyKeyGetter {
type Key = Cursor
fn get_key(self, _key_id: &str) -> Result<Self::Key, ..> {
Ok(Cursor::new(self.key.clone()))
}
}
struct HelloWorld { key_getter: MyKeyGetter, }
impl HelloWorld {
fn new(filename: &str) -> Result const PHRASE: &'static str = "Hewwo, Mr. Obama???"; impl Service for HelloWorld {
... } fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let server = Http::new()
.bind(&addr, || {
Ok(HelloWorld::new("test/assets/public.der").unwrap())
})
.unwrap();
server.run().unwrap();
}
``` Add this to your ```rust
let keyid = "some-username-or-something".into();
let privatekey = File::open("test/assets/private.der").unwrap(); let client = Client::new();
let mut req = client
.post("http://localhost:3000")
.body("Some Body")
.build()
.unwrap(); req.withhttpsignature(
keyid,
privatekey,
SignatureAlgorithm::RSA(ShaSize::FiveTwelve),
).unwrap(); client.execute(req).unwrap();
``` Add this to your struct MyKeyGetter {
key: Vec impl MyKeyGetter {
fn new(filename: &str) -> Result } impl GetKey for MyKeyGetter {
type Key = Cursor } struct Verified; impl<'a, 'r> FromRequest<'a, 'r> for Verified {
type Error = (); } fn index(_verified: Verified) -> &'static str {
"Successfully verified request"
} fn main() {
let key_getter = MyKeyGetter::new("test/assets/public.der").unwrap(); }
``` Since examples could have tests, they get compiled during a Please be aware that all code contributed to this project will be licensed under the GPL version 3. HTTP Signatures is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. HTTP Signatures is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This file is part of HTTP Signatures You should have received a copy of the GNU General Public License along with HTTP Signatures If not, see http://www.gnu.org/licenses/.fn call(&self, req: Request) -> Self::Future {
let verified = req.verify_authorization_header(self.key_getter.clone())
.map_err(|_| hyper::Error::Header);
Box::new(verified.into_future().and_then(|_| {
println!("Succesfully verified request!");
Ok(
Response::new()
.with_header(ContentLength(PHRASE.len() as u64))
.with_body(PHRASE),
)
}))
}
With Reqwest
Cargo.toml
toml
[dependencies.http-signatures]
version = "0.1"
features = ["use_reqwest"]
In your code, use it when building a request as follows.With Rocket
Cargo.toml
toml
[dependencies.http-signatures]
version = "0.1"
features = ["use_rocket"]
In your code, use it in a route like so
```rust[derive(Clone)]
Ok(MyKeyGetter { key })
}
fn get_key(self, _key_id: &str) -> Result<Self::Key, Self::Error> {
Ok(Cursor::new(self.key.clone()))
}
fn from_request(request: &'a Request<'r>) -> Outcome<Verified, ()> {
let res = request
.guard::<State<MyKeyGetter>>()
.succeeded()
.ok_or(())
.and_then(|key_getter| {
request
.verify_authorization_header(key_getter.clone())
.map_err(|_| ..)?;
Ok(Verified)
});
match res {
Ok(verified) => Success(verified),
Err(fail) => Failure((Status::Forbidden, fail)),
}
}
[get("/")]
rocket::ignite()
.mount("/", routes![index])
.manage(key_getter)
.launch();
Testing
cargo test
. Be sure to run cargo test --all-features
.Contributing
License