I tried actually writing the code and it appears that you cannot really implement serde’s Deserialize
on Secret
: you can write something like this
extern crate serde;
extern crate serde_derive;
extern crate typenum;
use serde_derive::Serialize;
use serde_derive::Deserialize;
use serde::Deserializer;
use serde::de;
struct Secret<T, MEC: typenum::Unsigned, EC: typenum::Unsigned> {
data: T,
mec: MEC,
ec: EC,
}
#[derive(Serialize, Deserialize)]
struct SerializedSecret {
secret: String,
mec: u32,
}
pub trait DeserializeSecret {
fn deserialize_secret(encripted_secret: String) -> Self;
}
impl<'de, T, MEC, EC> serde::Deserialize<'de> for Secret<T, MEC, EC>
where T: DeserializeSecret,
MEC: typenum::Unsigned,
EC: typenum::Unsigned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let ssecret = SerializedSecret::deserialize(deserializer)?;
if ssecret.mec != MEC::to_u32() {
Err(de::Error::invalid_type(de::Unexpected::Unsigned(
ssecret.mec.into()), &"different MEC value"))?
}
Ok(Secret {
data: T::deserialize_secret(ssecret.secret),
mec: Default::default(),
ec: Default::default(),
})
}
}
, but you may see one problem here: if encrypted secret is encrypted where is deserialize_secret
going to get key from? Unless you are fine with using some kind of global (or thread-local) variable this is not going to work. You can, however, do everything you need by discarding Deserialize
implementation on Secret
, making deserialize
standalone function which accepts key: I think you should do the following here:
Agree that all secrets get serialized into some fixed format (e.g. JSON) before being encrypted and that deserialize()
accepts key as its second argument. This way new deserialize
function will do the following:
- Deserialize to
SerializedSecret
using provided deserializer.
- Check MEC.
- Decrypt string.
- Create new deserializer for the selected format.
- Deserialize decrypted string from to
T
using that deserializer.
Note that second stage deserializer will be capable of leaking secret data, so fixed format with a second stage deserializer written by you is probably the best choice (example below, using JSON deserializer in place of handwritten). Or, at least, have marker trait SecureDeserializer
and require second stage deserializer implement both Deserialize
and it.
extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate typenum;
use serde_derive::Serialize;
use serde_derive::Deserialize;
use serde::Deserializer;
use serde::de;
struct Secret<T, MEC: typenum::Unsigned, EC: typenum::Unsigned> {
data: T,
mec: MEC,
ec: EC,
}
#[derive(Serialize, Deserialize)]
struct SerializedSecret {
secret: String,
mec: u32,
}
struct Key;
fn decrypt(encrypted_data: String, key: Key) -> Option<String> {
Some(encrypted_data)
}
fn deserialize<'de, D, T, MEC, EC>(deserializer: D, key: Key)
-> Result<Secret<T, MEC, EC>, serde_json::Error>
where
D: Deserializer<'de>,
T: de::DeserializeOwned,
MEC: typenum::Unsigned,
EC: typenum::Unsigned,
{
let ssecret: SerializedSecret = de::Deserialize::deserialize(deserializer)
.map_err(|error| de::Error::custom(error))?;
if ssecret.mec != MEC::to_u32() {
Err(de::Error::invalid_type(de::Unexpected::Unsigned(
ssecret.mec.into()), &"different MEC value"))?
}
let decrypted_secret = decrypt(ssecret.secret, key)
.ok_or_else(|| <serde_json::Error as de::Error>::invalid_value(
de::Unexpected::Other("invalid string"),
&"string, which can be decrypted"))?;
let data = serde_json::from_reader(decrypted_secret.as_bytes())?;
Ok(Secret {
data,
mec: Default::default(),
ec: Default::default(),
})
}