From 06066e91c0129bfd32a77dad93f05adc3d51b5f4 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 5 Jan 2023 20:25:27 -0800 Subject: [PATCH] Remove SerializableToArray/DeserializableFromArray --- umbral-pre/Cargo.toml | 5 +- umbral-pre/src/bindings_python.rs | 122 ++----------- umbral-pre/src/bindings_wasm.rs | 89 +++------ umbral-pre/src/capsule.rs | 137 ++++++-------- umbral-pre/src/capsule_frag.rs | 167 +++-------------- umbral-pre/src/curve.rs | 119 ++++++++---- umbral-pre/src/dem.rs | 3 +- umbral-pre/src/hashing.rs | 3 +- umbral-pre/src/hashing_ds.rs | 23 ++- umbral-pre/src/key_frag.rs | 191 +++++--------------- umbral-pre/src/keys.rs | 185 +++++++------------ umbral-pre/src/lib.rs | 14 +- umbral-pre/src/params.rs | 4 + umbral-pre/src/pre.rs | 22 ++- umbral-pre/src/secret_box.rs | 6 + umbral-pre/src/serde_bytes.rs | 65 +------ umbral-pre/src/traits.rs | 290 ------------------------------ 17 files changed, 373 insertions(+), 1072 deletions(-) diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index b8186ff2..94920f97 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -36,6 +36,7 @@ typenum = "1.13" # typenum is a 2018-edition crate starting from 1.13 getrandom = { version = "0.2", optional = true, default-features = false, features = ["js"] } subtle = { version = "2.4", default-features = false } zeroize = { version = "1.5", default-features = false, features = ["derive"] } +rmp-serde = { version = "0.15", optional = true } [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } @@ -45,8 +46,8 @@ rmp-serde = "0.15" [features] default = ["default-rng"] bench-internals = ["default-rng"] -bindings-python = ["pyo3", "std", "derive_more"] -bindings-wasm = ["js-sys", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] +bindings-python = ["pyo3", "std", "derive_more", "serde-support", "rmp-serde"] +bindings-wasm = ["js-sys", "serde-support", "rmp-serde", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] default-rng = ["getrandom", "rand_core/getrandom"] serde-support = ["serde"] std = [] diff --git a/umbral-pre/src/bindings_python.rs b/umbral-pre/src/bindings_python.rs index 4ce30613..a0d2da5a 100644 --- a/umbral-pre/src/bindings_python.rs +++ b/umbral-pre/src/bindings_python.rs @@ -20,43 +20,28 @@ use pyo3::pyclass::PyClass; use pyo3::types::{PyBytes, PyUnicode}; use pyo3::wrap_pyfunction; +use serde::{Deserialize, Serialize}; + use crate as umbral_pre; -use crate::{ - DeserializableFromArray, RepresentableAsArray, SerializableToArray, SerializableToSecretArray, -}; fn to_bytes(obj: &T) -> PyResult where T: AsRef, - U: SerializableToArray, -{ - let serialized = obj.as_ref().to_array(); - Python::with_gil(|py| -> PyResult { - Ok(PyBytes::new(py, serialized.as_slice()).into()) - }) -} - -// Can't keep the secret in Python anymore, so this function does the same as `to_bytes()` -fn to_secret_bytes(obj: &T) -> PyResult -where - T: AsRef, - U: SerializableToSecretArray, + U: Serialize, { - // Dereferencing a secret. - let serialized = obj.as_ref().to_secret_array().as_secret().clone(); - Python::with_gil(|py| -> PyResult { - Ok(PyBytes::new(py, serialized.as_slice()).into()) - }) + let serialized = + rmp_serde::to_vec(obj.as_ref()).map_err(|err| PyValueError::new_err(format!("{}", err)))?; + Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, &serialized).into()) }) } -fn from_bytes(data: &[u8]) -> PyResult +fn from_bytes<'de, T, U>(data: &'de [u8]) -> PyResult where T: From, - U: DeserializableFromArray, + U: Deserialize<'de>, { - U::from_bytes(data) - .map(T::from) - .map_err(|err| PyValueError::new_err(format!("{}", err))) + let backend: U = + rmp_serde::from_slice(data).map_err(|err| PyValueError::new_err(format!("{}", err)))?; + Ok(T::from(backend)) } fn type_name() -> &'static str { @@ -68,15 +53,16 @@ fn type_name() -> &'static str { fn hash(obj: &T) -> PyResult where T: AsRef, - U: SerializableToArray, + U: Serialize, { - let serialized = obj.as_ref().to_array(); + let serialized = + rmp_serde::to_vec(obj.as_ref()).map_err(|err| PyValueError::new_err(format!("{}", err)))?; // call `hash((class_name, bytes(obj)))` Python::with_gil(|py| { let builtins = PyModule::import(py, "builtins")?; let arg1 = PyUnicode::new(py, type_name::()); - let arg2: PyObject = PyBytes::new(py, serialized.as_slice()).into(); + let arg2: PyObject = PyBytes::new(py, &serialized).into(); builtins.getattr("hash")?.call1(((arg1, arg2),))?.extract() }) } @@ -119,17 +105,10 @@ impl SecretKey { } pub fn to_secret_bytes(&self) -> PyResult { - to_secret_bytes(self) - } - - #[staticmethod] - pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, umbral_pre::SecretKey>(data) - } - - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::SecretKey::serialized_size() + let serialized = self.as_ref().to_secret_bytes().as_secret().clone(); + Python::with_gil(|py| -> PyResult { + Ok(PyBytes::new(py, serialized.as_slice()).into()) + }) } fn __str__(&self) -> PyResult { @@ -170,20 +149,6 @@ impl SecretKeyFactory { self.backend.make_factory(label).into() } - pub fn to_secret_bytes(&self) -> PyResult { - to_secret_bytes(self) - } - - #[staticmethod] - pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, umbral_pre::SecretKeyFactory>(data) - } - - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::SecretKeyFactory::serialized_size() - } - fn __str__(&self) -> PyResult { Ok(format!("{}", self.backend)) } @@ -202,11 +167,6 @@ impl PublicKey { from_bytes::<_, umbral_pre::PublicKey>(data) } - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::PublicKey::serialized_size() - } - fn __bytes__(&self) -> PyResult { to_bytes(self) } @@ -267,11 +227,6 @@ impl Signature { self.backend.verify(&verifying_pk.backend, message) } - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::Signature::serialized_size() - } - fn __bytes__(&self) -> PyResult { to_bytes(self) } @@ -302,11 +257,6 @@ impl Capsule { from_bytes::<_, umbral_pre::Capsule>(data) } - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::Capsule::serialized_size() - } - fn __bytes__(&self) -> PyResult { to_bytes(self) } @@ -385,11 +335,6 @@ impl KeyFrag { from_bytes::<_, umbral_pre::KeyFrag>(data) } - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::KeyFrag::serialized_size() - } - fn __bytes__(&self) -> PyResult { to_bytes(self) } @@ -415,18 +360,6 @@ pub struct VerifiedKeyFrag { #[pymethods] impl VerifiedKeyFrag { - #[staticmethod] - pub fn from_verified_bytes(data: &[u8]) -> PyResult { - umbral_pre::VerifiedKeyFrag::from_verified_bytes(data) - .map(Self::from) - .map_err(|err| PyValueError::new_err(format!("{}", err))) - } - - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::VerifiedKeyFrag::serialized_size() - } - pub fn unverify(&self) -> KeyFrag { KeyFrag { backend: self.backend.clone().unverify(), @@ -516,11 +449,6 @@ impl CapsuleFrag { from_bytes::<_, umbral_pre::CapsuleFrag>(data) } - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::CapsuleFrag::serialized_size() - } - fn __bytes__(&self) -> PyResult { to_bytes(self) } @@ -558,18 +486,6 @@ impl VerifiedCapsuleFrag { Ok(format!("{}", self.backend)) } - #[staticmethod] - pub fn from_verified_bytes(data: &[u8]) -> PyResult { - umbral_pre::VerifiedCapsuleFrag::from_verified_bytes(data) - .map(Self::from) - .map_err(|err| PyValueError::new_err(format!("{}", err))) - } - - #[staticmethod] - pub fn serialized_size() -> usize { - umbral_pre::VerifiedCapsuleFrag::serialized_size() - } - pub fn unverify(&self) -> CapsuleFrag { CapsuleFrag { backend: self.backend.clone().unverify(), diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 89145472..2d9ea38f 100644 --- a/umbral-pre/src/bindings_wasm.rs +++ b/umbral-pre/src/bindings_wasm.rs @@ -20,7 +20,7 @@ use wasm_bindgen::JsCast; use wasm_bindgen_derive::TryFromJsValue; use crate as umbral_pre; -use crate::{DeserializableFromArray, SerializableToArray, SerializableToSecretArray}; +use crate::serde_bytes::TryFromBytes; #[wasm_bindgen] extern "C" { @@ -100,19 +100,12 @@ impl SecretKey { #[wasm_bindgen(js_name = toSecretBytes)] pub fn to_secret_bytes(&self) -> Box<[u8]> { self.0 - .to_secret_array() + .to_secret_bytes() .as_secret() .to_vec() .into_boxed_slice() } - #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::SecretKey::from_bytes(data) - .map(Self) - .map_err(map_js_err) - } - #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -152,22 +145,6 @@ impl SecretKeyFactory { Self(self.0.make_factory(label)) } - #[wasm_bindgen(js_name = toSecretBytes)] - pub fn to_secret_bytes(&self) -> Box<[u8]> { - self.0 - .to_secret_array() - .as_secret() - .to_vec() - .into_boxed_slice() - } - - #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::SecretKeyFactory::from_bytes(data) - .map(Self) - .map_err(map_js_err) - } - #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -189,7 +166,7 @@ impl PublicKey { #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::PublicKey::from_bytes(data) + umbral_pre::PublicKey::try_from_bytes(data) .map(PublicKey) .map_err(map_js_err) } @@ -243,12 +220,12 @@ impl Signature { #[wasm_bindgen(js_name = toBytes)] pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + self.0.to_der_bytes() } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::Signature::from_bytes(data) + umbral_pre::Signature::from_der_bytes(data) .map(Self) .map_err(map_js_err) } @@ -272,15 +249,13 @@ pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] impl Capsule { #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + pub fn to_bytes(&self) -> Result, Error> { + rmp_serde::to_vec(&self.0).map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::Capsule::from_bytes(data) - .map(Capsule) - .map_err(map_js_err) + rmp_serde::from_slice(data).map(Self).map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -321,15 +296,13 @@ impl CapsuleFrag { } #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + pub fn to_bytes(&self) -> Result, Error> { + rmp_serde::to_vec(&self.0).map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::CapsuleFrag::from_bytes(data) - .map(Self) - .map_err(map_js_err) + rmp_serde::from_slice(data).map(Self).map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -338,6 +311,11 @@ impl CapsuleFrag { format!("{}", self.0) } + #[wasm_bindgen(js_name = skipVerification)] + pub fn skip_verification(&self) -> VerifiedCapsuleFrag { + VerifiedCapsuleFrag(self.0.clone().skip_verification()) + } + pub fn equals(&self, other: &CapsuleFrag) -> bool { self.0 == other.0 } @@ -350,16 +328,9 @@ pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); #[wasm_bindgen] impl VerifiedCapsuleFrag { - #[wasm_bindgen(js_name = fromVerifiedBytes)] - pub fn from_verified_bytes(bytes: &[u8]) -> Result { - umbral_pre::VerifiedCapsuleFrag::from_verified_bytes(bytes) - .map(VerifiedCapsuleFrag) - .map_err(map_js_err) - } - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + pub fn to_bytes(&self) -> Result, Error> { + rmp_serde::to_vec(&self.0).map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -449,15 +420,13 @@ impl KeyFrag { } #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + pub fn to_bytes(&self) -> Result, Error> { + rmp_serde::to_vec(&self.0).map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::KeyFrag::from_bytes(data) - .map(Self) - .map_err(map_js_err) + rmp_serde::from_slice(data).map(Self).map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -466,6 +435,11 @@ impl KeyFrag { format!("{}", self.0) } + #[wasm_bindgen(js_name = skipVerification)] + pub fn skip_verification(&self) -> VerifiedKeyFrag { + VerifiedKeyFrag(self.0.clone().skip_verification()) + } + pub fn equals(&self, other: &KeyFrag) -> bool { self.0 == other.0 } @@ -478,16 +452,9 @@ pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] impl VerifiedKeyFrag { - #[wasm_bindgen(js_name = fromVerifiedBytes)] - pub fn from_verified_bytes(bytes: &[u8]) -> Result { - umbral_pre::VerifiedKeyFrag::from_verified_bytes(bytes) - .map(VerifiedKeyFrag) - .map_err(map_js_err) - } - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + pub fn to_bytes(&self) -> Result, Error> { + rmp_serde::to_vec(&self.0).map_err(map_js_err) } #[allow(clippy::inherent_to_string)] diff --git a/umbral-pre/src/capsule.rs b/umbral-pre/src/capsule.rs index fd53f6a5..4030ee6c 100644 --- a/umbral-pre/src/capsule.rs +++ b/umbral-pre/src/capsule.rs @@ -1,27 +1,23 @@ +#[cfg(feature = "serde-support")] +use alloc::string::String; + +use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt; -use generic_array::sequence::Concat; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use typenum::op; #[cfg(feature = "serde-support")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use crate::capsule_frag::CapsuleFrag; -use crate::curve::{CurvePoint, CurveScalar, NonZeroCurveScalar}; +use crate::curve::{CompressedPointSize, CurvePoint, CurveScalar, NonZeroCurveScalar}; use crate::hashing_ds::{hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret}; use crate::keys::{PublicKey, SecretKey}; use crate::params::Parameters; use crate::secret_box::SecretBox; -use crate::traits::{ - fmt_public, ConstructionError, DeserializableFromArray, RepresentableAsArray, - SerializableToArray, -}; - -#[cfg(feature = "serde-support")] -use crate::serde_bytes::{deserialize_with_encoding, serialize_as_array, Encoding}; +use crate::traits::fmt_public; /// Errors that can happen when opening a `Capsule` using reencrypted `CapsuleFrag` objects. #[derive(Debug, PartialEq, Eq)] @@ -50,8 +46,22 @@ impl fmt::Display for OpenReencryptedError { } } +/// A helper struct: +/// - allows us not to serialize `params` +/// - allows us to verify the capsule on deserialization. +#[cfg(feature = "serde-support")] +#[derive(Serialize, Deserialize)] +struct SerializedCapsule { + point_e: CurvePoint, + point_v: CurvePoint, + signature: CurveScalar, +} + /// Encapsulated symmetric key used to encrypt the plaintext. #[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-support", serde(try_from = "SerializedCapsule"))] +#[cfg_attr(feature = "serde-support", serde(into = "SerializedCapsule"))] pub struct Capsule { pub(crate) params: Parameters, pub(crate) point_e: CurvePoint, @@ -59,51 +69,24 @@ pub struct Capsule { pub(crate) signature: CurveScalar, } -type PointSize = ::Size; -type ScalarSize = ::Size; - -impl RepresentableAsArray for Capsule { - type Size = op!(PointSize + PointSize + ScalarSize); -} - -impl SerializableToArray for Capsule { - fn to_array(&self) -> GenericArray { - self.point_e - .to_array() - .concat(self.point_v.to_array()) - .concat(self.signature.to_array()) - } -} - -impl DeserializableFromArray for Capsule { - fn from_array(arr: &GenericArray) -> Result { - let (point_e, rest) = CurvePoint::take(*arr)?; - let (point_v, rest) = CurvePoint::take(rest)?; - let signature = CurveScalar::take_last(rest)?; - Self::new_verified(point_e, point_v, signature) - .ok_or_else(|| ConstructionError::new("Capsule", "Self-verification failed")) - } -} - #[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl Serialize for Capsule { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serialize_as_array(self, serializer, Encoding::Base64) +impl TryFrom for Capsule { + type Error = String; + + fn try_from(source: SerializedCapsule) -> Result { + Self::new_verified(source.point_e, source.point_v, source.signature) + .ok_or_else(|| "Capsule self-verification failed".into()) } } #[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl<'de> Deserialize<'de> for Capsule { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_with_encoding(deserializer, Encoding::Base64) +impl From for SerializedCapsule { + fn from(source: Capsule) -> Self { + Self { + point_e: source.point_e, + point_v: source.point_v, + signature: source.signature, + } } } @@ -113,7 +96,7 @@ impl fmt::Display for Capsule { } } -pub(crate) type KeySeed = GenericArray::Size>; +pub(crate) type KeySeed = GenericArray; impl Capsule { fn new(point_e: CurvePoint, point_v: CurvePoint, signature: CurveScalar) -> Self { @@ -126,6 +109,7 @@ impl Capsule { } } + #[cfg(feature = "serde-support")] pub(crate) fn new_verified( point_e: CurvePoint, point_v: CurvePoint, @@ -138,7 +122,21 @@ impl Capsule { } } + pub(crate) fn to_associated_data_bytes(&self) -> Box<[u8]> { + let e = self.point_e.to_compressed_array(); + let v = self.point_v.to_compressed_array(); + let s = self.signature.to_array(); + + let e_ref: &[u8] = e.as_ref(); + let v_ref: &[u8] = v.as_ref(); + let s_ref: &[u8] = s.as_ref(); + + let v: Vec = [e_ref, v_ref, s_ref].concat(); + v.into() + } + /// Verifies the integrity of the capsule. + #[cfg(feature = "serde-support")] fn verify(&self) -> bool { let g = CurvePoint::generator(); let h = hash_capsule_points(&self.point_e, &self.point_v); @@ -167,7 +165,10 @@ impl Capsule { let capsule = Self::new(pub_r, pub_u, s); - (capsule, SecretBox::new(shared_key.as_secret().to_array())) + ( + capsule, + SecretBox::new(shared_key.as_secret().to_compressed_array()), + ) } /// Derive the same symmetric key @@ -175,7 +176,7 @@ impl Capsule { let shared_key = SecretBox::new( &(&self.point_e + &self.point_v) * delegating_sk.to_secret_scalar().as_secret(), ); - SecretBox::new(shared_key.as_secret().to_array()) + SecretBox::new(shared_key.as_secret().to_compressed_array()) } #[allow(clippy::many_single_char_names)] @@ -231,7 +232,7 @@ impl Capsule { } let shared_key = SecretBox::new(&(&e_prime + &v_prime) * &d); - Ok(SecretBox::new(shared_key.as_secret().to_array())) + Ok(SecretBox::new(shared_key.as_secret().to_compressed_array())) } } @@ -256,29 +257,10 @@ mod tests { use super::{Capsule, OpenReencryptedError}; - use crate::{ - encrypt, generate_kfrags, reencrypt, DeserializableFromArray, SecretKey, - SerializableToArray, Signer, - }; + use crate::{generate_kfrags, reencrypt, SecretKey, Signer}; #[cfg(feature = "serde-support")] - use crate::serde_bytes::{ - tests::{check_deserialization, check_serialization}, - Encoding, - }; - - #[test] - fn test_serialize() { - let delegating_sk = SecretKey::random(); - let delegating_pk = delegating_sk.public_key(); - - let plaintext = b"peace at dawn"; - let (capsule, _ciphertext) = encrypt(&delegating_pk, plaintext).unwrap(); - - let capsule_arr = capsule.to_array(); - let capsule_back = Capsule::from_array(&capsule_arr).unwrap(); - assert_eq!(capsule, capsule_back); - } + use crate::serde_bytes::tests::check_serialization_roundtrip; #[test] fn test_open_reencrypted() { @@ -348,7 +330,6 @@ mod tests { let delegating_pk = delegating_sk.public_key(); let (capsule, _key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk); - check_serialization(&capsule, Encoding::Base64); - check_deserialization(&capsule); + check_serialization_roundtrip(&capsule); } } diff --git a/umbral-pre/src/capsule_frag.rs b/umbral-pre/src/capsule_frag.rs index 69f22384..cb8c51f1 100644 --- a/umbral-pre/src/capsule_frag.rs +++ b/umbral-pre/src/capsule_frag.rs @@ -1,12 +1,9 @@ use core::fmt; -use generic_array::sequence::Concat; -use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use typenum::op; #[cfg(feature = "serde-support")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use crate::capsule::Capsule; use crate::curve::{CurvePoint, CurveScalar, NonZeroCurveScalar}; @@ -14,15 +11,10 @@ use crate::hashing_ds::{hash_to_cfrag_verification, kfrag_signature_message}; use crate::key_frag::{KeyFrag, KeyFragID}; use crate::keys::{PublicKey, Signature}; use crate::secret_box::SecretBox; -use crate::traits::{ - fmt_public, ConstructionError, DeserializableFromArray, DeserializationError, - RepresentableAsArray, SerializableToArray, -}; - -#[cfg(feature = "serde-support")] -use crate::serde_bytes::{deserialize_with_encoding, serialize_as_array, Encoding}; +use crate::traits::fmt_public; #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub(crate) struct CapsuleFragProof { point_e2: CurvePoint, point_v2: CurvePoint, @@ -32,47 +24,6 @@ pub(crate) struct CapsuleFragProof { kfrag_signature: Signature, } -type PointSize = ::Size; -type ScalarSize = ::Size; -type SignatureSize = ::Size; -type CapsuleFragProofSize = - op!(PointSize + PointSize + PointSize + PointSize + ScalarSize + SignatureSize); - -impl RepresentableAsArray for CapsuleFragProof { - type Size = CapsuleFragProofSize; -} - -impl SerializableToArray for CapsuleFragProof { - fn to_array(&self) -> GenericArray { - self.point_e2 - .to_array() - .concat(self.point_v2.to_array()) - .concat(self.kfrag_commitment.to_array()) - .concat(self.kfrag_pok.to_array()) - .concat(self.signature.to_array()) - .concat(self.kfrag_signature.to_array()) - } -} - -impl DeserializableFromArray for CapsuleFragProof { - fn from_array(arr: &GenericArray) -> Result { - let (point_e2, rest) = CurvePoint::take(*arr)?; - let (point_v2, rest) = CurvePoint::take(rest)?; - let (kfrag_commitment, rest) = CurvePoint::take(rest)?; - let (kfrag_pok, rest) = CurvePoint::take(rest)?; - let (signature, rest) = CurveScalar::take(rest)?; - let kfrag_signature = Signature::take_last(rest)?; - Ok(Self { - point_e2, - point_v2, - kfrag_commitment, - kfrag_pok, - signature, - kfrag_signature, - }) - } -} - impl CapsuleFragProof { #[allow(clippy::many_single_char_names)] fn from_kfrag_and_cfrag( @@ -121,6 +72,7 @@ impl CapsuleFragProof { /// A reencrypted fragment of a [`Capsule`] created by a proxy. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub struct CapsuleFrag { pub(crate) point_e1: CurvePoint, pub(crate) point_v1: CurvePoint, @@ -129,60 +81,6 @@ pub struct CapsuleFrag { pub(crate) proof: CapsuleFragProof, } -impl RepresentableAsArray for CapsuleFrag { - type Size = op!(PointSize + PointSize + ScalarSize + PointSize + CapsuleFragProofSize); -} - -impl SerializableToArray for CapsuleFrag { - fn to_array(&self) -> GenericArray { - self.point_e1 - .to_array() - .concat(self.point_v1.to_array()) - .concat(self.kfrag_id.to_array()) - .concat(self.precursor.to_array()) - .concat(self.proof.to_array()) - } -} - -impl DeserializableFromArray for CapsuleFrag { - fn from_array(arr: &GenericArray) -> Result { - let (point_e1, rest) = CurvePoint::take(*arr)?; - let (point_v1, rest) = CurvePoint::take(rest)?; - let (kfrag_id, rest) = KeyFragID::take(rest)?; - let (precursor, rest) = CurvePoint::take(rest)?; - let proof = CapsuleFragProof::take_last(rest)?; - Ok(Self { - point_e1, - point_v1, - kfrag_id, - precursor, - proof, - }) - } -} - -#[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl Serialize for CapsuleFrag { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serialize_as_array(self, serializer, Encoding::Base64) - } -} - -#[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl<'de> Deserialize<'de> for CapsuleFrag { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_with_encoding(deserializer, Encoding::Base64) - } -} - impl fmt::Display for CapsuleFrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("CapsuleFrag", &self.kfrag_id, f) @@ -297,8 +195,9 @@ impl CapsuleFrag { Ok(VerifiedCapsuleFrag { cfrag: self }) } - /// Explicitly skips verification. - /// Useful in cases when the verifying keys are impossible to obtain independently. + /// Explicitly skips [`CapsuleFrag::verify`] call. + /// Useful in cases when the verifying keys are impossible to obtain independently, + /// or when this capsule frag came from a trusted storage. /// /// **Warning:** make sure you considered the implications of not enforcing verification. pub fn skip_verification(self) -> VerifiedCapsuleFrag { @@ -310,20 +209,12 @@ impl CapsuleFrag { /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`CapsuleFrag::verify`] or [`CapsuleFrag::skip_verification`]. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize))] +#[cfg_attr(feature = "serde-support", serde(transparent))] pub struct VerifiedCapsuleFrag { cfrag: CapsuleFrag, } -impl RepresentableAsArray for VerifiedCapsuleFrag { - type Size = ::Size; -} - -impl SerializableToArray for VerifiedCapsuleFrag { - fn to_array(&self) -> GenericArray { - self.cfrag.to_array() - } -} - impl fmt::Display for VerifiedCapsuleFrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("VerifiedCapsuleFrag", &self.cfrag.kfrag_id, f) @@ -341,15 +232,6 @@ impl VerifiedCapsuleFrag { } } - /// Restores a verified capsule frag directly from serialized bytes, - /// skipping [`CapsuleFrag::verify`] call. - /// - /// Intended for internal storage; - /// make sure that the bytes come from a trusted source. - pub fn from_verified_bytes(data: impl AsRef<[u8]>) -> Result { - CapsuleFrag::from_bytes(data).map(|cfrag| Self { cfrag }) - } - /// Clears the verification status from the capsule frag. /// Useful for the cases where it needs to be put in the protocol structure /// containing [`CapsuleFrag`] types (since those are the ones @@ -365,18 +247,12 @@ mod tests { use alloc::boxed::Box; use alloc::vec::Vec; - use super::{CapsuleFrag, VerifiedCapsuleFrag}; + use super::VerifiedCapsuleFrag; - use crate::{ - encrypt, generate_kfrags, reencrypt, Capsule, DeserializableFromArray, PublicKey, - SecretKey, SerializableToArray, Signer, - }; + use crate::{encrypt, generate_kfrags, reencrypt, Capsule, PublicKey, SecretKey, Signer}; #[cfg(feature = "serde-support")] - use crate::serde_bytes::{ - tests::{check_deserialization, check_serialization}, - Encoding, - }; + use crate::serde_bytes::tests::check_serialization_roundtrip; fn prepare_cfrags() -> ( PublicKey, @@ -419,12 +295,8 @@ mod tests { prepare_cfrags(); for verified_cfrag in verified_cfrags.iter() { - let cfrag_array = verified_cfrag.to_array(); - let cfrag_back = CapsuleFrag::from_array(&cfrag_array).unwrap(); - - assert_eq!(cfrag_back.to_array(), cfrag_array); - - let verified_cfrag_back = cfrag_back + let cfrag = verified_cfrag.clone().unverify(); + let verified_cfrag_back = cfrag .verify(&capsule, &verifying_pk, &delegating_pk, &receiving_pk) .unwrap(); @@ -438,10 +310,13 @@ mod tests { let (_delegating_pk, _receiving_pk, _verifying_pk, _capsule, verified_cfrags) = prepare_cfrags(); - let vcfrag = verified_cfrags[0].clone(); - let cfrag = CapsuleFrag::from_array(&vcfrag.to_array()).unwrap(); + let cfrag = verified_cfrags[0].clone().unverify(); + + // Check that the cfrag serializes to the same thing as the verified cfrag + let cfrag_bytes = rmp_serde::to_vec(&cfrag).unwrap(); + let vcfrag_bytes = rmp_serde::to_vec(&verified_cfrags[0]).unwrap(); + assert_eq!(vcfrag_bytes, cfrag_bytes); - check_serialization(&cfrag, Encoding::Base64); - check_deserialization(&cfrag); + check_serialization_roundtrip(&cfrag); } } diff --git a/umbral-pre/src/curve.rs b/umbral-pre/src/curve.rs index ae1c903e..212b3255 100644 --- a/umbral-pre/src/curve.rs +++ b/umbral-pre/src/curve.rs @@ -2,12 +2,14 @@ //! `elliptic_curves` has a somewhat unstable API, //! and we isolate all the related logic here. +#[cfg(feature = "serde-support")] +use alloc::string::String; + use core::default::Default; use core::ops::{Add, Mul, Sub}; use digest::Digest; use elliptic_curve::bigint::U256; // Note that this type is different from typenum::U256 -use elliptic_curve::group::ff::PrimeField; use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; use elliptic_curve::ops::Reduce; use elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint}; @@ -19,14 +21,22 @@ use sha2::Sha256; use subtle::CtOption; use zeroize::{DefaultIsZeroes, Zeroize}; -use crate::traits::{ - ConstructionError, DeserializableFromArray, RepresentableAsArray, SerializableToArray, +#[cfg(feature = "serde-support")] +use elliptic_curve::group::ff::PrimeField; + +#[cfg(feature = "serde-support")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(feature = "serde-support")] +use crate::serde_bytes::{ + deserialize_with_encoding, serialize_with_encoding, Encoding, TryFromBytes, }; pub(crate) type CurveType = Secp256k1; -type CompressedPointSize = as ModulusSize>::CompressedPointSize; +pub(crate) type CompressedPointSize = as ModulusSize>::CompressedPointSize; type BackendScalar = Scalar; +pub(crate) type ScalarSize = FieldSize; pub(crate) type BackendNonZeroScalar = NonZeroScalar; // We have to define newtypes for scalar and point here because the compiler @@ -48,32 +58,50 @@ impl CurveScalar { pub(crate) fn one() -> Self { Self(BackendScalar::one()) } -} -impl DefaultIsZeroes for CurveScalar {} + pub(crate) fn to_array(&self) -> GenericArray { + self.0.to_bytes() + } +} -impl RepresentableAsArray for CurveScalar { - // Currently it's the only size available. - // A separate scalar size may appear in later versions of `elliptic_curve`. - type Size = FieldSize; +#[cfg(feature = "serde-support")] +impl Serialize for CurveScalar { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serialize_with_encoding(&self.0.to_bytes(), serializer, Encoding::Hex) + } } -impl SerializableToArray for CurveScalar { - fn to_array(&self) -> GenericArray { - self.0.to_bytes() +#[cfg(feature = "serde-support")] +impl<'de> Deserialize<'de> for CurveScalar { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserialize_with_encoding(deserializer, Encoding::Hex) } } -impl DeserializableFromArray for CurveScalar { - fn from_array(arr: &GenericArray) -> Result { +#[cfg(feature = "serde-support")] +impl TryFromBytes for CurveScalar { + type Error = String; + + fn try_from_bytes(bytes: &[u8]) -> Result { + let arr = GenericArray::::from_exact_iter(bytes.iter().cloned()) + .ok_or_else(|| "Invalid length of a curve scalar")?; + // unwrap CtOption into Option - let maybe_scalar: Option = BackendScalar::from_repr(*arr).into(); + let maybe_scalar: Option = BackendScalar::from_repr(arr).into(); maybe_scalar .map(Self) - .ok_or_else(|| ConstructionError::new("CurveScalar", "Internal backend error")) + .ok_or_else(|| "Invalid curve scalar representation".into()) } } +impl DefaultIsZeroes for CurveScalar {} + #[derive(Clone, Zeroize)] pub(crate) struct NonZeroCurveScalar(BackendNonZeroScalar); @@ -101,9 +129,7 @@ impl NonZeroCurveScalar { Self(BackendNonZeroScalar::new(inv).unwrap()) } - pub(crate) fn from_digest( - d: impl Digest::Size>, - ) -> Self { + pub(crate) fn from_digest(d: impl Digest) -> Self { // There's currently no way to make the required digest output size // depend on the target scalar size, so we are hardcoding it to 256 bit // (that is, equal to the scalar size). @@ -127,7 +153,7 @@ type BackendPoint = ::ProjectivePoint; type BackendPointAffine = AffinePoint; #[derive(Clone, Copy, Debug, PartialEq)] -pub struct CurvePoint(BackendPoint); +pub(crate) struct CurvePoint(BackendPoint); impl CurvePoint { pub(crate) fn from_backend_point(point: &BackendPoint) -> Self { @@ -155,7 +181,7 @@ impl CurvePoint { cp_opt.map(Self) } - fn to_compressed_array(self) -> GenericArray { + pub(crate) fn to_compressed_array(self) -> GenericArray { *GenericArray::::from_slice( self.0.to_affine().to_encoded_point(true).as_bytes(), ) @@ -177,6 +203,38 @@ impl Default for CurvePoint { } } +#[cfg(feature = "serde-support")] +impl Serialize for CurvePoint { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serialize_with_encoding(&self.to_compressed_array(), serializer, Encoding::Hex) + } +} + +#[cfg(feature = "serde-support")] +impl<'de> Deserialize<'de> for CurvePoint { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserialize_with_encoding(deserializer, Encoding::Hex) + } +} + +#[cfg(feature = "serde-support")] +impl TryFromBytes for CurvePoint { + type Error = String; + + fn try_from_bytes(bytes: &[u8]) -> Result { + let arr = GenericArray::::from_exact_iter(bytes.iter().cloned()) + .ok_or_else(|| "Invalid length of a curve point")?; + + Self::from_compressed_array(&arr).ok_or_else(|| "Invalid curve point representation".into()) + } +} + impl DefaultIsZeroes for CurvePoint {} impl Add<&CurveScalar> for &CurveScalar { @@ -266,20 +324,3 @@ impl Mul<&NonZeroCurveScalar> for &NonZeroCurveScalar { NonZeroCurveScalar(self.0.mul(other.0)) } } - -impl RepresentableAsArray for CurvePoint { - type Size = CompressedPointSize; -} - -impl SerializableToArray for CurvePoint { - fn to_array(&self) -> GenericArray { - self.to_compressed_array() - } -} - -impl DeserializableFromArray for CurvePoint { - fn from_array(arr: &GenericArray) -> Result { - Self::from_compressed_array(arr) - .ok_or_else(|| ConstructionError::new("CurvePoint", "Internal backend error")) - } -} diff --git a/umbral-pre/src/dem.rs b/umbral-pre/src/dem.rs index d300323e..9bc944a3 100644 --- a/umbral-pre/src/dem.rs +++ b/umbral-pre/src/dem.rs @@ -148,14 +148,13 @@ mod tests { use super::kdf; use crate::curve::CurvePoint; use crate::secret_box::SecretBox; - use crate::SerializableToArray; #[test] fn test_kdf() { let p1 = CurvePoint::generator(); let salt = b"abcdefg"; let info = b"sdasdasd"; - let seed = SecretBox::new(p1.to_array()); + let seed = SecretBox::new(p1.to_compressed_array()); let key = kdf::(seed.as_secret(), Some(&salt[..]), Some(&info[..])); let key_same = kdf::(seed.as_secret(), Some(&salt[..]), Some(&info[..])); assert_eq!(key.as_secret(), key_same.as_secret()); diff --git a/umbral-pre/src/hashing.rs b/umbral-pre/src/hashing.rs index d8a3b72c..835bffea 100644 --- a/umbral-pre/src/hashing.rs +++ b/umbral-pre/src/hashing.rs @@ -4,7 +4,6 @@ use zeroize::Zeroize; use crate::curve::{CurvePoint, NonZeroCurveScalar}; use crate::secret_box::SecretBox; -use crate::traits::SerializableToArray; // Our hash of choice. pub(crate) type BackendDigest = Sha256; @@ -58,7 +57,7 @@ impl ScalarDigest { } pub fn chain_point(self, point: &CurvePoint) -> Self { - self.chain_bytes(point.to_array()) + self.chain_bytes(point.to_compressed_array()) } pub fn chain_points(self, points: &[CurvePoint]) -> Self { diff --git a/umbral-pre/src/hashing_ds.rs b/umbral-pre/src/hashing_ds.rs index 7002fee2..89a974a5 100644 --- a/umbral-pre/src/hashing_ds.rs +++ b/umbral-pre/src/hashing_ds.rs @@ -8,7 +8,6 @@ use crate::curve::{CurvePoint, NonZeroCurveScalar}; use crate::hashing::ScalarDigest; use crate::key_frag::KeyFragID; use crate::keys::PublicKey; -use crate::traits::SerializableToArray; pub(crate) fn hash_to_polynomial_arg( precursor: &CurvePoint, @@ -52,6 +51,14 @@ pub(crate) fn hash_to_cfrag_verification(points: &[CurvePoint]) -> NonZeroCurveS .finalize() } +fn bool_to_array(val: bool) -> [u8; 1] { + if val { + [1u8] + } else { + [0u8] + } +} + pub(crate) fn kfrag_signature_message( kfrag_id: &KeyFragID, commitment: &CurvePoint, @@ -61,24 +68,24 @@ pub(crate) fn kfrag_signature_message( ) -> Box<[u8]> { let mut result = Vec::::new(); - result.extend_from_slice(&kfrag_id.to_array()); - result.extend_from_slice(&commitment.to_array()); - result.extend_from_slice(&precursor.to_array()); + result.extend_from_slice(kfrag_id.as_ref()); + result.extend_from_slice(&commitment.to_compressed_array()); + result.extend_from_slice(&precursor.to_compressed_array()); match maybe_delegating_pk { Some(delegating_pk) => { - result.extend_from_slice(&true.to_array()); + result.extend_from_slice(&bool_to_array(true)); result.extend_from_slice(&delegating_pk.to_array()) } - None => result.extend_from_slice(&false.to_array()), + None => result.extend_from_slice(&bool_to_array(false)), }; match maybe_receiving_pk { Some(receiving_pk) => { - result.extend_from_slice(&true.to_array()); + result.extend_from_slice(&bool_to_array(true)); result.extend_from_slice(&receiving_pk.to_array()) } - None => result.extend_from_slice(&false.to_array()), + None => result.extend_from_slice(&bool_to_array(false)), }; result.into_boxed_slice() diff --git a/umbral-pre/src/key_frag.rs b/umbral-pre/src/key_frag.rs index 5a14cefd..48055834 100644 --- a/umbral-pre/src/key_frag.rs +++ b/umbral-pre/src/key_frag.rs @@ -1,11 +1,13 @@ +#[cfg(feature = "serde-support")] +use alloc::string::String; + use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt; -use generic_array::sequence::Concat; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use typenum::{op, U32}; +use typenum::U32; #[cfg(feature = "serde-support")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -15,13 +17,12 @@ use crate::hashing_ds::{hash_to_polynomial_arg, hash_to_shared_secret, kfrag_sig use crate::keys::{PublicKey, SecretKey, Signature, Signer}; use crate::params::Parameters; use crate::secret_box::SecretBox; -use crate::traits::{ - fmt_public, ConstructionError, DeserializableFromArray, DeserializationError, - RepresentableAsArray, SerializableToArray, -}; +use crate::traits::fmt_public; #[cfg(feature = "serde-support")] -use crate::serde_bytes::{deserialize_with_encoding, serialize_as_array, Encoding}; +use crate::serde_bytes::{ + deserialize_with_encoding, serialize_with_encoding, Encoding, TryFromBytes, +}; #[allow(clippy::upper_case_acronyms)] type KeyFragIDSize = U32; @@ -44,23 +45,39 @@ impl AsRef<[u8]> for KeyFragID { } } -impl RepresentableAsArray for KeyFragID { - type Size = KeyFragIDSize; +#[cfg(feature = "serde-support")] +impl Serialize for KeyFragID { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serialize_with_encoding(&self.0, serializer, Encoding::Hex) + } } -impl SerializableToArray for KeyFragID { - fn to_array(&self) -> GenericArray { - self.0 +#[cfg(feature = "serde-support")] +impl<'de> Deserialize<'de> for KeyFragID { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserialize_with_encoding(deserializer, Encoding::Hex) } } -impl DeserializableFromArray for KeyFragID { - fn from_array(arr: &GenericArray) -> Result { - Ok(Self(*arr)) +#[cfg(feature = "serde-support")] +impl TryFromBytes for KeyFragID { + type Error = String; + + fn try_from_bytes(bytes: &[u8]) -> Result { + let arr = GenericArray::::from_exact_iter(bytes.iter().cloned()) + .ok_or_else(|| "Invalid length of a key frag ID")?; + Ok(Self(arr)) } } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub(crate) struct KeyFragProof { pub(crate) commitment: CurvePoint, signature_for_proxy: Signature, @@ -69,44 +86,6 @@ pub(crate) struct KeyFragProof { receiving_key_signed: bool, } -type SignatureSize = ::Size; -type ScalarSize = ::Size; -type PointSize = ::Size; -type BoolSize = ::Size; -type KeyFragProofSize = op!(PointSize + SignatureSize + SignatureSize + BoolSize + BoolSize); - -impl RepresentableAsArray for KeyFragProof { - type Size = KeyFragProofSize; -} - -impl SerializableToArray for KeyFragProof { - fn to_array(&self) -> GenericArray { - self.commitment - .to_array() - .concat(self.signature_for_proxy.to_array()) - .concat(self.signature_for_receiver.to_array()) - .concat(self.delegating_key_signed.to_array()) - .concat(self.receiving_key_signed.to_array()) - } -} - -impl DeserializableFromArray for KeyFragProof { - fn from_array(arr: &GenericArray) -> Result { - let (commitment, rest) = CurvePoint::take(*arr)?; - let (signature_for_proxy, rest) = Signature::take(rest)?; - let (signature_for_receiver, rest) = Signature::take(rest)?; - let (delegating_key_signed, rest) = bool::take(rest)?; - let receiving_key_signed = bool::take_last(rest)?; - Ok(Self { - commitment, - signature_for_proxy, - signature_for_receiver, - delegating_key_signed, - receiving_key_signed, - }) - } -} - fn none_unless(x: Option, predicate: bool) -> Option { if predicate { x @@ -165,6 +144,7 @@ impl KeyFragProof { /// A fragment of the encrypting party's key used to create a [`CapsuleFrag`](`crate::CapsuleFrag`). #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub struct KeyFrag { params: Parameters, pub(crate) id: KeyFragID, @@ -173,59 +153,6 @@ pub struct KeyFrag { pub(crate) proof: KeyFragProof, } -impl RepresentableAsArray for KeyFrag { - type Size = op!(ScalarSize + ScalarSize + PointSize + KeyFragProofSize); -} - -impl SerializableToArray for KeyFrag { - fn to_array(&self) -> GenericArray { - self.id - .to_array() - .concat(self.key.to_array()) - .concat(self.precursor.to_array()) - .concat(self.proof.to_array()) - } -} - -impl DeserializableFromArray for KeyFrag { - fn from_array(arr: &GenericArray) -> Result { - let params = Parameters::new(); - let (id, rest) = KeyFragID::take(*arr)?; - let (key, rest) = CurveScalar::take(rest)?; - let (precursor, rest) = CurvePoint::take(rest)?; - let proof = KeyFragProof::take_last(rest)?; - Ok(Self { - params, - id, - key, - precursor, - proof, - }) - } -} - -#[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl Serialize for KeyFrag { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serialize_as_array(self, serializer, Encoding::Base64) - } -} - -#[cfg(feature = "serde-support")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde-support")))] -impl<'de> Deserialize<'de> for KeyFrag { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_with_encoding(deserializer, Encoding::Base64) - } -} - impl fmt::Display for KeyFrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("KeyFrag", &self.id, f) @@ -354,8 +281,9 @@ impl KeyFrag { Ok(VerifiedKeyFrag { kfrag: self }) } - /// Explicitly skips verification. - /// Useful in cases when the verifying keys are impossible to obtain independently. + /// Explicitly skips [`KeyFrag::verify`] call. + /// Useful in cases when the verifying keys are impossible to obtain independently, + /// or when this capsule frag came from a trusted storage. /// /// **Warning:** make sure you considered the implications of not enforcing verification. pub fn skip_verification(self) -> VerifiedKeyFrag { @@ -367,20 +295,12 @@ impl KeyFrag { /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`KeyFrag::verify`] or [`KeyFrag::skip_verification`]. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize))] +#[cfg_attr(feature = "serde-support", serde(transparent))] pub struct VerifiedKeyFrag { kfrag: KeyFrag, } -impl RepresentableAsArray for VerifiedKeyFrag { - type Size = ::Size; -} - -impl SerializableToArray for VerifiedKeyFrag { - fn to_array(&self) -> GenericArray { - self.kfrag.to_array() - } -} - impl fmt::Display for VerifiedKeyFrag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("VerifiedKeyFrag", &self.kfrag.id, f) @@ -399,15 +319,6 @@ impl VerifiedKeyFrag { } } - /// Restores a verified keyfrag directly from serialized bytes, - /// skipping [`KeyFrag::verify`] call. - /// - /// Intended for internal storage; - /// make sure that the bytes come from a trusted source. - pub fn from_verified_bytes(data: impl AsRef<[u8]>) -> Result { - KeyFrag::from_bytes(data).map(|kfrag| Self { kfrag }) - } - /// Clears the verification status from the keyfrag. /// Useful for the cases where it needs to be put in the protocol structure /// containing [`KeyFrag`] types (since those are the ones @@ -494,15 +405,12 @@ mod tests { use rand_core::OsRng; - use super::{KeyFrag, KeyFragBase, KeyFragVerificationError, VerifiedKeyFrag}; + use super::{KeyFragBase, KeyFragVerificationError, VerifiedKeyFrag}; - use crate::{DeserializableFromArray, PublicKey, SecretKey, SerializableToArray, Signer}; + use crate::{PublicKey, SecretKey, Signer}; #[cfg(feature = "serde-support")] - use crate::serde_bytes::{ - tests::{check_deserialization, check_serialization}, - Encoding, - }; + use crate::serde_bytes::tests::check_serialization_roundtrip; fn prepare_kfrags( sign_delegating_key: bool, @@ -534,11 +442,7 @@ mod tests { let (delegating_pk, receiving_pk, verifying_pk, vkfrags) = prepare_kfrags(sign_dk, sign_rk); - let kfrag_arr = vkfrags[0].to_array(); - let kfrag = KeyFrag::from_array(&kfrag_arr).unwrap(); - - // Check that the kfrag serializes to the same thing as the verified kfrag - assert_eq!(kfrag.to_array(), kfrag_arr); + let kfrag = vkfrags[0].clone().unverify(); for supply_dk in [false, true].iter().copied() { for supply_rk in [false, true].iter().copied() { @@ -585,10 +489,13 @@ mod tests { let (_delegating_pk, _receiving_pk, _verifying_pk, verified_kfrags) = prepare_kfrags(true, true); - let vkfrag = verified_kfrags[0].clone(); - let kfrag = KeyFrag::from_array(&vkfrag.to_array()).unwrap(); + let kfrag = verified_kfrags[0].clone().unverify(); + + // Check that the kfrag serializes to the same thing as the verified kfrag + let kfrag_bytes = rmp_serde::to_vec(&kfrag).unwrap(); + let vkfrag_bytes = rmp_serde::to_vec(&verified_kfrags[0]).unwrap(); + assert_eq!(vkfrag_bytes, kfrag_bytes); - check_serialization(&kfrag, Encoding::Base64); - check_deserialization(&kfrag); + check_serialization_roundtrip(&kfrag); } } diff --git a/umbral-pre/src/keys.rs b/umbral-pre/src/keys.rs index 4c7ace69..db422272 100644 --- a/umbral-pre/src/keys.rs +++ b/umbral-pre/src/keys.rs @@ -1,13 +1,18 @@ +#[cfg(feature = "serde-support")] +use alloc::string::String; + +use alloc::boxed::Box; +use alloc::format; use alloc::vec::Vec; use core::cmp::Ordering; use core::fmt; use digest::Digest; -use ecdsa::{Signature as BackendSignature, SignatureSize, SigningKey, VerifyingKey}; +use ecdsa::{Signature as BackendSignature, SigningKey, VerifyingKey}; use elliptic_curve::{PublicKey as BackendPublicKey, SecretKey as BackendSecretKey}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use signature::{DigestVerifier, RandomizedDigestSigner, Signature as SignatureTrait}; +use signature::{DigestVerifier, RandomizedDigestSigner}; use typenum::{Unsigned, U32, U64}; use zeroize::ZeroizeOnDrop; @@ -17,39 +22,32 @@ use rand_core::OsRng; #[cfg(feature = "serde-support")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::curve::{CurvePoint, CurveScalar, CurveType, NonZeroCurveScalar}; +use crate::curve::{CompressedPointSize, CurvePoint, CurveType, NonZeroCurveScalar, ScalarSize}; use crate::dem::kdf; use crate::hashing::{BackendDigest, Hash, ScalarDigest}; use crate::secret_box::SecretBox; -use crate::traits::{ - fmt_public, fmt_secret, ConstructionError, DeserializableFromArray, RepresentableAsArray, - SerializableToArray, SerializableToSecretArray, SizeMismatchError, -}; +use crate::traits::{fmt_public, fmt_secret, SizeMismatchError}; #[cfg(feature = "serde-support")] -use crate::serde_bytes::{deserialize_with_encoding, serialize_as_array, Encoding}; +use crate::serde_bytes::{ + deserialize_with_encoding, serialize_with_encoding, Encoding, TryFromBytes, +}; /// ECDSA signature object. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Signature(BackendSignature); -impl RepresentableAsArray for Signature { - type Size = SignatureSize; -} - -impl SerializableToArray for Signature { - fn to_array(&self) -> GenericArray { - *GenericArray::::from_slice(self.0.as_bytes()) +impl Signature { + pub(crate) fn to_der_bytes(&self) -> Box<[u8]> { + self.0.to_der().as_bytes().into() } -} -impl DeserializableFromArray for Signature { - fn from_array(arr: &GenericArray) -> Result { + pub(crate) fn from_der_bytes(bytes: &[u8]) -> Result { // Note that it will not normalize `s` automatically, // and if it is not normalized, verification will fail. - BackendSignature::::from_bytes(arr.as_slice()) + BackendSignature::::from_der(bytes) .map(Self) - .map_err(|_| ConstructionError::new("Signature", "Internal backend error")) + .map_err(|err| format!("Internal backend error: {}", err)) } } @@ -60,7 +58,7 @@ impl Serialize for Signature { where S: Serializer, { - serialize_as_array(self, serializer, Encoding::Base64) + serialize_with_encoding(&self.to_der_bytes(), serializer, Encoding::Hex) } } @@ -71,7 +69,16 @@ impl<'de> Deserialize<'de> for Signature { where D: Deserializer<'de>, { - deserialize_with_encoding(deserializer, Encoding::Base64) + deserialize_with_encoding(deserializer, Encoding::Hex) + } +} + +#[cfg(feature = "serde-support")] +impl TryFromBytes for Signature { + type Error = String; + + fn try_from_bytes(bytes: &[u8]) -> Result { + Self::from_der_bytes(bytes) } } @@ -85,12 +92,12 @@ impl Signature { impl fmt::Display for Signature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_public("Signature", &self.to_array(), f) + fmt_public("Signature", &self.to_der_bytes(), f) } } /// A secret key. -#[derive(Clone, ZeroizeOnDrop)] +#[derive(Clone, ZeroizeOnDrop, PartialEq, Eq)] pub struct SecretKey(BackendSecretKey); impl SecretKey { @@ -103,6 +110,10 @@ impl SecretKey { Self::new(BackendSecretKey::::random(rng)) } + pub fn to_secret_bytes(&self) -> SecretBox> { + SecretBox::new(self.0.to_be_bytes().into()) + } + /// Creates a secret key using the default RNG. #[cfg(feature = "default-rng")] #[cfg_attr(docsrs, doc(cfg(feature = "default-rng")))] @@ -129,24 +140,6 @@ impl SecretKey { } } -impl RepresentableAsArray for SecretKey { - type Size = ::Size; -} - -impl SerializableToSecretArray for SecretKey { - fn to_secret_array(&self) -> SecretBox> { - SecretBox::new(self.0.to_be_bytes()) - } -} - -impl DeserializableFromArray for SecretKey { - fn from_array(arr: &GenericArray) -> Result { - BackendSecretKey::::from_be_bytes(arr.as_slice()) - .map(Self::new) - .map_err(|_| ConstructionError::new("SecretKey", "Internal backend error")) - } -} - impl fmt::Display for SecretKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_secret("SecretKey", f) @@ -214,24 +207,9 @@ impl PublicKey { let verifier = VerifyingKey::from(&self.0); verifier.verify_digest(digest, &signature.0).is_ok() } -} -impl RepresentableAsArray for PublicKey { - type Size = ::Size; -} - -impl SerializableToArray for PublicKey { - fn to_array(&self) -> GenericArray { - self.to_point().to_array() - } -} - -impl DeserializableFromArray for PublicKey { - fn from_array(arr: &GenericArray) -> Result { - let cp = CurvePoint::from_array(arr)?; - BackendPublicKey::::from_affine(cp.to_affine_point()) - .map(Self) - .map_err(|_| ConstructionError::new("PublicKey", "Internal backend error")) + pub(crate) fn to_array(&self) -> GenericArray { + self.to_point().to_compressed_array() } } @@ -242,7 +220,11 @@ impl Serialize for PublicKey { where S: Serializer, { - serialize_as_array(self, serializer, Encoding::Hex) + serialize_with_encoding( + &self.to_point().to_compressed_array(), + serializer, + Encoding::Hex, + ) } } @@ -257,6 +239,18 @@ impl<'de> Deserialize<'de> for PublicKey { } } +#[cfg(feature = "serde-support")] +impl TryFromBytes for PublicKey { + type Error = String; + + fn try_from_bytes(bytes: &[u8]) -> Result { + let cp = CurvePoint::try_from_bytes(bytes)?; + BackendPublicKey::::from_affine(cp.to_affine_point()) + .map(Self) + .map_err(|_| "Cannot instantiate a public key from the given curve point".into()) + } +} + impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("PublicKey", &self.to_array(), f) @@ -269,7 +263,7 @@ type SecretKeyFactorySeed = GenericArray; /// This class handles keyring material for Umbral, by allowing deterministic /// derivation of `SecretKey` objects based on labels. -#[derive(Clone)] +#[derive(Clone, ZeroizeOnDrop, PartialEq)] pub struct SecretKeyFactory(SecretBox); impl SecretKeyFactory { @@ -340,22 +334,6 @@ impl SecretKeyFactory { } } -impl RepresentableAsArray for SecretKeyFactory { - type Size = SecretKeyFactorySeedSize; -} - -impl SerializableToSecretArray for SecretKeyFactory { - fn to_secret_array(&self) -> SecretBox> { - self.0.clone() - } -} - -impl DeserializableFromArray for SecretKeyFactory { - fn from_array(arr: &GenericArray) -> Result { - Ok(Self(SecretBox::new(*arr))) - } -} - impl fmt::Display for SecretKeyFactory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_secret("SecretKeyFactory", f) @@ -365,30 +343,10 @@ impl fmt::Display for SecretKeyFactory { #[cfg(test)] mod tests { - use super::{PublicKey, SecretKey, SecretKeyFactory, Signer}; - use crate::{DeserializableFromArray, SerializableToArray, SerializableToSecretArray}; + use super::{SecretKey, SecretKeyFactory, Signer}; #[cfg(feature = "serde-support")] - use crate::serde_bytes::{ - tests::{check_deserialization, check_serialization}, - Encoding, - }; - - #[test] - fn test_serialize_secret_key() { - let sk = SecretKey::random(); - let sk_arr = sk.to_secret_array(); - let sk_back = SecretKey::from_array(sk_arr.as_secret()).unwrap(); - assert!(sk.to_secret_array().as_secret() == sk_back.to_secret_array().as_secret()); - } - - #[test] - fn test_serialize_secret_key_factory() { - let skf = SecretKeyFactory::random(); - let skf_arr = skf.to_secret_array(); - let skf_back = SecretKeyFactory::from_array(skf_arr.as_secret()).unwrap(); - assert!(skf.to_secret_array().as_secret() == skf_back.to_secret_array().as_secret()); - } + use crate::serde_bytes::tests::check_serialization_roundtrip; #[test] fn test_secret_key_factory() { @@ -397,17 +355,8 @@ mod tests { let sk2 = skf.make_key(b"foo"); let sk3 = skf.make_key(b"bar"); - assert!(sk1.to_secret_array().as_secret() == sk2.to_secret_array().as_secret()); - assert!(sk1.to_secret_array().as_secret() != sk3.to_secret_array().as_secret()); - } - - #[test] - fn test_serialize_public_key() { - let sk = SecretKey::random(); - let pk = sk.public_key(); - let pk_arr = pk.to_array(); - let pk_back = PublicKey::from_array(&pk_arr).unwrap(); - assert_eq!(pk, pk_back); + assert!(sk1 == sk2); + assert!(sk1 != sk3); } #[test] @@ -426,16 +375,20 @@ mod tests { #[cfg(feature = "serde-support")] #[test] - fn test_serde_serialization() { + fn test_serialize_signature() { let message = b"asdafdahsfdasdfasd"; let signer = Signer::new(SecretKey::random()); - let pk = signer.verifying_key(); let signature = signer.sign(message); - check_serialization(&pk, Encoding::Hex); - check_deserialization(&pk); + check_serialization_roundtrip(&signature); + } + + #[cfg(feature = "serde-support")] + #[test] + fn test_serialize_public_key() { + let signer = Signer::new(SecretKey::random()); + let pk = signer.verifying_key(); - check_serialization(&signature, Encoding::Base64); - check_deserialization(&signature); + check_serialization_roundtrip(&pk); } } diff --git a/umbral-pre/src/lib.rs b/umbral-pre/src/lib.rs index 39811afc..62b92b54 100644 --- a/umbral-pre/src/lib.rs +++ b/umbral-pre/src/lib.rs @@ -62,8 +62,8 @@ //! // obtaining this way a "capsule fragment", or cfrag. //! //! // Simulate network transfer -//! let kfrag0 = KeyFrag::from_array(&verified_kfrags[0].to_array()).unwrap(); -//! let kfrag1 = KeyFrag::from_array(&verified_kfrags[1].to_array()).unwrap(); +//! let kfrag0 = verified_kfrags[0].clone().unverify(); +//! let kfrag1 = verified_kfrags[1].clone().unverify(); //! //! // Bob collects the resulting cfrags from several Ursulas. //! // Bob must gather at least `threshold` cfrags in order to open the capsule. @@ -82,8 +82,8 @@ //! // ... //! //! // Simulate network transfer -//! let cfrag0 = CapsuleFrag::from_array(&verified_cfrag0.to_array()).unwrap(); -//! let cfrag1 = CapsuleFrag::from_array(&verified_cfrag1.to_array()).unwrap(); +//! let cfrag0 = verified_cfrag0.clone().unverify(); +//! let cfrag1 = verified_cfrag1.clone().unverify(); //! //! // Finally, Bob opens the capsule by using at least `threshold` cfrags, //! // and then decrypts the re-encrypted ciphertext. @@ -108,7 +108,7 @@ #![warn(missing_docs, rust_2018_idioms, unused_qualifications)] #![no_std] // Allows us to mark items in the documentation as gated under specific features. -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(feature = "std")] extern crate std; @@ -153,10 +153,6 @@ pub use pre::{ reencrypt_with_rng, ReencryptionError, }; pub use secret_box::SecretBox; -pub use traits::{ - ConstructionError, DeserializableFromArray, DeserializationError, RepresentableAsArray, - SerializableToArray, SerializableToSecretArray, SizeMismatchError, -}; #[cfg(feature = "default-rng")] pub use pre::{encrypt, generate_kfrags, reencrypt}; diff --git a/umbral-pre/src/params.rs b/umbral-pre/src/params.rs index 59b5afc6..2208d82d 100644 --- a/umbral-pre/src/params.rs +++ b/umbral-pre/src/params.rs @@ -1,7 +1,11 @@ +#[cfg(feature = "serde-support")] +use serde::{Deserialize, Serialize}; + use crate::curve::CurvePoint; /// An object containing shared scheme parameters. #[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub struct Parameters { pub(crate) u: CurvePoint, } diff --git a/umbral-pre/src/pre.rs b/umbral-pre/src/pre.rs index e0c09dad..6a2a21d2 100644 --- a/umbral-pre/src/pre.rs +++ b/umbral-pre/src/pre.rs @@ -12,7 +12,6 @@ use crate::capsule_frag::VerifiedCapsuleFrag; use crate::dem::{DecryptionError, EncryptionError, DEM}; use crate::key_frag::{KeyFragBase, VerifiedKeyFrag}; use crate::keys::{PublicKey, SecretKey, Signer}; -use crate::traits::SerializableToArray; use alloc::boxed::Box; use alloc::vec::Vec; @@ -45,7 +44,7 @@ pub fn encrypt_with_rng( ) -> Result<(Capsule, Box<[u8]>), EncryptionError> { let (capsule, key_seed) = Capsule::from_public_key(rng, delegating_pk); let dem = DEM::new(key_seed.as_secret()); - dem.encrypt(rng, plaintext, &capsule.to_array()) + dem.encrypt(rng, plaintext, &capsule.to_associated_data_bytes()) .map(|ciphertext| (capsule, ciphertext)) } @@ -67,7 +66,7 @@ pub fn decrypt_original( ) -> Result, DecryptionError> { let key_seed = capsule.open_original(delegating_sk); let dem = DEM::new(key_seed.as_secret()); - dem.decrypt(ciphertext, &capsule.to_array()) + dem.decrypt(ciphertext, &capsule.to_associated_data_bytes()) } /// Creates `shares` fragments of `delegating_sk`, @@ -185,7 +184,7 @@ pub fn decrypt_reencrypted( .open_reencrypted(receiving_sk, delegating_pk, &cfrags) .map_err(ReencryptionError::OnOpen)?; let dem = DEM::new(key_seed.as_secret()); - dem.decrypt(&ciphertext, &capsule.to_array()) + dem.decrypt(&ciphertext, &capsule.to_associated_data_bytes()) .map_err(ReencryptionError::OnDecryption) } @@ -194,10 +193,7 @@ mod tests { use alloc::vec::Vec; - use crate::{ - CapsuleFrag, DeserializableFromArray, KeyFrag, SecretKey, SerializableToArray, Signer, - VerifiedCapsuleFrag, - }; + use crate::{SecretKey, Signer, VerifiedCapsuleFrag}; use super::{decrypt_original, decrypt_reencrypted, encrypt, generate_kfrags, reencrypt}; @@ -250,8 +246,9 @@ mod tests { // Simulate network transfer let kfrags = verified_kfrags - .iter() - .map(|vkfrag| KeyFrag::from_array(&vkfrag.to_array()).unwrap()); + .to_vec() + .into_iter() + .map(|vkfrag| vkfrag.unverify()); // If Ursula received kfrags from the network, she must check that they are valid let verified_kfrags: Vec<_> = kfrags @@ -270,8 +267,9 @@ mod tests { // Simulate network transfer let cfrags = verified_cfrags - .iter() - .map(|vcfrag| CapsuleFrag::from_array(&vcfrag.to_array()).unwrap()); + .to_vec() + .into_iter() + .map(|vcfrag| vcfrag.clone().unverify()); // If Bob received cfrags from the network, he must check that they are valid let verified_cfrags: Vec<_> = cfrags diff --git a/umbral-pre/src/secret_box.rs b/umbral-pre/src/secret_box.rs index 6b8ea440..bc9fe583 100644 --- a/umbral-pre/src/secret_box.rs +++ b/umbral-pre/src/secret_box.rs @@ -35,6 +35,12 @@ pub struct SecretBox(Box) where T: Zeroize + Clone; +impl PartialEq> for SecretBox { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + impl SecretBox where T: Zeroize + Clone, diff --git a/umbral-pre/src/serde_bytes.rs b/umbral-pre/src/serde_bytes.rs index 100931fd..5cca48ca 100644 --- a/umbral-pre/src/serde_bytes.rs +++ b/umbral-pre/src/serde_bytes.rs @@ -9,8 +9,6 @@ use core::marker::PhantomData; use serde::{de, Deserializer, Serializer}; -use crate::traits::{DeserializableFromArray, DeserializationError, SerializableToArray}; - pub(crate) enum Encoding { /// Use base64 representation for byte arrays. Base64, @@ -227,32 +225,6 @@ impl TryFromBytes for Box<[u8]> { } } -/// An adapter `SerializableToArray` -> `AsRef<[u8]>` for serialization. -/// (we can't just define a trait since we need to instantiate the array first -/// and cannot return a dangling reference to it). -pub(crate) fn serialize_as_array( - obj: &T, - serializer: S, - encoding: Encoding, -) -> Result -where - T: SerializableToArray, - S: Serializer, -{ - let array = obj.to_array(); - serialize_with_encoding(&array, serializer, encoding) -} - -/// For deserialization, unlike serialization, we can piggyback on `TryFromBytes` support directly, -/// since anything implementing `DeserializableFromArray` trivially implements `TryFromBytes`. -impl TryFromBytes for T { - type Error = DeserializationError; - - fn try_from_bytes(bytes: &[u8]) -> Result { - Self::from_bytes(bytes) - } -} - #[cfg(test)] pub(crate) mod tests { @@ -261,40 +233,9 @@ pub(crate) mod tests { use serde::de::DeserializeOwned; use serde::Serialize; - use super::Encoding; - use crate::SerializableToArray; - - /// A helper function that checks that serialization to a human-readable format - /// uses b64 encoding, and serialization to a binary format contains plain bytes of the object. - pub(crate) fn check_serialization(obj: &T, encoding: Encoding) - where - T: SerializableToArray + fmt::Debug + PartialEq + Serialize, - { - // Check serialization to JSON (human-readable) - - let serialized = serde_json::to_string(obj).unwrap(); - - let substr = match encoding { - Encoding::Base64 => base64::encode(obj.to_array().as_ref()), - Encoding::Hex => hex::encode(obj.to_array().as_ref()), - }; - - // check that the serialization contains the properly encoded bytestring - assert!(serialized.contains(&substr)); - - // Check serialization to MessagePack (binary) - - let serialized = rmp_serde::to_vec(obj).unwrap(); - let bytes = obj.to_array(); - // check that the serialization contains the bytestring - assert!(serialized - .windows(bytes.len()) - .any(move |sub_slice| sub_slice == bytes.as_ref())); - } - - pub(crate) fn check_deserialization(obj: &T) + pub(crate) fn check_serialization_roundtrip(obj: &T) where - T: SerializableToArray + fmt::Debug + PartialEq + Serialize + DeserializeOwned, + T: fmt::Debug + PartialEq + Serialize + DeserializeOwned, { // Check serialization to JSON (human-readable) @@ -305,7 +246,7 @@ pub(crate) mod tests { // Check serialization to MessagePack (binary) let serialized = rmp_serde::to_vec(obj).unwrap(); - let deserialized: T = rmp_serde::from_read(&*serialized).unwrap(); + let deserialized: T = rmp_serde::from_slice(&serialized).unwrap(); assert_eq!(obj, &deserialized); } } diff --git a/umbral-pre/src/traits.rs b/umbral-pre/src/traits.rs index c094ef28..55ed1e18 100644 --- a/umbral-pre/src/traits.rs +++ b/umbral-pre/src/traits.rs @@ -1,44 +1,4 @@ -use alloc::format; -use alloc::string::String; -use core::cmp::Ordering; use core::fmt; -use core::ops::Sub; - -use generic_array::sequence::Split; -use generic_array::{ArrayLength, GenericArray}; -use typenum::{Diff, Unsigned, U1}; - -use crate::secret_box::SecretBox; - -/// Errors that can happen during deserializing an object from a bytestring of correct length. -#[derive(Debug, PartialEq, Eq)] -pub struct ConstructionError { - /// The name of the type that was being deserialized - /// (can be one of the nested fields). - type_name: String, - /// An associated error message. - message: String, -} - -impl ConstructionError { - /// Creates a new `ConstructionError`. - pub fn new(type_name: &str, message: &str) -> Self { - Self { - type_name: type_name.into(), - message: message.into(), - } - } -} - -impl fmt::Display for ConstructionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Failed to construct a {} object: {}", - self.type_name, self.message - ) - } -} /// The provided bytestring is of an incorrect size. #[derive(Debug, PartialEq, Eq)] @@ -67,127 +27,6 @@ impl fmt::Display for SizeMismatchError { } } -/// Errors that can happen during object deserialization. -#[derive(Debug, PartialEq, Eq)] -pub enum DeserializationError { - /// Failed to construct the object from a given bytestring (with the correct length). - ConstructionFailure(ConstructionError), - /// The given bytestring is too short or too long. - SizeMismatch(SizeMismatchError), -} - -impl fmt::Display for DeserializationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ConstructionFailure(err) => write!(f, "{}", err), - Self::SizeMismatch(err) => write!(f, "{}", err), - } - } -} - -/// A trait denoting that the object can be represented as an array of bytes -/// with size known at compile time. -pub trait RepresentableAsArray: Sized { - /// Resulting array length. - type Size: ArrayLength; - - // It would be nice to have a dependent type - // type Array = GenericArray; - // but it's currently an unstable feature or Rust. - - /// Resulting array length exposed as a runtime method. - fn serialized_size() -> usize { - Self::Size::to_usize() - } -} - -/// A trait denoting that the object can be serialized to an array of bytes -/// with size known at compile time. -pub trait SerializableToArray: RepresentableAsArray { - /// Produces a byte array with the object's contents. - fn to_array(&self) -> GenericArray; -} - -/// A trait denoting that the object can be serialized to an array of bytes -/// containing secret data. -pub trait SerializableToSecretArray: RepresentableAsArray { - /// Produces a byte array with the object's contents, wrapped in a secret container. - fn to_secret_array(&self) -> SecretBox>; -} - -/// A trait denoting that the object can be deserialized from an array of bytes -/// with size known at compile time. -pub trait DeserializableFromArray: RepresentableAsArray { - /// Attempts to produce the object back from the serialized form. - fn from_array(arr: &GenericArray) -> Result; - - /// Attempts to produce the object back from a dynamically sized byte array, - /// checking that its length is correct. - fn from_bytes(data: impl AsRef<[u8]>) -> Result { - let data_slice = data.as_ref(); - let received_size = data_slice.len(); - let expected_size = Self::serialized_size(); - match received_size.cmp(&expected_size) { - Ordering::Greater | Ordering::Less => Err(DeserializationError::SizeMismatch( - SizeMismatchError::new(received_size, expected_size), - )), - Ordering::Equal => { - Self::from_array(GenericArray::::from_slice(data_slice)) - .map_err(DeserializationError::ConstructionFailure) - } - } - } - - /// Used to implement [`from_array()`](`Self::from_array()`) for structs whose fields - /// implement [`SerializableToArray`]. - /// - /// Attempts to split off enough bytes from `arr` to call - /// [`from_array()`](`Self::from_array()`), - /// and if it succeeds, returns the resulting object and the rest of the array. - #[allow(clippy::type_complexity)] - fn take( - arr: GenericArray, - ) -> Result<(Self, GenericArray>), ConstructionError> - where - U: ArrayLength + Sub, - Diff: ArrayLength, - { - let (res_bytes, rest): (GenericArray, GenericArray) = arr.split(); - let maybe_res = Self::from_array(&res_bytes); - maybe_res.map(|res| (res, rest)) - } - - /// A variant of [`take()`](`Self::take()`) to be called for the last field of the struct, - /// where no remainder of the array is expected. - fn take_last(arr: GenericArray) -> Result { - Self::from_array(&arr) - } -} - -impl RepresentableAsArray for bool { - type Size = U1; -} - -impl SerializableToArray for bool { - fn to_array(&self) -> GenericArray { - GenericArray::::from([*self as u8]) - } -} - -impl DeserializableFromArray for bool { - fn from_array(arr: &GenericArray) -> Result { - let bytes_slice = arr.as_slice(); - match bytes_slice[0] { - 0u8 => Ok(false), - 1u8 => Ok(true), - _ => Err(ConstructionError::new( - "bool", - &format!("Expected 0x0 or 0x1, got 0x{:x?}", bytes_slice[0]), - )), - } - } -} - /// A `fmt` implementation for types with secret data. pub(crate) fn fmt_secret(type_name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:...", type_name) @@ -203,132 +42,3 @@ pub(crate) fn fmt_public( let bytes = if bytes.len() > 8 { &bytes[..8] } else { bytes }; write!(f, "{}:{}", type_name, hex::encode(bytes),) } - -#[cfg(test)] -mod tests { - - use generic_array::sequence::Concat; - use generic_array::GenericArray; - use typenum::{op, U1, U2}; - - use super::{ - ConstructionError, DeserializableFromArray, DeserializationError, RepresentableAsArray, - SerializableToArray, SizeMismatchError, - }; - - impl RepresentableAsArray for u8 { - type Size = U1; - } - - impl SerializableToArray for u8 { - fn to_array(&self) -> GenericArray { - GenericArray::::from([*self]) - } - } - - impl DeserializableFromArray for u8 { - fn from_array(arr: &GenericArray) -> Result { - Ok(arr.as_slice()[0]) - } - } - - impl RepresentableAsArray for u16 { - type Size = U2; - } - - impl SerializableToArray for u16 { - fn to_array(&self) -> GenericArray { - GenericArray::::from([(self >> 8) as u8, *self as u8]) - } - } - - impl DeserializableFromArray for u16 { - fn from_array(arr: &GenericArray) -> Result { - let b1 = arr.as_slice()[0]; - let b2 = arr.as_slice()[1]; - Ok(((b1 as u16) << 8) + (b2 as u16)) - } - } - - #[derive(Debug, PartialEq)] - struct SomeStruct { - f1: u16, - f2: u8, - f3: u16, - f4: bool, - } - - type U8Size = ::Size; - type U16Size = ::Size; - type BoolSize = ::Size; - - impl RepresentableAsArray for SomeStruct { - type Size = op!(U16Size + U8Size + U16Size + BoolSize); - } - - impl SerializableToArray for SomeStruct { - fn to_array(&self) -> GenericArray { - self.f1 - .to_array() - .concat(self.f2.to_array()) - .concat(self.f3.to_array()) - .concat(self.f4.to_array()) - } - } - - impl DeserializableFromArray for SomeStruct { - fn from_array(arr: &GenericArray) -> Result { - let (f1, rest) = u16::take(*arr)?; - let (f2, rest) = u8::take(rest)?; - let (f3, rest) = u16::take(rest)?; - let f4 = bool::take_last(rest)?; - Ok(Self { f1, f2, f3, f4 }) - } - } - - #[test] - fn test_serialize() { - let s = SomeStruct { - f1: 1, - f2: 2, - f3: 3, - f4: true, - }; - let s_arr = s.to_array(); - - let s_arr_ref: [u8; 6] = [0x00, 0x01, 0x02, 0x00, 0x03, 0x01]; - assert_eq!(s_arr.as_slice(), &s_arr_ref); - - let s_from_arr = SomeStruct::from_array(&s_arr).unwrap(); - assert_eq!(s_from_arr, s); - - let s_from_bytes = SomeStruct::from_bytes(s_arr_ref).unwrap(); - assert_eq!(s_from_bytes, s); - } - - #[test] - fn test_invalid_data() { - // invalid value for `f4` (`bool` must be either 0 or 1) - let s_arr: [u8; 6] = [0x00, 0x01, 0x02, 0x00, 0x03, 0x02]; - let s = SomeStruct::from_bytes(s_arr); - assert_eq!( - s, - Err(DeserializationError::ConstructionFailure( - ConstructionError::new("bool", "Expected 0x0 or 0x1, got 0x2") - )) - ) - } - - #[test] - fn test_invalid_length() { - // An excessive byte at the end - let s_arr: [u8; 7] = [0x00, 0x01, 0x02, 0x00, 0x03, 0x01, 0x00]; - let s = SomeStruct::from_bytes(s_arr); - assert_eq!( - s, - Err(DeserializationError::SizeMismatch(SizeMismatchError::new( - 7, 6 - ))) - ) - } -}