Skip to content

Migrating away from the serde_impls feature

Michael Rosenberg edited this page Jul 6, 2024 · 2 revisions

Since version 0.12, there is no more serde_impls feature in hpke. This means that serde::{Serialize, Deserialize} are no longer implemented for AeadTag, PublicKey, or PrivateKey (for any kem::{PublicKey, PrivateKey}).

If you were using these serde implementations and absolutely must maintain compatibility with the previous format, here's what you can do.

Note: if you aren't in the situation where you need to maintain compatiblity, you should consider how precisely you want to serialize fixed-length byte sequences in your format. There are a lot of options and it can make a big difference. See the discussion here.

First, import generic_array. Versions 0.14 and 1.x.x are fine.

Now suppose you have a struct like

#[derive(Serialize, Deserialize)]
struct HpkeStuff {
    public_key: HpkePublicKey,
    private_key: HpkePrivateKey,
    encapsulated_key: HpkeEncappedKey,
    // ...
}

where

type HpkePublicKey = <DhP256HkdfSha256 as Kem>::PublicKey;
type HpkePrivateKey = <DhP256HkdfSha256 as Kem>::PrivateKey;
type HpkeEncappedKey = <DhP256HkdfSha256 as Kem>::EncappedKey;

or something like that.

We will not be able to assume public_key, private_key, and encapsulated_key have serde impls anymore. So we apply a field attribute to tell serde what to do with them. Specifically, we change the struct def to

#[derive(Serialize, Deserialize)]
struct HpkeStuff {
    #[serde(with = "serde_pubkey")]
    public_key: HpkePublicKey,
    #[serde(with = "serde_privkey")]
    private_key: HpkePrivateKey,
    #[serde(with = "serde_encapped_key")]
    encapsulated_key: HpkeEncappedKey,
    // ...
}

What are serde_pubkey and friends? They're modules that we define now:

use generic_array::GenericArray;
use hpke::{
    Deserializable, Serializable,
};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use zeroize::Zeroize;

// A previous version of hpke had serde impls. For backwards compatibility, we re-implement that
// here. All this is is casting to/from GenericArray, and using GenericArray's serde impl, just as
// the original did it.

// Impl serde for $t: hpke::{Serializable, Deserializable}
macro_rules! impl_serde {
    ($modname:ident, $t:ty) => {
        pub(crate) mod $modname {
            use super::*;

            pub(crate) fn serialize<S: Serializer>(
                val: &$t,
                serializer: S,
            ) -> Result<S::Ok, S::Error> {
                let mut arr = val.to_bytes();
                let ret = arr.serialize(serializer);
                arr.zeroize();
                ret
            }

            pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
                deserializer: D,
            ) -> Result<$t, D::Error> {
                let mut arr = GenericArray::<u8, <$t as Serializable>::OutputSize>::deserialize(
                    deserializer,
                )?;
                let ret = <$t>::from_bytes(&arr).map_err(D::Error::custom);
                arr.zeroize();
                ret
            }
        }
    };
}

impl_serde!(serde_pubkey, HpkePublicKey);
impl_serde!(serde_privkey, HpkePrivateKey);
impl_serde!(serde_encapped_key, HpkeEncappedKey);

And that's it. Now everything should work exactly as before.

Clone this wiki locally