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)
}
}
}
|