From 684a202a02d4db18f515134332f5213ffe2e99f8 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Thu, 5 Jan 2023 20:05:10 -0800 Subject: [PATCH] Add SecretFactory::make_secret(), review non-serde serialization methods --- umbral-pre-python/umbral_pre/__init__.pyi | 62 ++--------- umbral-pre/Cargo.toml | 5 +- umbral-pre/src/bindings_python.rs | 119 +++++++++++----------- umbral-pre/src/bindings_wasm.rs | 76 +++++++------- umbral-pre/src/capsule.rs | 11 +- umbral-pre/src/capsule_frag.rs | 12 +++ umbral-pre/src/curve.rs | 32 +++--- umbral-pre/src/hashing_ds.rs | 4 +- umbral-pre/src/key_frag.rs | 14 ++- umbral-pre/src/keys.rs | 67 ++++++------ umbral-pre/src/lib.rs | 3 + umbral-pre/src/pre.rs | 10 +- umbral-pre/src/traits.rs | 26 +++++ 13 files changed, 231 insertions(+), 210 deletions(-) diff --git a/umbral-pre-python/umbral_pre/__init__.pyi b/umbral-pre-python/umbral_pre/__init__.pyi index b8121c6..cca83b7 100644 --- a/umbral-pre-python/umbral_pre/__init__.pyi +++ b/umbral-pre-python/umbral_pre/__init__.pyi @@ -10,17 +10,10 @@ class SecretKey: def public_key(self) -> PublicKey: ... - def to_secret_bytes(self) -> bytes: - ... - @staticmethod def from_bytes(data: bytes) -> SecretKey: ... - @staticmethod - def serialized_size() -> int: - ... - class SecretKeyFactory: @@ -36,35 +29,27 @@ class SecretKeyFactory: def from_secure_randomness(seed: bytes) -> SecretKeyFactory: ... - def make_key(self, label: bytes) -> SecretKey: + def make_secret(self, label: bytes) -> bytes: ... - def make_factory(self, label: bytes) -> SecretKeyFactory: - ... - - def to_secret_bytes(self) -> bytes: + def make_key(self, label: bytes) -> SecretKey: ... - @staticmethod - def from_bytes(data: bytes) -> SecretKeyFactory: + def make_factory(self, label: bytes) -> SecretKeyFactory: ... @staticmethod - def serialized_size() -> int: + def from_secure_randomness(data: bytes) -> SecretKeyFactory: ... class PublicKey: @staticmethod - def from_bytes(data: bytes) -> PublicKey: - ... - - def __bytes__(self) -> bytes: + def from_compressed_bytes(data: bytes) -> PublicKey: ... - @staticmethod - def serialized_size() -> int: + def to_compressed_bytes(self) -> bytes: ... @@ -86,23 +71,15 @@ class Signature: ... @staticmethod - def from_bytes(data: bytes) -> Signature: + def from_der_bytes(data: bytes) -> Signature: ... - def __bytes__(self) -> bytes: - ... - - @staticmethod - def serialized_size() -> int: + def to_der_bytes(self) -> bytes: ... class Capsule: - @staticmethod - def serialized_size() -> int: - ... - @staticmethod def from_bytes(data: bytes) -> Capsule: ... @@ -139,26 +116,15 @@ class KeyFrag: def __bytes__(self) -> bytes: ... - @staticmethod - def serialized_size() -> int: - ... - class VerifiedKeyFrag: - def from_verified_bytes(self, data: bytes) -> VerifiedKeyFrag: - ... - def __bytes__(self) -> bytes: ... def unverify(self) -> KeyFrag: ... - @staticmethod - def serialized_size() -> int: - ... - def generate_kfrags( delegating_sk: SecretKey, @@ -193,27 +159,15 @@ class CapsuleFrag: def __bytes__(self) -> bytes: ... - @staticmethod - def serialized_size() -> int: - ... - class VerifiedCapsuleFrag: - @staticmethod - def from_verified_bytes(data: bytes) -> VerifiedCapsuleFrag: - ... - def __bytes__(self) -> bytes: ... def unverify(self) -> CapsuleFrag: ... - @staticmethod - def serialized_size() -> int: - ... - def reencrypt(capsule: Capsule, kfrag: VerifiedKeyFrag) -> VerifiedCapsuleFrag: ... diff --git a/umbral-pre/Cargo.toml b/umbral-pre/Cargo.toml index 94920f9..04c1416 100644 --- a/umbral-pre/Cargo.toml +++ b/umbral-pre/Cargo.toml @@ -46,9 +46,10 @@ rmp-serde = "0.15" [features] default = ["default-rng"] bench-internals = ["default-rng"] -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"] +bindings-python = ["pyo3", "std", "derive_more", "default-serialization"] +bindings-wasm = ["js-sys", "default-serialization", "wasm-bindgen", "derive_more", "wasm-bindgen-derive"] default-rng = ["getrandom", "rand_core/getrandom"] +default-serialization = ["serde-support", "rmp-serde"] serde-support = ["serde"] std = [] diff --git a/umbral-pre/src/bindings_python.rs b/umbral-pre/src/bindings_python.rs index a0d2da5..0fe8381 100644 --- a/umbral-pre/src/bindings_python.rs +++ b/umbral-pre/src/bindings_python.rs @@ -11,36 +11,40 @@ use alloc::format; use alloc::string::String; use alloc::vec::Vec; +use core::fmt; +use generic_array::{sequence::Split, typenum::U8, GenericArray}; use pyo3::class::basic::CompareOp; use pyo3::create_exception; use pyo3::exceptions::{PyException, PyTypeError, PyValueError}; use pyo3::prelude::*; use pyo3::pyclass::PyClass; -use pyo3::types::{PyBytes, PyUnicode}; +use pyo3::types::PyBytes; use pyo3::wrap_pyfunction; - -use serde::{Deserialize, Serialize}; +use sha2::{digest::Update, Digest, Sha256}; use crate as umbral_pre; +use umbral_pre::{DefaultDeserialize, DefaultSerialize}; + +fn map_py_value_err(err: T) -> PyErr { + PyValueError::new_err(format!("{}", err)) +} fn to_bytes(obj: &T) -> PyResult where T: AsRef, - U: Serialize, + U: DefaultSerialize, { - let serialized = - rmp_serde::to_vec(obj.as_ref()).map_err(|err| PyValueError::new_err(format!("{}", err)))?; + let serialized = obj.as_ref().to_bytes().map_err(map_py_value_err)?; Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, &serialized).into()) }) } fn from_bytes<'de, T, U>(data: &'de [u8]) -> PyResult where T: From, - U: Deserialize<'de>, + U: DefaultDeserialize<'de>, { - let backend: U = - rmp_serde::from_slice(data).map_err(|err| PyValueError::new_err(format!("{}", err)))?; + let backend = U::from_bytes(data).map_err(map_py_value_err)?; Ok(T::from(backend)) } @@ -50,21 +54,13 @@ fn type_name() -> &'static str { core::any::type_name::() } -fn hash(obj: &T) -> PyResult -where - T: AsRef, - U: Serialize, -{ - 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).into(); - builtins.getattr("hash")?.call1(((arg1, arg2),))?.extract() - }) +fn hash(data: impl AsRef<[u8]>) -> i64 { + // This function does not require a cryptographic hash, + // we just need something fast that minimizes conflicts. + let digest = Sha256::new().chain(data).finalize(); + let (chunk, _): (GenericArray, _) = digest.split(); + let arr: [u8; 8] = chunk.try_into().unwrap(); + i64::from_be_bytes(arr) } fn richcmp(obj: &T, other: &T, op: CompareOp) -> PyResult @@ -104,13 +100,6 @@ impl SecretKey { } } - pub fn to_secret_bytes(&self) -> PyResult { - 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 { Ok(format!("{}", self.backend)) } @@ -138,7 +127,13 @@ impl SecretKeyFactory { pub fn from_secure_randomness(seed: &[u8]) -> PyResult { umbral_pre::SecretKeyFactory::from_secure_randomness(seed) .map(SecretKeyFactory::from) - .map_err(|err| PyValueError::new_err(format!("{}", err))) + .map_err(map_py_value_err) + } + + pub fn make_secret(&self, label: &[u8]) -> Vec { + let secret = self.backend.make_secret(label); + let bytes: &[u8] = secret.as_secret().as_ref(); + bytes.into() } pub fn make_key(&self, label: &[u8]) -> SecretKey { @@ -163,20 +158,23 @@ pub struct PublicKey { #[pymethods] impl PublicKey { #[staticmethod] - pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, umbral_pre::PublicKey>(data) + fn from_compressed_bytes(data: &[u8]) -> PyResult { + umbral_pre::PublicKey::try_from_compressed_bytes(data) + .map_err(map_py_value_err) + .map(Self::from) } - fn __bytes__(&self) -> PyResult { - to_bytes(self) + fn to_compressed_bytes(&self) -> PyResult { + let serialized = self.backend.to_compressed_bytes(); + Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, &serialized).into()) }) } fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> i64 { + hash(&self.backend.to_compressed_bytes()) } fn __str__(&self) -> PyResult { @@ -219,24 +217,27 @@ pub struct Signature { #[pymethods] impl Signature { #[staticmethod] - pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, umbral_pre::Signature>(data) + fn from_der_bytes(data: &[u8]) -> PyResult { + umbral_pre::Signature::try_from_der_bytes(data) + .map_err(map_py_value_err) + .map(Self::from) } - pub fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool { - self.backend.verify(&verifying_pk.backend, message) + fn to_der_bytes(&self) -> PyResult { + let serialized = self.backend.to_der_bytes(); + Python::with_gil(|py| -> PyResult { Ok(PyBytes::new(py, &serialized).into()) }) } - fn __bytes__(&self) -> PyResult { - to_bytes(self) + fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool { + self.backend.verify(&verifying_pk.backend, message) } fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> i64 { + hash(&self.backend.to_der_bytes()) } fn __str__(&self) -> PyResult { @@ -265,8 +266,8 @@ impl Capsule { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> PyResult { + self.backend.to_bytes().map_err(map_py_value_err).map(hash) } fn __str__(&self) -> PyResult { @@ -284,7 +285,7 @@ pub fn encrypt( .map(|(backend_capsule, ciphertext)| { (backend_capsule.into(), PyBytes::new(py, &ciphertext).into()) }) - .map_err(|err| PyValueError::new_err(format!("{}", err))) + .map_err(map_py_value_err) } #[pyfunction] @@ -296,7 +297,7 @@ pub fn decrypt_original( ) -> PyResult { umbral_pre::decrypt_original(&delegating_sk.backend, &capsule.backend, ciphertext) .map(|plaintext| PyBytes::new(py, &plaintext).into()) - .map_err(|err| PyValueError::new_err(format!("{}", err))) + .map_err(map_py_value_err) } #[pyclass(module = "umbral")] @@ -343,8 +344,8 @@ impl KeyFrag { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> PyResult { + self.backend.to_bytes().map_err(map_py_value_err).map(hash) } fn __str__(&self) -> PyResult { @@ -374,8 +375,8 @@ impl VerifiedKeyFrag { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> PyResult { + self.backend.to_bytes().map_err(map_py_value_err).map(hash) } fn __str__(&self) -> PyResult { @@ -457,8 +458,8 @@ impl CapsuleFrag { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> PyResult { + self.backend.to_bytes().map_err(map_py_value_err).map(hash) } fn __str__(&self) -> PyResult { @@ -478,8 +479,8 @@ impl VerifiedCapsuleFrag { richcmp(self, other, op) } - fn __hash__(&self) -> PyResult { - hash(self) + fn __hash__(&self) -> PyResult { + self.backend.to_bytes().map_err(map_py_value_err).map(hash) } fn __str__(&self) -> PyResult { @@ -525,7 +526,7 @@ pub fn decrypt_reencrypted( ciphertext, ) .map(|plaintext| PyBytes::new(py, &plaintext).into()) - .map_err(|err| PyValueError::new_err(format!("{}", err))) + .map_err(map_py_value_err) } // Since adding functions in pyo3 requires a two-step process diff --git a/umbral-pre/src/bindings_wasm.rs b/umbral-pre/src/bindings_wasm.rs index 2d9ea38..128b5ac 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::serde_bytes::TryFromBytes; +use umbral_pre::{DefaultDeserialize, DefaultSerialize}; #[wasm_bindgen] extern "C" { @@ -97,15 +97,6 @@ impl SecretKey { PublicKey(self.0.public_key()) } - #[wasm_bindgen(js_name = toSecretBytes)] - pub fn to_secret_bytes(&self) -> Box<[u8]> { - self.0 - .to_secret_bytes() - .as_secret() - .to_vec() - .into_boxed_slice() - } - #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -135,6 +126,13 @@ impl SecretKeyFactory { .map_err(map_js_err) } + #[wasm_bindgen(js_name = makeSecret)] + pub fn make_secret(&self, label: &[u8]) -> Vec { + let secret = self.0.make_secret(label); + let bytes: &[u8] = secret.as_secret().as_ref(); + bytes.into() + } + #[wasm_bindgen(js_name = makeKey)] pub fn make_key(&self, label: &[u8]) -> SecretKey { SecretKey(self.0.make_key(label)) @@ -159,15 +157,15 @@ pub struct PublicKey(umbral_pre::PublicKey); #[wasm_bindgen] impl PublicKey { - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - self.0.to_array().to_vec().into_boxed_slice() + #[wasm_bindgen(js_name = toCompressedBytes)] + pub fn to_compressed_bytes(&self) -> Box<[u8]> { + self.0.to_compressed_bytes() } - #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::PublicKey::try_from_bytes(data) - .map(PublicKey) + #[wasm_bindgen(js_name = fromCompressedBytes)] + pub fn from_compressed_bytes(data: &[u8]) -> Result { + umbral_pre::PublicKey::try_from_compressed_bytes(data) + .map(Self) .map_err(map_js_err) } @@ -218,14 +216,14 @@ impl Signature { self.0.verify(&verifying_pk.0, message) } - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { + #[wasm_bindgen(js_name = toDerBytes)] + pub fn to_der_bytes(&self) -> Box<[u8]> { self.0.to_der_bytes() } - #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - umbral_pre::Signature::from_der_bytes(data) + #[wasm_bindgen(js_name = fromDerBytes)] + pub fn from_der_bytes(data: &[u8]) -> Result { + umbral_pre::Signature::try_from_der_bytes(data) .map(Self) .map_err(map_js_err) } @@ -243,19 +241,21 @@ impl Signature { #[derive(TryFromJsValue)] #[wasm_bindgen] -#[derive(Clone, Copy, derive_more::AsRef, derive_more::From, derive_more::Into)] +#[derive(Clone, derive_more::AsRef, derive_more::From, derive_more::Into)] pub struct Capsule(umbral_pre::Capsule); #[wasm_bindgen] impl Capsule { #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Result, Error> { - rmp_serde::to_vec(&self.0).map_err(map_js_err) + pub fn to_bytes(&self) -> Result, Error> { + self.0.to_bytes().map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - rmp_serde::from_slice(data).map(Self).map_err(map_js_err) + umbral_pre::Capsule::from_bytes(data) + .map(Self) + .map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -296,13 +296,15 @@ impl CapsuleFrag { } #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Result, Error> { - rmp_serde::to_vec(&self.0).map_err(map_js_err) + pub fn to_bytes(&self) -> Result, Error> { + self.0.to_bytes().map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - rmp_serde::from_slice(data).map(Self).map_err(map_js_err) + umbral_pre::CapsuleFrag::from_bytes(data) + .map(Self) + .map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -329,8 +331,8 @@ pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag); #[wasm_bindgen] impl VerifiedCapsuleFrag { #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Result, Error> { - rmp_serde::to_vec(&self.0).map_err(map_js_err) + pub fn to_bytes(&self) -> Result, Error> { + self.0.to_bytes().map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -420,13 +422,15 @@ impl KeyFrag { } #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Result, Error> { - rmp_serde::to_vec(&self.0).map_err(map_js_err) + pub fn to_bytes(&self) -> Result, Error> { + self.0.to_bytes().map_err(map_js_err) } #[wasm_bindgen(js_name = fromBytes)] pub fn from_bytes(data: &[u8]) -> Result { - rmp_serde::from_slice(data).map(Self).map_err(map_js_err) + umbral_pre::KeyFrag::from_bytes(data) + .map(Self) + .map_err(map_js_err) } #[allow(clippy::inherent_to_string)] @@ -453,8 +457,8 @@ pub struct VerifiedKeyFrag(umbral_pre::VerifiedKeyFrag); #[wasm_bindgen] impl VerifiedKeyFrag { #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Result, Error> { - rmp_serde::to_vec(&self.0).map_err(map_js_err) + pub fn to_bytes(&self) -> Result, Error> { + self.0.to_bytes().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 4030ee6..426e5ec 100644 --- a/umbral-pre/src/capsule.rs +++ b/umbral-pre/src/capsule.rs @@ -19,6 +19,9 @@ use crate::params::Parameters; use crate::secret_box::SecretBox; use crate::traits::fmt_public; +#[cfg(feature = "default-serialization")] +use crate::{DefaultDeserialize, DefaultSerialize}; + /// Errors that can happen when opening a `Capsule` using reencrypted `CapsuleFrag` objects. #[derive(Debug, PartialEq, Eq)] pub enum OpenReencryptedError { @@ -58,7 +61,7 @@ struct SerializedCapsule { } /// Encapsulated symmetric key used to encrypt the plaintext. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, 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"))] @@ -90,6 +93,12 @@ impl From for SerializedCapsule { } } +#[cfg(feature = "default-serialization")] +impl DefaultSerialize for Capsule {} + +#[cfg(feature = "default-serialization")] +impl<'de> DefaultDeserialize<'de> for Capsule {} + impl fmt::Display for Capsule { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_public("Capsule", &self.signature.to_array(), f) diff --git a/umbral-pre/src/capsule_frag.rs b/umbral-pre/src/capsule_frag.rs index cb8c51f..44fb6ee 100644 --- a/umbral-pre/src/capsule_frag.rs +++ b/umbral-pre/src/capsule_frag.rs @@ -13,6 +13,9 @@ use crate::keys::{PublicKey, Signature}; use crate::secret_box::SecretBox; use crate::traits::fmt_public; +#[cfg(feature = "default-serialization")] +use crate::{DefaultDeserialize, DefaultSerialize}; + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))] pub(crate) struct CapsuleFragProof { @@ -205,6 +208,12 @@ impl CapsuleFrag { } } +#[cfg(feature = "default-serialization")] +impl DefaultSerialize for CapsuleFrag {} + +#[cfg(feature = "default-serialization")] +impl<'de> DefaultDeserialize<'de> for CapsuleFrag {} + /// Verified capsule fragment, good for dencryption. /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`CapsuleFrag::verify`] or [`CapsuleFrag::skip_verification`]. @@ -241,6 +250,9 @@ impl VerifiedCapsuleFrag { } } +#[cfg(feature = "default-serialization")] +impl DefaultSerialize for VerifiedCapsuleFrag {} + #[cfg(test)] mod tests { diff --git a/umbral-pre/src/curve.rs b/umbral-pre/src/curve.rs index 212b325..399e409 100644 --- a/umbral-pre/src/curve.rs +++ b/umbral-pre/src/curve.rs @@ -2,9 +2,8 @@ //! `elliptic_curves` has a somewhat unstable API, //! and we isolate all the related logic here. -#[cfg(feature = "serde-support")] +use alloc::format; use alloc::string::String; - use core::default::Default; use core::ops::{Add, Mul, Sub}; @@ -13,7 +12,7 @@ use elliptic_curve::bigint::U256; // Note that this type is different from typen use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; use elliptic_curve::ops::Reduce; use elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint}; -use elliptic_curve::{AffinePoint, Field, FieldSize, NonZeroScalar, ProjectiveArithmetic, Scalar}; +use elliptic_curve::{Field, FieldSize, NonZeroScalar, ProjectiveArithmetic, Scalar}; use generic_array::GenericArray; use k256::Secp256k1; use rand_core::{CryptoRng, RngCore}; @@ -59,7 +58,7 @@ impl CurveScalar { Self(BackendScalar::one()) } - pub(crate) fn to_array(&self) -> GenericArray { + pub(crate) fn to_array(self) -> k256::FieldBytes { self.0.to_bytes() } } @@ -90,7 +89,7 @@ impl TryFromBytes for CurveScalar { 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")?; + .ok_or("Invalid length of a curve scalar")?; // unwrap CtOption into Option let maybe_scalar: Option = BackendScalar::from_repr(arr).into(); @@ -150,7 +149,6 @@ impl From<&NonZeroCurveScalar> for CurveScalar { } type BackendPoint = ::ProjectivePoint; -type BackendPointAffine = AffinePoint; #[derive(Clone, Copy, Debug, PartialEq)] pub(crate) struct CurvePoint(BackendPoint); @@ -160,6 +158,10 @@ impl CurvePoint { Self(*point) } + pub(crate) fn as_backend_point(&self) -> &BackendPoint { + &self.0 + } + pub(crate) fn generator() -> Self { Self(BackendPoint::GENERATOR) } @@ -168,17 +170,14 @@ impl CurvePoint { Self(BackendPoint::IDENTITY) } - pub(crate) fn to_affine_point(self) -> BackendPointAffine { - self.0.to_affine() - } + pub(crate) fn try_from_compressed_bytes(bytes: &[u8]) -> Result { + let ep = EncodedPoint::::from_bytes(bytes).map_err(|err| format!("{}", err))?; - pub(crate) fn from_compressed_array( - arr: &GenericArray, - ) -> Option { - let ep = EncodedPoint::::from_bytes(arr.as_slice()).ok()?; // Unwrap CtOption into Option let cp_opt: Option = BackendPoint::from_encoded_point(&ep).into(); - cp_opt.map(Self) + cp_opt + .map(Self) + .ok_or_else(|| "Invalid curve point representation".into()) } pub(crate) fn to_compressed_array(self) -> GenericArray { @@ -228,10 +227,7 @@ 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()) + Self::try_from_compressed_bytes(bytes) } } diff --git a/umbral-pre/src/hashing_ds.rs b/umbral-pre/src/hashing_ds.rs index 89a974a..2abf521 100644 --- a/umbral-pre/src/hashing_ds.rs +++ b/umbral-pre/src/hashing_ds.rs @@ -75,7 +75,7 @@ pub(crate) fn kfrag_signature_message( match maybe_delegating_pk { Some(delegating_pk) => { result.extend_from_slice(&bool_to_array(true)); - result.extend_from_slice(&delegating_pk.to_array()) + result.extend_from_slice(&delegating_pk.to_point().to_compressed_array()) } None => result.extend_from_slice(&bool_to_array(false)), }; @@ -83,7 +83,7 @@ pub(crate) fn kfrag_signature_message( match maybe_receiving_pk { Some(receiving_pk) => { result.extend_from_slice(&bool_to_array(true)); - result.extend_from_slice(&receiving_pk.to_array()) + result.extend_from_slice(&receiving_pk.to_point().to_compressed_array()) } None => result.extend_from_slice(&bool_to_array(false)), }; diff --git a/umbral-pre/src/key_frag.rs b/umbral-pre/src/key_frag.rs index 4805583..a71ca66 100644 --- a/umbral-pre/src/key_frag.rs +++ b/umbral-pre/src/key_frag.rs @@ -19,6 +19,9 @@ use crate::params::Parameters; use crate::secret_box::SecretBox; use crate::traits::fmt_public; +#[cfg(feature = "default-serialization")] +use crate::{DefaultDeserialize, DefaultSerialize}; + #[cfg(feature = "serde-support")] use crate::serde_bytes::{ deserialize_with_encoding, serialize_with_encoding, Encoding, TryFromBytes, @@ -71,7 +74,7 @@ impl TryFromBytes for KeyFragID { 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_or("Invalid length of a key frag ID")?; Ok(Self(arr)) } } @@ -291,6 +294,12 @@ impl KeyFrag { } } +#[cfg(feature = "default-serialization")] +impl DefaultSerialize for KeyFrag {} + +#[cfg(feature = "default-serialization")] +impl<'de> DefaultDeserialize<'de> for KeyFrag {} + /// Verified key fragment, good for reencryption. /// Can be serialized, but cannot be deserialized directly. /// It can only be obtained from [`KeyFrag::verify`] or [`KeyFrag::skip_verification`]. @@ -328,6 +337,9 @@ impl VerifiedKeyFrag { } } +#[cfg(feature = "default-serialization")] +impl DefaultSerialize for VerifiedKeyFrag {} + pub(crate) struct KeyFragBase<'a> { signer: &'a Signer, precursor: CurvePoint, diff --git a/umbral-pre/src/keys.rs b/umbral-pre/src/keys.rs index db42227..9808f3e 100644 --- a/umbral-pre/src/keys.rs +++ b/umbral-pre/src/keys.rs @@ -1,9 +1,8 @@ #[cfg(feature = "serde-support")] -use alloc::string::String; +use alloc::format; use alloc::boxed::Box; -use alloc::format; -use alloc::vec::Vec; +use alloc::string::String; use core::cmp::Ordering; use core::fmt; @@ -22,7 +21,7 @@ use rand_core::OsRng; #[cfg(feature = "serde-support")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::curve::{CompressedPointSize, CurvePoint, CurveType, NonZeroCurveScalar, ScalarSize}; +use crate::curve::{CompressedPointSize, CurvePoint, CurveType, NonZeroCurveScalar}; use crate::dem::kdf; use crate::hashing::{BackendDigest, Hash, ScalarDigest}; use crate::secret_box::SecretBox; @@ -42,7 +41,8 @@ impl Signature { self.0.to_der().as_bytes().into() } - pub(crate) fn from_der_bytes(bytes: &[u8]) -> Result { + #[cfg(feature = "serde-support")] + pub(crate) fn try_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_der(bytes) @@ -78,7 +78,7 @@ impl TryFromBytes for Signature { type Error = String; fn try_from_bytes(bytes: &[u8]) -> Result { - Self::from_der_bytes(bytes) + Self::try_from_der_bytes(bytes) } } @@ -110,10 +110,6 @@ 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")))] @@ -208,8 +204,19 @@ impl PublicKey { verifier.verify_digest(digest, &signature.0).is_ok() } - pub(crate) fn to_array(&self) -> GenericArray { - self.to_point().to_compressed_array() + /// Retunrs the serialized pubic key as the compressed curve point. + pub fn try_from_compressed_bytes(bytes: &[u8]) -> Result { + let cp = CurvePoint::try_from_compressed_bytes(bytes)?; + BackendPublicKey::::try_from(cp.as_backend_point()) + .map(Self) + .map_err(|_| "Cannot instantiate a public key from the given curve point".into()) + } + + /// Restores the public key from a compressed curve point. + pub fn to_compressed_bytes(self) -> Box<[u8]> { + let arr: GenericArray = self.to_point().to_compressed_array(); + let slice: &[u8] = arr.as_ref(); + slice.into() } } @@ -220,11 +227,7 @@ impl Serialize for PublicKey { where S: Serializer, { - serialize_with_encoding( - &self.to_point().to_compressed_array(), - serializer, - Encoding::Hex, - ) + serialize_with_encoding(&self.to_compressed_bytes(), serializer, Encoding::Hex) } } @@ -244,16 +247,13 @@ 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()) + Self::try_from_compressed_bytes(bytes) } } impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_public("PublicKey", &self.to_array(), f) + fmt_public("PublicKey", &self.to_compressed_bytes(), f) } } @@ -304,14 +304,21 @@ impl SecretKeyFactory { } } + /// Creates an untyped bytestring deterministically from the given label. + /// This can be used externally to seed some kind of a secret key. + pub fn make_secret( + &self, + label: &[u8], + ) -> SecretBox> { + let prefix = b"SECRET_DERIVATION/"; + let info = [prefix, label].concat(); + kdf::(self.0.as_secret(), None, Some(&info)) + } + /// Creates a `SecretKey` deterministically from the given label. pub fn make_key(&self, label: &[u8]) -> SecretKey { let prefix = b"KEY_DERIVATION/"; - let info: Vec = prefix - .iter() - .cloned() - .chain(label.iter().cloned()) - .collect(); + let info = [prefix, label].concat(); let key = kdf::(self.0.as_secret(), None, Some(&info)); let nz_scalar = SecretBox::new( ScalarDigest::new_with_dst(&info) @@ -324,11 +331,7 @@ impl SecretKeyFactory { /// Creates a `SecretKeyFactory` deterministically from the given label. pub fn make_factory(&self, label: &[u8]) -> Self { let prefix = b"FACTORY_DERIVATION/"; - let info: Vec = prefix - .iter() - .cloned() - .chain(label.iter().cloned()) - .collect(); + let info = [prefix, label].concat(); let derived_seed = kdf::(self.0.as_secret(), None, Some(&info)); Self(derived_seed) } diff --git a/umbral-pre/src/lib.rs b/umbral-pre/src/lib.rs index 62b92b5..2bf504d 100644 --- a/umbral-pre/src/lib.rs +++ b/umbral-pre/src/lib.rs @@ -156,3 +156,6 @@ pub use secret_box::SecretBox; #[cfg(feature = "default-rng")] pub use pre::{encrypt, generate_kfrags, reencrypt}; + +#[cfg(feature = "default-serialization")] +pub use traits::{DefaultDeserialize, DefaultSerialize}; diff --git a/umbral-pre/src/pre.rs b/umbral-pre/src/pre.rs index 6a2a21d..c5bac41 100644 --- a/umbral-pre/src/pre.rs +++ b/umbral-pre/src/pre.rs @@ -246,8 +246,8 @@ mod tests { // Simulate network transfer let kfrags = verified_kfrags - .to_vec() - .into_iter() + .iter() + .cloned() .map(|vkfrag| vkfrag.unverify()); // If Ursula received kfrags from the network, she must check that they are valid @@ -267,9 +267,9 @@ mod tests { // Simulate network transfer let cfrags = verified_cfrags - .to_vec() - .into_iter() - .map(|vcfrag| vcfrag.clone().unverify()); + .iter() + .cloned() + .map(|vcfrag| vcfrag.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/traits.rs b/umbral-pre/src/traits.rs index 55ed1e1..62aaee9 100644 --- a/umbral-pre/src/traits.rs +++ b/umbral-pre/src/traits.rs @@ -1,5 +1,11 @@ +#[cfg(feature = "default-serialization")] +use alloc::boxed::Box; + use core::fmt; +#[cfg(feature = "default-serialization")] +use serde::{Deserialize, Serialize}; + /// The provided bytestring is of an incorrect size. #[derive(Debug, PartialEq, Eq)] pub struct SizeMismatchError { @@ -42,3 +48,23 @@ pub(crate) fn fmt_public( let bytes = if bytes.len() > 8 { &bytes[..8] } else { bytes }; write!(f, "{}:{}", type_name, hex::encode(bytes),) } + +/// Default serialization of an object that is used in all the bindings. +/// Uses MessagePack format. +#[cfg(feature = "default-serialization")] +pub trait DefaultSerialize: Serialize { + /// Serializes this object. + fn to_bytes(&self) -> Result, rmp_serde::encode::Error> { + rmp_serde::to_vec(self).map(|v| v.into_boxed_slice()) + } +} + +/// Default deserialization of an object that is used in all the bindings. +/// Uses MessagePack format. +#[cfg(feature = "default-serialization")] +pub trait DefaultDeserialize<'de>: Deserialize<'de> { + /// Deserializes a bytestring into this object. + fn from_bytes(bytes: &'de [u8]) -> Result { + rmp_serde::from_slice(bytes) + } +}