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]. // // // // 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". // // pub fn serialize(key: &VerifyingKey, serializer: S) -> Result 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 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(self, key: &str) -> Result 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) } } }