summaryrefslogtreecommitdiff
path: root/src/vapid/ser.rs
blob: 02c77e1b1986f94850d18aa8a31312327c690f9c (plain)
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
pub mod key {
    use std::fmt;

    use base64::{Engine as _, engine::general_purpose::URL_SAFE};
    use p256::ecdsa::VerifyingKey;
    use serde::{Deserializer, Serialize as _, de};

    // This serialization - to a URL-safe base-64-encoded string and back - is based on my best
    // understanding of RFC 8292 and the corresponding browser APIs. Particularly, it's based on
    // section 3.2:
    //
    // > The "k" parameter includes an ECDSA public key [FIPS186] in uncompressed form [X9.62] that
    // > is encoded using base64url encoding [RFC7515].
    //
    // <https://datatracker.ietf.org/doc/html/rfc8292#section-3.2>
    //
    // I believe this is also supported by MDN's explanation:
    //
    // > `applicationServerKey`
    // >
    // > A Base64-encoded string or ArrayBuffer containing an ECDSA P-256 public key that the push
    // > server will use to authenticate your application server. If specified, all messages from
    // > your application server must use the VAPID authentication scheme, and include a JWT signed
    // > with the corresponding private key. This key IS NOT the same ECDH key that you use to
    // > encrypt the data. For more information, see "Using VAPID with WebPush".
    //
    // <https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#applicationserverkey>

    pub fn serialize<S>(key: &VerifyingKey, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let key = key.to_sec1_bytes();
        let key = URL_SAFE.encode(key);
        key.serialize(serializer)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<VerifyingKey, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(Visitor)
    }

    struct Visitor;
    impl de::Visitor<'_> for Visitor {
        type Value = VerifyingKey;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a string containing a VAPID key")
        }

        fn visit_str<E>(self, key: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            let key = URL_SAFE.decode(key).map_err(E::custom)?;
            let key = VerifyingKey::from_sec1_bytes(&key).map_err(E::custom)?;

            Ok(key)
        }
    }
}