Skip to content

Commit

Permalink
v0.9.3 (#36)
Browse files Browse the repository at this point in the history
* Bump dependencies and remove pkcs8

* Add base64 support

* Bump to 0.9.3
  • Loading branch information
andyblarblar authored Nov 25, 2022
1 parent 3eba2dd commit 84c8937
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 16 deletions.
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "web-push"
description = "Web push notification client with support for http-ece encryption and VAPID authentication."
version = "0.9.2"
version = "0.9.3"
authors = ["Julius de Bruijn <[email protected]>", "Andrew Ealovega <[email protected]>"]
license = "Apache-2.0"
homepage = "https://github.com/pimeys/rust-web-push"
Expand All @@ -25,10 +25,9 @@ http = "^0.2"
serde = "^1.0"
serde_json = "^1.0"
serde_derive = "^1.0"
jwt-simple = "^0.10.4"
jwt-simple = "0.11.2"
ece = "^2.1"
pem = "^0.8.3"
pkcs8 = { version = "^0.7.5", features = ["alloc"] }
pem = "1.1.0"
sec1_decode = "^0.1.0"
base64 = "^0.13"
chrono = "^0.4"
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub use crate::http_ece::ContentEncoding;
pub use crate::message::{SubscriptionInfo, SubscriptionKeys, WebPushMessage, WebPushMessageBuilder, WebPushPayload};
pub use crate::vapid::builder::PartialVapidSignatureBuilder;
pub use crate::vapid::{VapidSignature, VapidSignatureBuilder};
pub use base64::{Config, BCRYPT, BINHEX, CRYPT, IMAP_MUTF7, STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD};

mod clients;
mod error;
Expand Down
91 changes: 79 additions & 12 deletions src/vapid/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,13 @@ impl<'a> VapidSignatureBuilder<'a> {
let mut der_key: Vec<u8> = Vec::new();
pk_der.read_to_end(&mut der_key)?;

let decoded = sec1_decode::parse_der(&der_key).map_err(|_| WebPushError::InvalidCryptoKeys)?;

Ok(Self::from_ec(
ES256KeyPair::from_bytes(&decoded.key).unwrap(),
ES256KeyPair::from_bytes(
&sec1_decode::parse_der(&der_key)
.map_err(|_| WebPushError::InvalidCryptoKeys)?
.key,
)
.map_err(|_| WebPushError::InvalidCryptoKeys)?,
subscription_info,
))
}
Expand All @@ -133,10 +136,60 @@ impl<'a> VapidSignatureBuilder<'a> {
let mut der_key: Vec<u8> = Vec::new();
pk_der.read_to_end(&mut der_key)?;

let decoded = sec1_decode::parse_der(&der_key).map_err(|_| WebPushError::InvalidCryptoKeys)?;
Ok(PartialVapidSignatureBuilder {
key: VapidKey::new(
ES256KeyPair::from_bytes(
&sec1_decode::parse_der(&der_key)
.map_err(|_| WebPushError::InvalidCryptoKeys)?
.key,
)
.map_err(|_| WebPushError::InvalidCryptoKeys)?,
),
})
}

/// Creates a new builder from a raw base64 encoded private key. This isn't the base64 from a key
/// generated by openssl, but rather the literal bytes of the private key itself. This is the kind
/// of key given to you by most VAPID key generator sites, and also the kind used in the API of other
/// large web push libraries, such as PHP and Node.
///
/// # Config
/// base64 has multiple encodings itself, the most common of which for web push is URL_SAFE_NO_PAD.
/// This function does support other encodings however, if needed.
///
/// # Example
///
/// ```
/// # use web_push::VapidSignatureBuilder;
/// // Use `from_base64` here if you have a sub
/// let builder = VapidSignatureBuilder::from_base64_no_sub("IQ9Ur0ykXoHS9gzfYX0aBjy9lvdrjx_PFUXmie9YRcY", base64::URL_SAFE_NO_PAD).unwrap();
/// ```
pub fn from_base64(
encoded: &str,
config: base64::Config,
subscription_info: &'a SubscriptionInfo,
) -> Result<VapidSignatureBuilder<'a>, WebPushError> {
let pr_key = ES256KeyPair::from_bytes(
&base64::decode_config(encoded, config).map_err(|_| WebPushError::InvalidCryptoKeys)?,
)
.map_err(|_| WebPushError::InvalidCryptoKeys)?;

Ok(Self::from_ec(pr_key, subscription_info))
}

