1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
use crate::errors::{ApcErrorKind, Result};
use crate::util::b64_decode_url;

use fernet::MultiFernet;
use openssl::hash;
use url::Url;
use uuid::Uuid;

/// Create an v1 or v2 WebPush endpoint from the identifiers
///
/// Both endpoints use bytes instead of hex to reduce ID length.
//  v1 is the uaid + chid
//  v2 is the uaid + chid + sha256(key).bytes
pub fn make_endpoint(
    uaid: &Uuid,
    chid: &Uuid,
    key: Option<&str>,
    endpoint_url: &str,
    fernet: &MultiFernet,
) -> Result<String> {
    let root = Url::parse(endpoint_url)?.join("wpush/")?;
    let mut base = uaid.as_bytes().to_vec();
    base.extend(chid.as_bytes());

    if let Some(k) = key {
        let raw_key = b64_decode_url(k).map_err(|e| {
            warn!("Payload: error decoding user provided VAPID key:{:?}", e);
            ApcErrorKind::PayloadError("Error decoding VAPID key".to_owned())
        })?;
        let key_digest = hash::hash(hash::MessageDigest::sha256(), &raw_key).map_err(|e| {
            warn!("Payload: Error creating digest for VAPID key: {:?}", e);
            ApcErrorKind::PayloadError("Error creating message digest for key".to_owned())
        })?;
        base.extend(key_digest.iter());
        let encrypted = fernet.encrypt(&base).trim_matches('=').to_string();
        let final_url = root.join(&format!("v2/{encrypted}")).map_err(|e| {
            ApcErrorKind::GeneralError(format!("Encrypted endpoint data is not URL-safe {:?}", e))
        })?;
        Ok(final_url.to_string())
    } else {
        let encrypted = fernet.encrypt(&base).trim_matches('=').to_string();
        let final_url = root.join(&format!("v1/{encrypted}")).map_err(|e| {
            ApcErrorKind::GeneralError(format!("Encrypted endpoint data is not URL-safe {:?}", e))
        })?;
        Ok(final_url.to_string())
    }
}