use generic_ec::{
errors::{InvalidPoint, InvalidScalar},
Curve, NonZero, Point, Scalar, SecretScalar,
};
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "ciphersuite-bitcoin")]
mod bitcoin;
#[cfg(feature = "ciphersuite-ed25519")]
mod ed25519;
#[cfg(feature = "ciphersuite-secp256k1")]
mod secp256k1;
#[cfg(feature = "ciphersuite-bitcoin")]
pub use bitcoin::Bitcoin;
#[cfg(feature = "ciphersuite-ed25519")]
pub use ed25519::Ed25519;
#[cfg(feature = "ciphersuite-secp256k1")]
pub use secp256k1::Secp256k1;
pub trait Ciphersuite: Sized + Clone + Copy + Eq + core::fmt::Debug {
const NAME: &'static str;
type Curve: Curve;
type Digest: digest::Update + digest::FixedOutput + Clone;
type MultiscalarMul: generic_ec::multiscalar::MultiscalarMul<Self::Curve>;
const IS_TAPROOT: bool = false;
#[cfg(feature = "hd-wallet")]
type HdAlgo: hd_wallet::HdWallet<Self::Curve>;
fn h1(msg: &[&[u8]]) -> Scalar<Self::Curve>;
fn compute_challenge(
group_commitment: &NormalizedPoint<Self, Point<Self::Curve>>,
group_public_key: &NormalizedPoint<Self, NonZero<Point<Self::Curve>>>,
msg: &[u8],
) -> Scalar<Self::Curve>;
fn h3(msg: &[&[u8]]) -> Scalar<Self::Curve>;
fn h4() -> Self::Digest;
fn h5() -> Self::Digest;
type PointBytes: AsRef<[u8]>;
fn serialize_point(point: &Point<Self::Curve>) -> Self::PointBytes;
fn deserialize_point(bytes: &[u8]) -> Result<Point<Self::Curve>, InvalidPoint>;
type ScalarBytes: AsRef<[u8]>;
const SCALAR_SIZE: usize;
fn serialize_scalar(scalar: &Scalar<Self::Curve>) -> Self::ScalarBytes;
fn deserialize_scalar(bytes: &[u8]) -> Result<Scalar<Self::Curve>, InvalidScalar>;
fn deserialize_secret_scalar(bytes: &[u8]) -> Result<SecretScalar<Self::Curve>, InvalidScalar> {
let mut scalar = Self::deserialize_scalar(bytes)?;
Ok(SecretScalar::new(&mut scalar))
}
fn is_normalized(point: &Point<Self::Curve>) -> bool {
let _ = point;
true
}
fn normalize_point<P: AsRef<Point<Self::Curve>> + core::ops::Neg<Output = P>>(
point: P,
) -> NormalizedPoint<Self, P> {
match NormalizedPoint::<Self, P>::try_normalize(point) {
Ok(point) => point,
Err(point) => point,
}
}
type NormalizedPointBytes: AsRef<[u8]>;
const NORMALIZED_POINT_SIZE: usize;
fn serialize_normalized_point<P: AsRef<Point<Self::Curve>>>(
point: &NormalizedPoint<Self, P>,
) -> Self::NormalizedPointBytes;
fn deserialize_normalized_point(
bytes: &[u8],
) -> Result<NormalizedPoint<Self, Point<Self::Curve>>, InvalidPoint>;
}
pub fn generate_nonce<C: Ciphersuite>(
rng: &mut (impl RngCore + CryptoRng),
additional_entropy: impl AdditionalEntropy<C>,
) -> SecretScalar<C::Curve> {
let mut random_bytes = [0u8; 32];
rng.fill_bytes(&mut random_bytes);
let additional_entropy = additional_entropy.to_bytes();
let mut hash = C::h3(&[random_bytes.as_slice(), additional_entropy.as_ref()]);
generic_ec::SecretScalar::new(&mut hash)
}
pub trait AdditionalEntropy<C: Ciphersuite> {
type Bytes<'b>: AsRef<[u8]>
where
Self: 'b;
fn to_bytes(&self) -> Self::Bytes<'_>;
}
impl<C: Ciphersuite<Curve = E>, E: Curve> AdditionalEntropy<C> for crate::KeyShare<E> {
type Bytes<'b> = <SecretScalar<E> as AdditionalEntropy<C>>::Bytes<'b>;
fn to_bytes(&self) -> Self::Bytes<'_> {
AdditionalEntropy::<C>::to_bytes(&self.x)
}
}
impl<C: Ciphersuite<Curve = E>, E: Curve> AdditionalEntropy<C> for generic_ec::Scalar<E> {
type Bytes<'b> = C::ScalarBytes;
fn to_bytes(&self) -> Self::Bytes<'_> {
C::serialize_scalar(self)
}
}
impl<C: Ciphersuite<Curve = E>, E: Curve> AdditionalEntropy<C> for generic_ec::SecretScalar<E> {
type Bytes<'b> = <generic_ec::Scalar<E> as AdditionalEntropy<C>>::Bytes<'b>;
fn to_bytes(&self) -> Self::Bytes<'_> {
AdditionalEntropy::<C>::to_bytes(self.as_ref())
}
}
impl<C: Ciphersuite, T: AdditionalEntropy<C>> AdditionalEntropy<C> for generic_ec::NonZero<T> {
type Bytes<'b>
= <T as AdditionalEntropy<C>>::Bytes<'b>
where
Self: 'b;
fn to_bytes(&self) -> Self::Bytes<'_> {
AdditionalEntropy::<C>::to_bytes(self.as_ref())
}
}
impl<C: Ciphersuite> AdditionalEntropy<C> for [u8] {
type Bytes<'b> = &'b [u8];
fn to_bytes(&self) -> Self::Bytes<'_> {
self
}
}
impl<C: Ciphersuite, const N: usize> AdditionalEntropy<C> for [u8; N] {
type Bytes<'b> = &'b [u8; N];
fn to_bytes(&self) -> Self::Bytes<'_> {
self
}
}
impl<C: Ciphersuite, T: AdditionalEntropy<C>> AdditionalEntropy<C> for &T {
type Bytes<'b>
= <T as AdditionalEntropy<C>>::Bytes<'b>
where
Self: 'b;
fn to_bytes(&self) -> Self::Bytes<'_> {
(*self).to_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NormalizedPoint<C, P>(P, core::marker::PhantomData<C>);
impl<C: Ciphersuite, P: AsRef<Point<C::Curve>>> NormalizedPoint<C, P> {
pub fn to_bytes(&self) -> C::NormalizedPointBytes {
C::serialize_normalized_point(self)
}
}
impl<C: Ciphersuite, P: AsRef<Point<C::Curve>> + core::ops::Neg<Output = P>> NormalizedPoint<C, P> {
pub fn try_normalize(point: P) -> Result<Self, Self> {
if point.as_ref().is_zero() || C::is_normalized(point.as_ref()) {
Ok(Self(point, Default::default()))
} else {
let neg_point = -point;
debug_assert!(C::is_normalized(neg_point.as_ref()));
Err(Self(neg_point, Default::default()))
}
}
}
impl<C: Ciphersuite> NormalizedPoint<C, Point<C::Curve>> {
pub fn into_non_zero(self) -> Option<NormalizedPoint<C, NonZero<Point<C::Curve>>>> {
let point = NonZero::from_point(self.0)?;
Some(NormalizedPoint(point, Default::default()))
}
}
impl<C, P> core::ops::Deref for NormalizedPoint<C, P> {
type Target = P;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<C, P, T> AsRef<T> for NormalizedPoint<C, P>
where
P: AsRef<T>,
{
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
#[cfg(feature = "serde")]
impl<C, P: serde::Serialize> serde::Serialize for NormalizedPoint<C, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
(**self).serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C, P> serde::Deserialize<'de> for NormalizedPoint<C, P>
where
C: Ciphersuite,
P: AsRef<Point<C::Curve>> + serde::Deserialize<'de> + core::ops::Neg<Output = P>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let point = P::deserialize(deserializer)?;
NormalizedPoint::<C, P>::try_normalize(point)
.map_err(|_| <D::Error as serde::de::Error>::custom("point isn't normalized"))
}
}