Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@
use displaydoc::Display;

/// [`Result`](core::result::Result) shorthand that uses [`Error`].
pub type Result<T> = core::result::Result<T, Error>;
pub type Result<T, E = Error> = core::result::Result<T, E>;

/// Represents an error in the manipulation of internal cryptographic data
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Error {
/// Could not parse byte sequence for key
InvalidByteSequence,
/// Could not deserialize element, or deserialized to the identity element
PointError,
/// Computing the hash-to-curve function failed
HashToCurveError,
/// Failure to serialize or deserialize bytes
SerializationError,
/// Use of incompatible modes (base vs. verifiable)
IncompatibleModeError,
/**
* Internal error thrown when different-lengthed slices are supplied
* to the compute_composites() function.
*/
MismatchedLengthsForCompositeInputs,
/// Size of input is empty or longer then [`u16::MAX`].
Input,
/// Size of metadata is longer then `u16::MAX - 21`.
Metadata,
/// Failure to deserialize bytes
Deserialization,
/// Batched items are more then [`u16::MAX`] or length don't match.
Batch,
/// In verifiable mode, occurs when the proof failed to verify
ProofVerificationError,
/// Encountered insufficient bytes when attempting to deserialize
SizeError,
/// Encountered an invalid scalar
ScalarError,
ProofVerification,
/// Size of seed is longer then [`u16::MAX`].
Seed,
}

/// Only used to implement [`Group`](crate::Group).
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum InternalError {
/// Size of input is empty or longer then [`u16::MAX`].
Input,
/// `input` is longer then [`u16::MAX`].
I2osp,
}

#[cfg(feature = "std")]
Expand Down
21 changes: 14 additions & 7 deletions src/group/elliptic_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rand_core::{CryptoRng, RngCore};
use super::Group;
use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR};
use crate::voprf::{self, Mode};
use crate::{CipherSuite, Error, Result};
use crate::{CipherSuite, Error, InternalError, Result};

impl<C> Group for C
where
Expand All @@ -41,19 +41,26 @@ where

// Implements the `hash_to_curve()` function from
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
fn hash_to_curve<CS: CipherSuite>(msg: &[&[u8]], mode: Mode) -> Result<Self::Elem>
fn hash_to_curve<CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst =
GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::<CS>(mode));

Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(msg, &dst).map_err(|_| Error::PointError)
Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(input, &dst)
.map_err(|_| InternalError::Input)
}

// Implements the `HashToScalar()` function
fn hash_to_scalar<CS: CipherSuite>(input: &[&[u8]], mode: Mode) -> Result<Self::Scalar>
fn hash_to_scalar<CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
Expand All @@ -62,7 +69,7 @@ where
GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::<CS>(mode));

<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<CS::Hash>>(input, &dst)
.map_err(|_| Error::PointError)
.map_err(|_| InternalError::Input)
}

fn base_elem() -> Self::Elem {
Expand All @@ -85,7 +92,7 @@ where
fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem> {
PublicKey::<Self>::from_sec1_bytes(element_bits)
.map(|public_key| public_key.to_projective())
.map_err(|_| Error::PointError)
.map_err(|_| Error::Deserialization)
}

fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Expand All @@ -108,6 +115,6 @@ where
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar> {
SecretKey::<Self>::from_be_bytes(scalar_bits)
.map(|secret_key| *secret_key.to_nonzero_scalar())
.map_err(|_| Error::ScalarError)
.map_err(|_| Error::Deserialization)
}
}
30 changes: 26 additions & 4 deletions src/group/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use subtle::ConstantTimeEq;
use zeroize::Zeroize;

use crate::voprf::Mode;
use crate::{CipherSuite, Result};
use crate::{CipherSuite, InternalError, Result};

pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
Expand Down Expand Up @@ -52,14 +52,28 @@ pub trait Group {
/// The byte length necessary to represent scalars
type ScalarLen: ArrayLength<u8> + 'static;

/// transforms a password and domain separation tag (DST) into a curve point
fn hash_to_curve<CS: CipherSuite>(msg: &[&[u8]], mode: Mode) -> Result<Self::Elem>
/// Transforms a password and domain separation tag (DST) into a curve point
///
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
fn hash_to_curve<CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;

/// Hashes a slice of pseudo-random bytes to a scalar
fn hash_to_scalar<CS: CipherSuite>(input: &[&[u8]], mode: Mode) -> Result<Self::Scalar>
///
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
fn hash_to_scalar<CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
Expand All @@ -75,6 +89,10 @@ pub trait Group {

/// Return an element from its fixed-length bytes representation. If the
/// element is the identity element, return an error.
///
/// # Errors
/// [`Error::Deserialization`](crate::Error::Deserialization) if the element
/// is not a valid point on the group or the identity element.
fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem>;

/// picks a scalar at random
Expand All @@ -92,6 +110,10 @@ pub trait Group {

/// Return a scalar from its fixed-length bytes representation. If the
/// scalar is zero or invalid, then return an error.
///
/// # Errors
/// [`Error::Deserialization`](crate::Error::Deserialization) if the scalar
/// is not a valid point on the group or zero.
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar>;
}

Expand Down
22 changes: 14 additions & 8 deletions src/group/ristretto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rand_core::{CryptoRng, RngCore};

use super::{Group, STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR};
use crate::voprf::{self, Mode};
use crate::{CipherSuite, Error, Result};
use crate::{CipherSuite, Error, InternalError, Result};

/// [`Group`] implementation for Ristretto255.
pub struct Ristretto255;
Expand All @@ -46,7 +46,10 @@ impl Group for Ristretto255 {

// Implements the `hash_to_ristretto255()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
fn hash_to_curve<CS: CipherSuite>(msg: &[&[u8]], mode: Mode) -> Result<Self::Elem>
fn hash_to_curve<CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
Expand All @@ -55,16 +58,19 @@ impl Group for Ristretto255 {
GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::<Self>(mode));

let mut uniform_bytes = GenericArray::<_, U64>::default();
ExpandMsgXmd::<CS::Hash>::expand_message(msg, &dst, 64)
.map_err(|_| Error::PointError)?
ExpandMsgXmd::<CS::Hash>::expand_message(input, &dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);

Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
}

// Implements the `HashToScalar()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
fn hash_to_scalar<'a, CS: CipherSuite>(input: &[&[u8]], mode: Mode) -> Result<Self::Scalar>
fn hash_to_scalar<'a, CS: CipherSuite>(
input: &[&[u8]],
mode: Mode,
) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
Expand All @@ -74,7 +80,7 @@ impl Group for Ristretto255 {

let mut uniform_bytes = GenericArray::<_, U64>::default();
ExpandMsgXmd::<CS::Hash>::expand_message(input, &dst, 64)
.map_err(|_| Error::PointError)?
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);

Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into()))
Expand All @@ -97,7 +103,7 @@ impl Group for Ristretto255 {
CompressedRistretto::from_slice(element_bits)
.decompress()
.filter(|point| point != &RistrettoPoint::identity())
.ok_or(Error::PointError)
.ok_or(Error::Deserialization)
}

fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Expand Down Expand Up @@ -130,6 +136,6 @@ impl Group for Ristretto255 {
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar> {
Scalar::from_canonical_bytes((*scalar_bits).into())
.filter(|scalar| scalar != &Scalar::zero())
.ok_or(Error::ScalarError)
.ok_or(Error::Deserialization)
}
}
4 changes: 2 additions & 2 deletions src/group/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn test_group_properties() -> Result<()> {
fn test_identity_element_error<G: Group>() -> Result<()> {
let identity = G::identity_elem();
let result = G::deserialize_elem(&G::serialize_elem(identity));
assert!(matches!(result, Err(Error::PointError)));
assert!(matches!(result, Err(Error::Deserialization)));

Ok(())
}
Expand All @@ -43,7 +43,7 @@ fn test_identity_element_error<G: Group>() -> Result<()> {
fn test_zero_scalar_error<G: Group>() -> Result<()> {
let zero_scalar = G::zero_scalar();
let result = G::deserialize_scalar(&G::serialize_scalar(zero_scalar));
assert!(matches!(result, Err(Error::ScalarError)));
assert!(matches!(result, Err(Error::Deserialization)));

Ok(())
}
31 changes: 11 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@
//! use voprf::NonVerifiableServer;
//!
//! let mut server_rng = OsRng;
//! let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng)
//! .expect("Unable to construct server");
//! let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng);
//! ```
//!
//! ### Client Blinding
Expand Down Expand Up @@ -108,8 +107,7 @@
//! # ).expect("Unable to construct client");
//! # use voprf::NonVerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng);
//! let server_evaluate_result = server
//! .evaluate(&client_blind_result.message, None)
//! .expect("Unable to perform server evaluate");
Expand All @@ -136,8 +134,7 @@
//! # ).expect("Unable to construct client");
//! # use voprf::NonVerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng);
//! # let server_evaluate_result = server.evaluate(
//! # &client_blind_result.message,
//! # None,
Expand Down Expand Up @@ -178,8 +175,7 @@
//! use voprf::VerifiableServer;
//!
//! let mut server_rng = OsRng;
//! let server =
//! VerifiableServer::<CipherSuite>::new(&mut server_rng).expect("Unable to construct server");
//! let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//!
//! // To be sent to the client
//! println!("Server public key: {:?}", server.get_public_key());
Expand Down Expand Up @@ -234,8 +230,7 @@
//! # ).expect("Unable to construct client");
//! # use voprf::VerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//! let server_evaluate_result = server
//! .evaluate(&mut server_rng, &client_blind_result.message, None)
//! .expect("Unable to perform server evaluate");
Expand Down Expand Up @@ -263,8 +258,7 @@
//! # ).expect("Unable to construct client");
//! # use voprf::VerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//! # let server_evaluate_result = server.evaluate(
//! # &mut server_rng,
//! # &client_blind_result.message,
Expand Down Expand Up @@ -346,8 +340,7 @@
//! # }
//! # use voprf::VerifiableServer;
//! let mut server_rng = OsRng;
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//! let VerifiableServerBatchEvaluatePrepareResult {
//! prepared_evaluation_elements,
//! t,
Expand Down Expand Up @@ -385,8 +378,7 @@
//! # }
//! # use voprf::VerifiableServer;
//! let mut server_rng = OsRng;
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//! let VerifiableServerBatchEvaluateResult { messages, proof } = server
//! .batch_evaluate(&mut server_rng, &client_messages, None)
//! .expect("Unable to perform server batch evaluate");
Expand Down Expand Up @@ -420,8 +412,7 @@
//! # }
//! # use voprf::VerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server = VerifiableServer::<CipherSuite>::new(&mut server_rng);
//! # let VerifiableServerBatchEvaluateResult { messages, proof } = server
//! # .batch_evaluate(&mut server_rng, &client_messages, None)
//! # .expect("Unable to perform server batch evaluate");
Expand Down Expand Up @@ -488,7 +479,7 @@

#![deny(unsafe_code)]
#![no_std]
#![warn(clippy::cargo, missing_docs)]
#![warn(clippy::cargo, clippy::missing_errors_doc, missing_docs)]
#![allow(clippy::multiple_crate_versions)]

#[cfg(any(feature = "alloc", test))]
Expand All @@ -510,7 +501,7 @@ mod tests;
// Exports

pub use crate::ciphersuite::CipherSuite;
pub use crate::error::{Error, Result};
pub use crate::error::{Error, InternalError, Result};
pub use crate::group::Group;
#[cfg(feature = "ristretto255")]
pub use crate::group::Ristretto255;
Expand Down
Loading