From e2a851f68aacd74a248e925ab334c3cf9eabba18 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Fri, 24 Oct 2025 19:03:02 -0400 Subject: Move the VAPID public key encoding into a serde-compatible encoding module. The [Serde attribute docs][serde-attr] don't spell out that this will work, but experimentally, it looks like a module used with `#[serde(with)]` only needs to have the `encode`/`decode` functions if they're actually used, and can be "incomplete" if the missing ones are also unused in your code. That's the case here: we serialize VAPID keys, but never deserialize them. [serde-attr]: https://serde.rs/field-attrs.html#with This improves organization a bit in my view, but more importantly it also sets us up for a coming change where we _will_ start deserializing VAPID keys, and where I'd like to use the same logic: giving it its own module will make that easier to organize. --- src/vapid/ser.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/vapid/ser.rs (limited to 'src/vapid/ser.rs') diff --git a/src/vapid/ser.rs b/src/vapid/ser.rs new file mode 100644 index 0000000..f5372c8 --- /dev/null +++ b/src/vapid/ser.rs @@ -0,0 +1,35 @@ +pub mod key { + use base64::{Engine as _, engine::general_purpose::URL_SAFE}; + use p256::ecdsa::VerifyingKey; + use serde::Serialize as _; + + // 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) + } +} -- cgit v1.2.3