diff --git a/Cargo.toml b/Cargo.toml index 82d85c46..0eb85005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,19 +18,19 @@ isahc-client = ["isahc", "futures-lite/futures-io"] #futures are only used for hyper-client = ["hyper", "hyper-tls"] #use features = ["hyper-client"], default-features = false for about 300kb size decrease. [dependencies] -hyper = { version = "^0.14", features = ["client", "http1"], optional = true } -hyper-tls = { version = "^0.5", optional = true } +hyper = { version = "^1", features = ["client", "http1"], optional = true } +hyper-tls = { version = "^0.6", optional = true } isahc = { version = "^1.4.0", optional = true } -futures-lite = { version = "^1.12", optional = true } +futures-lite = { version = "^2", optional = true } http = "^0.2" serde = "^1.0" serde_json = "^1.0" serde_derive = "^1.0" -jwt-simple = "0.11.2" +jwt-simple = "0.12" ece = "^2.2" -pem = "1.1.0" +pem = "3" sec1_decode = "^0.1.0" -base64 = "^0.13" +base64 = "^0.22" chrono = "^0.4" log = "^0.4" async-trait = "^0.1" diff --git a/src/http_ece.rs b/src/http_ece.rs index 1e5717ce..fa8bb0e9 100644 --- a/src/http_ece.rs +++ b/src/http_ece.rs @@ -1,5 +1,7 @@ //! Payload encryption algorithm +use base64::prelude::BASE64_URL_SAFE_NO_PAD; +use base64::Engine; use ece::encrypt; use crate::error::WebPushError; @@ -90,7 +92,7 @@ impl<'a> HttpEce<'a> { self.add_vapid_headers(&mut headers); // ECE library base64 encodes content in aesgcm, but not aes128gcm, so decode base64 here to match the 128 API - let data = base64::decode_config(data.body(), base64::URL_SAFE_NO_PAD) + let data = BASE64_URL_SAFE_NO_PAD.decode(data.body()) .expect("ECE library should always base64 encode"); Ok(WebPushPayload { @@ -111,7 +113,7 @@ impl<'a> HttpEce<'a> { format!( "vapid t={}, k={}", signature.auth_t, - base64::encode_config(&signature.auth_k, base64::URL_SAFE_NO_PAD) + BASE64_URL_SAFE_NO_PAD.encode(&signature.auth_k) ), )); } @@ -127,7 +129,8 @@ impl<'a> HttpEce<'a> { #[cfg(test)] mod tests { - use base64::{self, URL_SAFE}; + use base64::prelude::BASE64_URL_SAFE; + use base64::Engine; use regex::Regex; use crate::error::WebPushError; @@ -137,12 +140,11 @@ mod tests { #[test] fn test_payload_too_big() { - let p256dh = base64::decode_config( + let p256dh = BASE64_URL_SAFE.decode( "BLMaF9ffKBiWQLCKvTHb6LO8Nb6dcUh6TItC455vu2kElga6PQvUmaFyCdykxY2nOSSL3yKgfbmFLRTUaGv4yV8", - URL_SAFE, - ) + ) .unwrap(); - let auth = base64::decode_config("xS03Fj5ErfTNH_l9WHE9Ig", URL_SAFE).unwrap(); + let auth = BASE64_URL_SAFE.decode("xS03Fj5ErfTNH_l9WHE9Ig").unwrap(); let http_ece = HttpEce::new(ContentEncoding::Aes128Gcm, &p256dh, &auth, None); //This content is one above limit. let content = [0u8; 3801]; @@ -191,12 +193,11 @@ mod tests { } fn setup_payload(vapid_signature: Option, encoding: ContentEncoding) -> WebPushPayload { - let p256dh = base64::decode_config( + let p256dh = BASE64_URL_SAFE.decode( "BLMbF9ffKBiWQLCKvTHb6LO8Nb6dcUh6TItC455vu2kElga6PQvUmaFyCdykxY2nOSSL3yKgfbmFLRTUaGv4yV8", - URL_SAFE, - ) + ) .unwrap(); - let auth = base64::decode_config("xS03Fi5ErfTNH_l9WHE9Ig", URL_SAFE).unwrap(); + let auth = BASE64_URL_SAFE.decode("xS03Fi5ErfTNH_l9WHE9Ig").unwrap(); let http_ece = HttpEce::new(encoding, &p256dh, &auth, vapid_signature); let content = "Hello, world!".as_bytes(); diff --git a/src/lib.rs b/src/lib.rs index cf2a6720..dbbec9d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,8 @@ pub use crate::message::{ }; 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}; +pub use base64::alphabet::{BCRYPT, BIN_HEX, CRYPT, IMAP_MUTF7, STANDARD, URL_SAFE}; +pub use base64::engine::GeneralPurposeConfig; mod clients; mod error; diff --git a/src/message.rs b/src/message.rs index 8a1ff419..f4ce8b57 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,3 +1,5 @@ +use base64::prelude::BASE64_URL_SAFE; +use base64::Engine; use http::uri::Uri; use std::fmt::{Display, Formatter}; @@ -183,8 +185,8 @@ impl<'a> WebPushMessageBuilder<'a> { .transpose()?; if let Some(payload) = self.payload { - let p256dh = base64::decode_config(&self.subscription_info.keys.p256dh, base64::URL_SAFE)?; - let auth = base64::decode_config(&self.subscription_info.keys.auth, base64::URL_SAFE)?; + let p256dh = BASE64_URL_SAFE.decode(&self.subscription_info.keys.p256dh)?; + let auth = BASE64_URL_SAFE.decode(&self.subscription_info.keys.auth)?; let http_ece = HttpEce::new(payload.encoding, &p256dh, &auth, self.vapid_signature); diff --git a/src/vapid/builder.rs b/src/vapid/builder.rs index b86eade6..932fb9e6 100644 --- a/src/vapid/builder.rs +++ b/src/vapid/builder.rs @@ -1,6 +1,9 @@ use std::collections::BTreeMap; use std::io::Read; +use base64::alphabet::Alphabet; +use base64::engine::{GeneralPurpose, GeneralPurposeConfig}; +use base64::Engine; use http::uri::Uri; use jwt_simple::prelude::*; use serde_json::Value; @@ -73,7 +76,6 @@ use crate::vapid::{VapidKey, VapidSignature, VapidSigner}; /// let signature = sig_builder.build().unwrap(); /// # } /// ``` - pub struct VapidSignatureBuilder<'a> { claims: Claims, key: VapidKey, @@ -166,11 +168,12 @@ impl<'a> VapidSignatureBuilder<'a> { /// ``` pub fn from_base64( encoded: &str, - config: base64::Config, + alphabet: &Alphabet, + config: GeneralPurposeConfig, subscription_info: &'a SubscriptionInfo, ) -> Result, WebPushError> { let pr_key = ES256KeyPair::from_bytes( - &base64::decode_config(encoded, config).map_err(|_| WebPushError::InvalidCryptoKeys)?, + &GeneralPurpose::new(alphabet, config).decode(encoded).map_err(|_| WebPushError::InvalidCryptoKeys)?, ) .map_err(|_| WebPushError::InvalidCryptoKeys)?; @@ -181,10 +184,11 @@ impl<'a> VapidSignatureBuilder<'a> { /// 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, + alphabet: &Alphabet, + config: GeneralPurposeConfig, ) -> Result { let pr_key = ES256KeyPair::from_bytes( - &base64::decode_config(encoded, config).map_err(|_| WebPushError::InvalidCryptoKeys)?, + &GeneralPurpose::new(alphabet, config).decode(encoded).map_err(|_| WebPushError::InvalidCryptoKeys)?, ) .map_err(|_| WebPushError::InvalidCryptoKeys)?; @@ -230,8 +234,8 @@ impl<'a> VapidSignatureBuilder<'a> { //Parse many PEM in the assumption of extra unneeded sections. 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"); + let found_pkcs8 = parsed.iter().any(|pem| pem.tag() == "PRIVATE KEY"); + let found_sec1 = parsed.iter().any(|pem| pem.tag() == "EC PRIVATE KEY"); //Handle each kind of PEM file differently, as EC keys can be in SEC1 or PKCS8 format. if found_sec1 { @@ -296,6 +300,10 @@ impl PartialVapidSignatureBuilder { mod tests { use std::fs::File; + use base64::alphabet::STANDARD; + use base64::engine::GeneralPurposeConfig; + use base64::prelude::BASE64_URL_SAFE_NO_PAD; + use base64::Engine; use ::lazy_static::lazy_static; use crate::message::SubscriptionInfo; @@ -328,7 +336,7 @@ mod tests { assert_eq!( "BMo1HqKF6skMZYykrte9duqYwBD08mDQKTunRkJdD3sTJ9E-yyN6sJlPWTpKNhp-y2KeS6oANHF-q3w37bClb7U", - base64::encode_config(&signature.auth_k, base64::URL_SAFE_NO_PAD) + BASE64_URL_SAFE_NO_PAD.encode(&signature.auth_k) ); assert!(!signature.auth_t.is_empty()); @@ -341,7 +349,7 @@ mod tests { assert_eq!( "BMo1HqKF6skMZYykrte9duqYwBD08mDQKTunRkJdD3sTJ9E-yyN6sJlPWTpKNhp-y2KeS6oANHF-q3w37bClb7U", - base64::encode_config(&signature.auth_k, base64::URL_SAFE_NO_PAD) + BASE64_URL_SAFE_NO_PAD.encode(&signature.auth_k) ); assert!(!signature.auth_t.is_empty()); @@ -350,12 +358,12 @@ mod tests { #[test] fn test_builder_from_base64() { let builder = - VapidSignatureBuilder::from_base64(PRIVATE_BASE64, base64::URL_SAFE_NO_PAD, &SUBSCRIPTION_INFO).unwrap(); + VapidSignatureBuilder::from_base64(PRIVATE_BASE64, &STANDARD, GeneralPurposeConfig::new().with_encode_padding(false), &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) + BASE64_URL_SAFE_NO_PAD.encode(&signature.auth_k) ); assert!(!signature.auth_t.is_empty());