Skip to content

Commit

Permalink
Delegate jwt signing and remove many dependencies (#31)
Browse files Browse the repository at this point in the history
* Remove unused ring import.

* Refactor jwt code.
This delegates jwt handling to the jwt_simple crate, and replaces openssl uses with lighter PEM and DER decoders. This does not break the public API. Also removes now unused dependencies. Improves compile times by ~11s.

* Handle Results and improve docs.

* Install openssl on windows Github action.
This is needed to compile, as actions does not contain openssl on windows-latest.

* Change semver to 0.9 and better doc openssl.
0.9 is needed as adding an error variant is a breaking change.
  • Loading branch information
andyblarblar authored Aug 22, 2021
1 parent ddc8301 commit 4bef960
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 151 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,11 @@ jobs:
with:
toolchain: ${{matrix.rust}}

- name: Install vcpkg openssl on windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
vcpkg integrate install
vcpkg install openssl:x64-windows-static-md
- name: Run tests
run: cargo test --features=openssl/vendored
run: cargo test
23 changes: 10 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "web-push"
description = "Web push notification client with support for http-ece encryption and VAPID authentication."
version = "0.8.0"
authors = ["Julius de Bruijn <[email protected]>"]
version = "0.9.0"
authors = ["Julius de Bruijn <[email protected]>", "Andrew Ealovega <[email protected]>"]
license = "Apache-2.0"
homepage = "https://github.com/pimeys/rust-web-push"
repository = "https://github.com/pimeys/rust-web-push"
Expand All @@ -12,33 +12,30 @@ keywords = ["web-push", "http-ece", "vapid"]
categories = ["web-programming", "asynchronous"]
edition = "2018"

[badges]
travis-ci = { repository = "pimeys/rust-web-push" }

[features]
default = ["isahc"]
default = ["isahc", "futures-lite/futures-io"] #futures are only used for read_to_end() in isach client.
hyper-client = ["hyper", "hyper-tls"] #use features = ["hyper-client"], default-features = false for about 300kb size decrease.

[dependencies]
futures = "^0.3"
hyper = { version = "^0.14", features = ["client", "http1"], optional = true }
hyper-tls = { version = "^0.5", optional = true }
isahc = { version = "^1.4.0", optional = true }
futures-lite = { version = "^1.12", optional = true }
http = "^0.2"
serde = "^1.0"
serde_json = "^1.0"
serde_derive = "^1.0"
ring = "^0.16"
ece = "^2.1.0"
native-tls = "^0.2"
jwt-simple = "^0.10.4"
ece = "^2.1"
pem = "^0.8.3"
pkcs8 = { version = "^0.7.5", features = ["alloc"] }
sec1_decode = "^0.1.0"
base64 = "^0.13"
openssl = "^0.10"
time = { version = "^0.2", features = ["std"] }
lazy_static = "^1.4"
chrono = "^0.4"
log = "^0.4"

[dev-dependencies]
argparse = "^0.2"
regex = "^1.5"
tokio = { version = "^1.1", features = ["macros", "rt-multi-thread"] }
lazy_static = "^1.4"
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Web push notification sender.

## Requirements

Any async executor for use with client.
Clients require an async executor. System Openssl is needed for compilation.

## Migration to v0.8
## Migration to greater than v0.7

- The `aesgcm` variant of `ContentEncoding` has been removed. Aes128Gcm support was added in v0.8, so all uses
of `ContentEncoding::aesgcm` can simply be changed to `ContentEncoding::Aes128Gcm` with no change to functionality.
Expand All @@ -26,6 +26,8 @@ Any async executor for use with client.
the [fcm crate](https://crates.io/crates/fcm). If you just require web push, you will need to use VAPID to send
payloads. See below for info.

- A new error variant `WebPushError::InvalidClaims` has been added. This may break exhaustive matches.

## Usage

To send a web push from command line, first subscribe to receive push notifications with your browser and store the
Expand Down Expand Up @@ -111,7 +113,7 @@ these claims to the builder manually will override the default values.
Overview
--------

Currently, implements
Currently, the crate implements
[RFC8188](https://datatracker.ietf.org/doc/html/rfc8188) content encryption for notification payloads. This is done by
delegating encryption to mozilla's [ece crate](https://crates.io/crates/ece). Our security is thus tied
to [theirs](https://github.com/mozilla/rust-ece/issues/18). The default client is built
Expand All @@ -120,6 +122,16 @@ on [isahc](https://crates.io/crates/isahc), but can be swapped out with a hyper

Library tested with Google's and Mozilla's push notification services. Also verified to work on Edge.

Openssl is needed to build. Install `openssl-dev` or equivalent on *nix, or `openssl` using `vcpkg` on Windows. A nix
script is also available.

If installing on Windows, this is the exact command:

```shell
vcpkg integrate install
vcpkg install openssl:x64-windows-static-md
```

Debugging
--------
If you get an error or the push notification doesn't work you can try to debug using the following instructions:
Expand Down
2 changes: 1 addition & 1 deletion src/clients/isahc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use isahc::HttpClient;
use crate::clients::request_builder;
use crate::error::{RetryAfter, WebPushError};
use crate::message::WebPushMessage;
use futures::AsyncReadExt;
use futures_lite::AsyncReadExt;

/// An async client for sending the notification payload. This client is expensive to create, and
/// should be reused.
Expand Down
4 changes: 2 additions & 2 deletions src/clients/request_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ mod tests {
#[test]
fn builds_a_correct_request_with_empty_payload() {
//This *was* a real token
let sub = json!({"endpoint":"https://fcm.googleapis.com/fcm/send/eKClHsXFm9E:APA91bH2x3gNOMv4dF1lQfCgIfOet8EngqKCAUS5DncLOd5hzfSUxcjigIjw9ws-bqa-KmohqiTOcgepAIVO03N39dQfkEkopubML_m3fyvF03pV9_JCB7SxpUjcFmBSVhCaWS6m8l7x",
let sub = serde_json::json!({"endpoint":"https://fcm.googleapis.com/fcm/send/eKClHsXFm9E:APA91bH2x3gNOMv4dF1lQfCgIfOet8EngqKCAUS5DncLOd5hzfSUxcjigIjw9ws-bqa-KmohqiTOcgepAIVO03N39dQfkEkopubML_m3fyvF03pV9_JCB7SxpUjcFmBSVhCaWS6m8l7x",
"expirationTime":null,
"keys":{"p256dh":
"BGa4N1PI79lboMR_YrwCiCsgp35DRvedt7opHcf0yM3iOBTSoQYqQLwWxAfRKE6tsDnReWmhsImkhDF_DBdkNSU",
Expand All @@ -120,7 +120,7 @@ mod tests {
#[test]
fn builds_a_correct_request_with_payload() {
//This *was* a real token
let sub = json!({"endpoint":"https://fcm.googleapis.com/fcm/send/eKClHsXFm9E:APA91bH2x3gNOMv4dF1lQfCgIfOet8EngqKCAUS5DncLOd5hzfSUxcjigIjw9ws-bqa-KmohqiTOcgepAIVO03N39dQfkEkopubML_m3fyvF03pV9_JCB7SxpUjcFmBSVhCaWS6m8l7x",
let sub = serde_json::json!({"endpoint":"https://fcm.googleapis.com/fcm/send/eKClHsXFm9E:APA91bH2x3gNOMv4dF1lQfCgIfOet8EngqKCAUS5DncLOd5hzfSUxcjigIjw9ws-bqa-KmohqiTOcgepAIVO03N39dQfkEkopubML_m3fyvF03pV9_JCB7SxpUjcFmBSVhCaWS6m8l7x",
"expirationTime":null,
"keys":{"p256dh":
"BGa4N1PI79lboMR_YrwCiCsgp35DRvedt7opHcf0yM3iOBTSoQYqQLwWxAfRKE6tsDnReWmhsImkhDF_DBdkNSU",
Expand Down
24 changes: 4 additions & 20 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use base64::DecodeError;
use http::uri::InvalidUri;
use openssl::error::ErrorStack;
use ring::error;
use serde_json::error::Error as JsonError;
use std::string::FromUtf8Error;
use std::time::{Duration, SystemTime};
Expand Down Expand Up @@ -44,6 +42,8 @@ pub enum WebPushError {
InvalidCryptoKeys,
/// Corrupted response data
InvalidResponse,
/// A claim had invalid data
InvalidClaims,
Other(String),
}

Expand All @@ -67,12 +67,6 @@ impl From<InvalidUri> for WebPushError {
}
}

impl From<error::Unspecified> for WebPushError {
fn from(_: error::Unspecified) -> WebPushError {
WebPushError::Unspecified
}
}

#[cfg(feature = "hyper-client")]
impl From<hyper::Error> for WebPushError {
fn from(_: hyper::Error) -> Self {
Expand All @@ -87,24 +81,12 @@ impl From<isahc::Error> for WebPushError {
}
}

impl From<native_tls::Error> for WebPushError {
fn from(_: native_tls::Error) -> WebPushError {
WebPushError::TlsError
}
}

impl From<IoError> for WebPushError {
fn from(_: IoError) -> WebPushError {
WebPushError::IoError
}
}

impl From<ErrorStack> for WebPushError {
fn from(_: ErrorStack) -> WebPushError {
WebPushError::SslError
}
}

impl From<DecodeError> for WebPushError {
fn from(_: DecodeError) -> WebPushError {
WebPushError::InvalidCryptoKeys
Expand Down Expand Up @@ -132,6 +114,7 @@ impl WebPushError {
WebPushError::SslError => "ssl_error",
WebPushError::IoError => "io_error",
WebPushError::Other(_) => "other",
WebPushError::InvalidClaims => "invalidClaims",
}
}
}
Expand Down Expand Up @@ -170,6 +153,7 @@ impl fmt::Display for WebPushError {
WebPushError::MissingCryptoKeys => write!(f, "The request is missing cryptographic keys"),
WebPushError::InvalidCryptoKeys => write!(f, "The request is having invalid cryptographic keys"),
WebPushError::Other(_) => write!(f, "An unknown error when connecting the notification service"),
WebPushError::InvalidClaims => write!(f, "At least one JWT claim was invalid.")
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/http_ece.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl<'a> HttpEce<'a> {

let mut headers = Vec::new();

//VAPID uses a special Authorisation header, which contains a ecdhsa key and a jwt.
if let Some(signature) = &self.vapid_signature {
headers.push((
"Authorization",
Expand Down
4 changes: 0 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

mod clients;
Expand Down
Loading

0 comments on commit 4bef960

Please sign in to comment.