use crate::error::WebPushError;
use hyper::Uri;
use crate::message::SubscriptionInfo;
use openssl::ec::EcKey;
use openssl::pkey::Private;
use serde_json::Value;
use std::collections::BTreeMap;
use std::io::Read;
use crate::vapid::{VapidKey, VapidSignature, VapidSigner};
pub struct VapidSignatureBuilder<'a> {
claims: BTreeMap<&'a str, Value>,
key: VapidKey,
subscription_info: &'a SubscriptionInfo,
}
impl<'a> VapidSignatureBuilder<'a> {
pub fn from_pem<R: Read>(
mut pk_pem: R,
subscription_info: &'a SubscriptionInfo,
) -> Result<VapidSignatureBuilder<'a>, WebPushError> {
let mut pem_key: Vec<u8> = Vec::new();
pk_pem.read_to_end(&mut pem_key)?;
Ok(Self::from_ec(
EcKey::private_key_from_pem(&pem_key)?,
subscription_info,
))
}
pub fn from_der<R: Read>(
mut pk_der: R,
subscription_info: &'a SubscriptionInfo,
) -> Result<VapidSignatureBuilder<'a>, WebPushError> {
let mut der_key: Vec<u8> = Vec::new();
pk_der.read_to_end(&mut der_key)?;
Ok(Self::from_ec(
EcKey::private_key_from_der(&der_key)?,
subscription_info,
))
}
pub fn add_claim<V>(&mut self, key: &'a str, val: V)
where
V: Into<Value>,
{
self.claims.insert(key, val.into());
}
pub fn build(self) -> Result<VapidSignature, WebPushError> {
let endpoint: Uri = self.subscription_info.endpoint.parse()?;
let signature = VapidSigner::sign(self.key, &endpoint, self.claims)?;
Ok(signature)
}
fn from_ec(
ec_key: EcKey<Private>,
subscription_info: &'a SubscriptionInfo,
) -> VapidSignatureBuilder<'a> {
VapidSignatureBuilder {
claims: BTreeMap::new(),
key: VapidKey::new(ec_key),
subscription_info: subscription_info,
}
}
}
#[cfg(test)]
mod tests {
use crate::message::SubscriptionInfo;
use serde_json;
use std::fs::File;
use crate::vapid::VapidSignatureBuilder;
lazy_static! {
static ref PRIVATE_PEM: File = File::open("resources/vapid_test_key.pem").unwrap();
static ref PRIVATE_DER: File = File::open("resources/vapid_test_key.der").unwrap();
}
lazy_static! {
static ref SUBSCRIPTION_INFO: SubscriptionInfo =
serde_json::from_value(
json!({
"endpoint": "https://updates.push.services.mozilla.com/wpush/v2/gAAAAABaso4Vajy4STM25r5y5oFfyN451rUmES6mhQngxABxbZB5q_o75WpG25oKdrlrh9KdgWFKdYBc-buLPhvCTqR5KdsK8iCZHQume-ndtZJWKOgJbQ20GjbxHmAT1IAv8AIxTwHO-JTQ2Np2hwkKISp2_KUtpnmwFzglLP7vlCd16hTNJ2I",
"keys": {
"auth": "sBXU5_tIYz-5w7G2B25BEw",
"p256dh": "BH1HTeKM7-NwaLGHEqxeu2IamQaVVLkcsFHPIHmsCnqxcBHPQBprF41bEMOr3O1hUQ2jU1opNEm1F_lZV_sxMP8"
}
})
).unwrap();
}
#[test]
fn test_builder_from_pem() {
let builder = VapidSignatureBuilder::from_pem(&*PRIVATE_PEM, &*SUBSCRIPTION_INFO).unwrap();
let signature = builder.build().unwrap();
assert_eq!(
"BMo1HqKF6skMZYykrte9duqYwBD08mDQKTunRkJdD3sTJ9E-yyN6sJlPWTpKNhp-y2KeS6oANHF-q3w37bClb7U",
&signature.auth_k
);
assert!(!signature.auth_t.is_empty());
}
#[test]
fn test_builder_from_der() {
let builder = VapidSignatureBuilder::from_der(&*PRIVATE_DER, &*SUBSCRIPTION_INFO).unwrap();
let signature = builder.build().unwrap();
assert_eq!(
"BMo1HqKF6skMZYykrte9duqYwBD08mDQKTunRkJdD3sTJ9E-yyN6sJlPWTpKNhp-y2KeS6oANHF-q3w37bClb7U",
&signature.auth_k
);
assert!(!signature.auth_t.is_empty());
}
}