diff --git a/README.md b/README.md index 64ab0fb..3745f88 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,7 @@ Rust **1.51** or higher. Contributors ------------ -The author of this code is Kevin Lewi -([@kevinlewi](https://github.com/kevinlewi)) . +The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)). To learn more about contributing to this project, [see this document](./CONTRIBUTING.md). License diff --git a/src/group/mod.rs b/src/group/mod.rs index 0d78383..360ec5e 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -119,6 +119,11 @@ pub trait Group: /// Compares in constant time if the scalars are equal fn ct_equal_scalar(s1: &Self::Scalar, s2: &Self::Scalar) -> bool; + + /// Set the contents of self to the identity value + fn zeroize(&mut self) { + *self = ::identity(); + } } #[cfg(test)] diff --git a/src/impls.rs b/src/impls.rs new file mode 100644 index 0000000..553f226 --- /dev/null +++ b/src/impls.rs @@ -0,0 +1,203 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +macro_rules! impl_debug_eq_hash_for { + (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? + $(where $($type: core::fmt::Debug,)+)? + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("$name") + .field("$field1", &self.$field1) + $(.field("$field2", &self.$field2))* + .finish() + } + } + + impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)? + $(where $($type: Eq,)+)? + {} + + impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)? + $(where $($type: PartialEq,)+)? + { + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&self.$field1, &other.$field1) + $(&& PartialEq::eq(&self.$field2, &other.$field2))* + } + } + + impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? + $(where $($type: core::hash::Hash,)+)? + { + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&self.$field1, state); + $(core::hash::Hash::hash(&self.$field2, state);)* + } + } + }; + (tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? + $(where $($type: core::fmt::Debug,)+)? + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("$name") + .field(&self.$field1) + $(.field(&self.$field2))* + .finish() + } + } + + impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)? + $(where $($type: Eq,)+)? + {} + + impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)? + $(where $($type: PartialEq,)+)? + { + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&self.$field1, &other.$field1) + $(&& PartialEq::eq(&self.$field2, &other.$field2))* + } + } + + impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? + $(where $($type: core::hash::Hash,)+)? + { + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&self.$field1, state); + $(core::hash::Hash::hash(&self.$field2, state);)* + } + } + }; +} + +macro_rules! impl_clone_for { + (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)? + $(where $($type: Clone,)+)? + { + fn clone(&self) -> Self { + Self { + $field1: self.$field1.clone(), + $($field2: self.$field2.clone(),)* + } + } + } + }; + (tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)? + $(where $($type: Clone,)+)? + { + fn clone(&self) -> Self { + Self( + self.$field1.clone(), + $(self.$field2.clone(),)* + ) + } + } + }; +} + +macro_rules! impl_zeroize_on_drop_for { + (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl$(<$($gen$(: $bound)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)? + { + fn zeroize(&mut self) { + self.$field1.zeroize(); + $(self.$field2.zeroize();)* + } + } + + impl$(<$($gen$(: $bound)?),+>)? Drop for $name$(<$($gen),+>)? + { + fn drop(&mut self) { + #[allow(unused_imports)] + use zeroize::Zeroize; + self.$field1.zeroize(); + $(self.$field2.zeroize();)* + } + } + }; +} + +/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. +macro_rules! impl_serialize_and_deserialize_for { + ($t:ident) => { + #[cfg(feature = "serialize")] + impl serde::Serialize for $t { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(&base64::encode(&self.serialize())) + } else { + serializer.serialize_bytes(&self.serialize()) + } + } + } + + #[cfg(feature = "serialize")] + impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = <&str>::deserialize(deserializer)?; + $t::::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?) + .map_err(serde::de::Error::custom) + } else { + struct ByteVisitor { + marker: core::marker::PhantomData, + } + impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor { + type Value = $t; + fn expecting( + &self, + formatter: &mut core::fmt::Formatter, + ) -> core::fmt::Result { + formatter.write_str(core::concat!( + "the byte representation of a ", + core::stringify!($t) + )) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + $t::::deserialize(value).map_err(|_| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Bytes(value), + &core::concat!( + "invalid byte sequence for ", + core::stringify!($t) + ), + ) + }) + } + } + deserializer.deserialize_bytes(ByteVisitor:: { + marker: core::marker::PhantomData, + }) + } + } + } + }; +} + +// Convenience macro for implementing all of the above traits +macro_rules! impl_traits_for { + (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { + impl_debug_eq_hash_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?); + impl_clone_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?); + impl_zeroize_on_drop_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?); + impl_serialize_and_deserialize_for!($name); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9341829..dedb75a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -459,6 +459,8 @@ //! //! # Features //! +//! - The `p256` feature enables using p256 as the underlying group for the [Ciphersuite] choice +//! //! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with //! [serde](https://serde.rs/). //! @@ -472,9 +474,10 @@ extern crate alloc; +#[macro_use] +mod impls; #[macro_use] mod serialization; - mod ciphersuite; pub mod errors; pub mod group; diff --git a/src/serialization.rs b/src/serialization.rs index 8c4539d..b90bd26 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -20,80 +20,11 @@ use crate::{ use alloc::vec::Vec; use generic_array::{typenum::Unsigned, GenericArray}; -/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. -macro_rules! impl_serialize_and_deserialize_for { - ($t:ident) => { - #[cfg(feature = "serialize")] - impl serde::Serialize for $t { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if serializer.is_human_readable() { - serializer.serialize_str(&base64::encode(&self.serialize())) - } else { - serializer.serialize_bytes(&self.serialize()) - } - } - } - - #[cfg(feature = "serialize")] - impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - if deserializer.is_human_readable() { - let s = <&str>::deserialize(deserializer)?; - $t::::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?) - .map_err(serde::de::Error::custom) - } else { - struct ByteVisitor { - marker: core::marker::PhantomData, - } - impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor { - type Value = $t; - fn expecting( - &self, - formatter: &mut core::fmt::Formatter, - ) -> core::fmt::Result { - formatter.write_str(core::concat!( - "the byte representation of a ", - core::stringify!($t) - )) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: serde::de::Error, - { - $t::::deserialize(value).map_err(|_| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Bytes(value), - &core::concat!( - "invalid byte sequence for ", - core::stringify!($t) - ), - ) - }) - } - } - deserializer.deserialize_bytes(ByteVisitor:: { - marker: core::marker::PhantomData, - }) - } - } - } - }; -} - ////////////////////////////////////////////////////////// // Serialization and Deserialization for High-Level API // // ==================================================== // ////////////////////////////////////////////////////////// -impl_serialize_and_deserialize_for!(NonVerifiableClient); - impl NonVerifiableClient { /// Serialization into bytes pub fn serialize(&self) -> Vec { @@ -118,8 +49,6 @@ impl NonVerifiableClient { } } -impl_serialize_and_deserialize_for!(VerifiableClient); - impl VerifiableClient { /// Serialization into bytes pub fn serialize(&self) -> Vec { @@ -153,8 +82,6 @@ impl VerifiableClient { } } -impl_serialize_and_deserialize_for!(NonVerifiableServer); - impl NonVerifiableServer { /// Serialization into bytes pub fn serialize(&self) -> Vec { @@ -174,8 +101,6 @@ impl NonVerifiableServer { } } -impl_serialize_and_deserialize_for!(VerifiableServer); - impl VerifiableServer { /// Serialization into bytes pub fn serialize(&self) -> Vec { @@ -201,8 +126,6 @@ impl VerifiableServer { } } -impl_serialize_and_deserialize_for!(Proof); - impl Proof { /// Serialization into bytes pub fn serialize(&self) -> Vec { @@ -226,35 +149,31 @@ impl Proof { } } -impl_serialize_and_deserialize_for!(BlindedElement); - impl BlindedElement { /// Serialization into bytes pub fn serialize(&self) -> Vec { - self.0.to_arr().to_vec() + self.value.to_arr().to_vec() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - Ok(Self(CS::Group::from_element_slice( - GenericArray::from_slice(input), - )?)) + Ok(Self { + value: CS::Group::from_element_slice(GenericArray::from_slice(input))?, + }) } } -impl_serialize_and_deserialize_for!(EvaluationElement); - impl EvaluationElement { /// Serialization into bytes pub fn serialize(&self) -> Vec { - self.0.to_arr().to_vec() + self.value.to_arr().to_vec() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - Ok(Self(CS::Group::from_element_slice( - GenericArray::from_slice(input), - )?)) + Ok(Self { + value: CS::Group::from_element_slice(GenericArray::from_slice(input))?, + }) } } diff --git a/src/voprf.rs b/src/voprf.rs index e42fe82..546fb28 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -13,13 +13,12 @@ use crate::{ group::Group, serialization::{i2osp, serialize}, }; +use alloc::vec; +use alloc::vec::Vec; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use rand::{CryptoRng, RngCore}; -use alloc::vec; -use alloc::vec::Vec; - /////////////// // Constants // // ========= // @@ -53,6 +52,11 @@ pub struct NonVerifiableClient { pub(crate) blind: ::Scalar, pub(crate) data: Vec, } +impl_traits_for!( + struct NonVerifiableClient, + [blind, data], + [::Scalar], +); /// A client which engages with a [VerifiableServer] /// in verifiable mode, meaning that the OPRF outputs @@ -62,6 +66,11 @@ pub struct VerifiableClient { pub(crate) blinded_element: CS::Group, pub(crate) data: alloc::vec::Vec, } +impl_traits_for!( + struct VerifiableClient, + [blind, blinded_element, data], + [::Scalar, CS::Group], +); /// A server which engages with a [NonVerifiableClient] /// in base mode, meaning that the OPRF outputs are not @@ -69,6 +78,12 @@ pub struct VerifiableClient { pub struct NonVerifiableServer { pub(crate) sk: ::Scalar, } +impl_traits_for!( + struct NonVerifiableServer, + [sk], + [::Scalar], +); + /// A server which engages with a [VerifiableClient] /// in verifiable mode, meaning that the OPRF outputs /// can be checked against a server public key. @@ -76,6 +91,11 @@ pub struct VerifiableServer { pub(crate) sk: ::Scalar, pub(crate) pk: CS::Group, } +impl_traits_for!( + struct VerifiableServer, + [sk, pk], + [::Scalar, CS::Group], +); /// A proof produced by a [VerifiableServer] that /// the OPRF output matches against a server public key. @@ -83,15 +103,34 @@ pub struct Proof { pub(crate) c_scalar: ::Scalar, pub(crate) s_scalar: ::Scalar, } +impl_traits_for!( + struct Proof, + [c_scalar, s_scalar], + [::Scalar], +); /// The first client message sent from a client (either verifiable or not) /// to a server (either verifiable or not). -pub struct BlindedElement(pub(crate) CS::Group); +pub struct BlindedElement { + pub(crate) value: CS::Group, +} +impl_traits_for!( + struct BlindedElement, + [value], + [CS::Group], +); /// The server's response to the [BlindedElement] message from /// a client (either verifiable or not) /// to a server (either verifiable or not). -pub struct EvaluationElement(pub(crate) CS::Group); +pub struct EvaluationElement { + pub(crate) value: CS::Group, +} +impl_traits_for!( + struct EvaluationElement, + [value], + [CS::Group], +); ///////////////////////// // API Implementations // @@ -110,7 +149,9 @@ impl NonVerifiableClient { data: input.to_vec(), blind, }, - message: BlindedElement(blinded_element), + message: BlindedElement { + value: blinded_element, + }, }) } @@ -122,7 +163,7 @@ impl NonVerifiableClient { metadata: &Metadata, ) -> Result, InternalError> { let unblinded_element = - evaluation_element.0 * &::scalar_invert(&self.blind); + evaluation_element.value * &::scalar_invert(&self.blind); let outputs = finalize_after_unblind::( &[(self.data.clone(), unblinded_element)], &metadata.0, @@ -163,7 +204,9 @@ impl VerifiableClient { blind, blinded_element, }, - message: BlindedElement(blinded_element), + message: BlindedElement { + value: blinded_element, + }, }) } @@ -199,7 +242,9 @@ impl VerifiableClient { .map(|(client, evaluation_element)| BatchItems { blind: client.blind, evaluation_element: evaluation_element.clone(), - blinded_element: BlindedElement(client.blinded_element), + blinded_element: BlindedElement { + value: client.blinded_element, + }, }) .collect(); @@ -289,9 +334,11 @@ impl NonVerifiableServer { let dst = [STR_HASH_TO_SCALAR, &get_context_string::(Mode::Base)?].concat(); let m = CS::Group::hash_to_scalar::(&context, &dst)?; let t = self.sk + &m; - let evaluation_element = blinded_element.0 * &CS::Group::scalar_invert(&t); + let evaluation_element = blinded_element.value * &CS::Group::scalar_invert(&t); Ok(NonVerifiableServerEvaluateResult { - message: EvaluationElement(evaluation_element), + message: EvaluationElement { + value: evaluation_element, + }, }) } } @@ -370,7 +417,9 @@ impl VerifiableServer { let t = self.sk + &m; let evaluation_elements: Vec> = blinded_elements .iter() - .map(|x| EvaluationElement(x.0 * &CS::Group::scalar_invert(&t))) + .map(|x| EvaluationElement { + value: x.value * &CS::Group::scalar_invert(&t), + }) .collect(); let g = CS::Group::base_point(); @@ -499,28 +548,6 @@ struct BatchItems { blinded_element: BlindedElement, } -impl Clone for BlindedElement { - fn clone(&self) -> Self { - Self(self.0) - } -} - -impl Clone for EvaluationElement { - fn clone(&self) -> Self { - Self(self.0) - } -} - -impl Clone for VerifiableClient { - fn clone(&self) -> Self { - Self { - data: self.data.clone(), - blind: self.blind, - blinded_element: self.blinded_element, - } - } -} - // Inner function for blind. Returns the blind scalar and the blinded element fn blind( input: &[u8], @@ -574,7 +601,7 @@ fn verifiable_unblind( let unblinded_elements = blinds .iter() .zip(evaluation_elements.iter()) - .map(|(&blind, x)| x.0 * &CS::Group::scalar_invert(&blind)) + .map(|(&blind, x)| x.value * &CS::Group::scalar_invert(&blind)) .collect(); Ok(unblinded_elements) } @@ -705,8 +732,8 @@ fn compute_composites( let h2_input = [ serialize(&seed, 2)?, i2osp(i, 2)?, - serialize(&c_slice[i].0.to_arr().to_vec(), 2)?, - serialize(&d_slice[i].0.to_arr().to_vec(), 2)?, + serialize(&c_slice[i].value.to_arr().to_vec(), 2)?, + serialize(&d_slice[i].value.to_arr().to_vec(), 2)?, serialize(&composite_dst, 2)?, ] .concat(); @@ -716,10 +743,10 @@ fn compute_composites( ] .concat(); let di = CS::Group::hash_to_scalar::(&h2_input, &dst)?; - m = c_slice[i].0 * &di + &m; + m = c_slice[i].value * &di + &m; z = match k_option { Some(_) => z, - None => d_slice[i].0 * &di + &z, + None => d_slice[i].value * &di + &z, }; } @@ -746,71 +773,55 @@ fn get_context_string(mode: Mode) -> Result // Tests // // ===== // /////////// + #[cfg(test)] mod tests { use super::*; use crate::group::Group; - use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::{arr, GenericArray}; use rand::rngs::OsRng; - use sha2::Sha512; - struct Ristretto255Sha512; - impl CipherSuite for Ristretto255Sha512 { - type Group = RistrettoPoint; - type Hash = Sha512; - } - - fn prf( + fn prf( input: &[u8], oprf_key: &[u8], info: &[u8], - ) -> GenericArray::OutputSize> { + ) -> GenericArray::OutputSize> { let dst = [ STR_HASH_TO_GROUP, - &get_context_string::(Mode::Base).unwrap(), + &get_context_string::(Mode::Base).unwrap(), ] .concat(); - let point = RistrettoPoint::hash_to_curve::(input, &dst).unwrap(); - let scalar = - RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); + let point = CS::Group::hash_to_curve::(input, &dst).unwrap(); + let scalar = CS::Group::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); let context = [ STR_CONTEXT, - &get_context_string::(Mode::Base).unwrap(), + &get_context_string::(Mode::Base).unwrap(), &serialize(info, 2).unwrap(), ] .concat(); let dst = [ STR_HASH_TO_SCALAR, - &get_context_string::(Mode::Base).unwrap(), + &get_context_string::(Mode::Base).unwrap(), ] .concat(); - let m = <::Group as Group>::hash_to_scalar::< - ::Hash, - >(&context, &dst) - .unwrap(); + let m = ::hash_to_scalar::(&context, &dst).unwrap(); - let res = point - * &<::Group as Group>::scalar_invert(&(scalar + m)); + let res = point * &::scalar_invert(&(scalar + &m)); - finalize_after_unblind::(&[(input.to_vec(), res)], info, Mode::Base) - .unwrap()[0] + finalize_after_unblind::(&[(input.to_vec(), res)], info, Mode::Base).unwrap()[0].clone() } - #[test] - fn oprf_retrieval() { + fn oprf_retrieval() { let input = b"hunter2"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - NonVerifiableClient::::blind(&input[..], &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(&input[..], &mut rng).unwrap(); let oprf_key_bytes = arr![ u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ]; - let server = - NonVerifiableServer::::new_with_key(&oprf_key_bytes).unwrap(); + let server = NonVerifiableServer::::new_with_key(&oprf_key_bytes).unwrap(); let server_result = server .evaluate(client_blind_result.message, &Metadata(info.to_vec())) .unwrap(); @@ -818,39 +829,54 @@ mod tests { .state .finalize(server_result.message, &Metadata(info.to_vec())) .unwrap(); - let res2 = prf(&input[..], &oprf_key_bytes, info); + let res2 = prf::(&input[..], &oprf_key_bytes, info); assert_eq!(client_finalize_result.output, res2); } - #[test] - fn oprf_inversion_unsalted() { + fn oprf_inversion_unsalted() { let mut rng = OsRng; let mut input = alloc::vec![0u8; 64]; rng.fill_bytes(&mut input); let info = b"info"; - let client_blind_result = - NonVerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); let client_finalize_result = client_blind_result .state .finalize( - EvaluationElement(client_blind_result.message.0), + EvaluationElement { + value: client_blind_result.message.value, + }, &Metadata(info.to_vec()), ) .unwrap(); let dst = [ STR_HASH_TO_GROUP, - &get_context_string::(Mode::Base).unwrap(), + &get_context_string::(Mode::Base).unwrap(), ] .concat(); - let point = RistrettoPoint::hash_to_curve::(&input, &dst).unwrap(); - let res2 = finalize_after_unblind::( - &[(input.to_vec(), point)], - info, - Mode::Base, - ) - .unwrap()[0]; + let point = CS::Group::hash_to_curve::(&input, &dst).unwrap(); + let res2 = finalize_after_unblind::(&[(input.to_vec(), point)], info, Mode::Base) + .unwrap()[0] + .clone(); assert_eq!(client_finalize_result.output, res2); } + + #[test] + fn test_functionality() -> Result<(), InternalError> { + use crate::tests::Ristretto255Sha512; + + oprf_retrieval::(); + oprf_inversion_unsalted::(); + + #[cfg(feature = "p256")] + { + use crate::tests::P256Sha256; + + oprf_retrieval::(); + oprf_inversion_unsalted::(); + } + + Ok(()) + } }