/// Creates a new builder from a raw base64 encoded private key. This function doesn't take a subscription,
/// allowing the reuse of one builder for multiple messages by cloning the resulting builder.
pub fn from_base64_no_sub(
encoded: &str,
config: base64::Config,
) -> Result<PartialVapidSignatureBuilder, WebPushError> {
let pr_key = ES256KeyPair::from_bytes(
&base64::decode_config(encoded, config).map_err(|_| WebPushError::InvalidCryptoKeys)?,
)
.map_err(|_| WebPushError::InvalidCryptoKeys)?;

Ok(PartialVapidSignatureBuilder {
key: VapidKey::new(ES256KeyPair::from_bytes(&decoded.key).unwrap()),
key: VapidKey::new(pr_key),
})
}

Expand Down Expand Up @@ -173,8 +226,9 @@ impl<'a> VapidSignatureBuilder<'a> {
pub(crate) fn read_pem<R: Read>(mut input: R) -> Result<ES256KeyPair, WebPushError> {
let mut buffer = String::new();
input.read_to_string(&mut buffer).map_err(|_| WebPushError::IoError)?;

//Parse many PEM in the assumption of extra unneeded sections.
let parsed = pem::parse_many(&buffer);
let parsed = pem::parse_many(&buffer).map_err(|_| WebPushError::InvalidCryptoKeys)?;

let found_pkcs8 = parsed.iter().any(|pem| pem.tag == "PRIVATE KEY");
let found_sec1 = parsed.iter().any(|pem| pem.tag == "EC PRIVATE KEY");
Expand All @@ -184,10 +238,7 @@ impl<'a> VapidSignatureBuilder<'a> {
let key = sec1_decode::parse_pem(buffer.as_bytes()).map_err(|_| WebPushError::InvalidCryptoKeys)?;
Ok(ES256KeyPair::from_bytes(&key.key).map_err(|_| WebPushError::InvalidCryptoKeys)?)
} else if found_pkcs8 {
let key =
pkcs8::PrivateKeyDocument::from_pem(buffer.as_str()).map_err(|_| WebPushError::InvalidCryptoKeys)?;
Ok(ES256KeyPair::from_bytes(key.private_key_info().private_key)
.map_err(|_| WebPushError::InvalidCryptoKeys)?)
Ok(ES256KeyPair::from_pem(&buffer).map_err(|_| WebPushError::InvalidCryptoKeys)?)
} else {
Err(WebPushError::MissingCryptoKeys)
}
Expand Down Expand Up @@ -268,9 +319,11 @@ mod tests {
).unwrap();
}

static PRIVATE_BASE64: &str = "IQ9Ur0ykXoHS9gzfYX0aBjy9lvdrjx_PFUXmie9YRcY";

#[test]
fn test_builder_from_pem() {
let builder = VapidSignatureBuilder::from_pem(&*PRIVATE_PEM, &*SUBSCRIPTION_INFO).unwrap();
let builder = VapidSignatureBuilder::from_pem(&*PRIVATE_PEM, &SUBSCRIPTION_INFO).unwrap();
let signature = builder.build().unwrap();

assert_eq!(
Expand All @@ -283,7 +336,7 @@ mod tests {

#[test]
fn test_builder_from_der() {
let builder = VapidSignatureBuilder::from_der(&*PRIVATE_DER, &*SUBSCRIPTION_INFO).unwrap();
let builder = VapidSignatureBuilder::from_der(&*PRIVATE_DER, &SUBSCRIPTION_INFO).unwrap();
let signature = builder.build().unwrap();

assert_eq!(
Expand All @@ -293,4 +346,18 @@ mod tests {

assert!(!signature.auth_t.is_empty());
}

#[test]
fn test_builder_from_base64() {
let builder =
VapidSignatureBuilder::from_base64(PRIVATE_BASE64, base64::URL_SAFE_NO_PAD, &SUBSCRIPTION_INFO).unwrap();
let signature = builder.build().unwrap();

assert_eq!(
"BMjQIp55pdbU8pfCBKyXcZjlmER_mXt5LqNrN1hrXbdBS5EnhIbMu3Au-RV53iIpztzNXkGI56BFB1udQ8Bq_H4",
base64::encode_config(&signature.auth_k, base64::URL_SAFE_NO_PAD)
);

assert!(!signature.auth_t.is_empty());
}
}

0 comments on commit 84c8937

Please sign in to comment.