-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes needed for frost DKG / threshold signatures to verify onchain. (#…
…471)
- Loading branch information
Showing
16 changed files
with
409 additions
and
58 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
use core::{ | ||
fmt::Debug, | ||
hash::{Hash, Hasher}, | ||
}; | ||
|
||
use crate::{ | ||
error::{Error, FieldError}, | ||
serialization::ScalarSerialization, | ||
traits::{Ciphersuite, Field, Group, Scalar}, | ||
util::scalar_is_valid, | ||
}; | ||
|
||
#[derive(Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize)] | ||
#[serde(bound = "C: Ciphersuite")] | ||
#[serde(try_from = "ScalarSerialization<C>")] | ||
#[serde(into = "ScalarSerialization<C>")] | ||
pub struct Identifier<C: Ciphersuite>(Scalar<C>); | ||
|
||
impl<C> Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
/// Create a new Identifier from a scalar. For internal use only. | ||
fn new(scalar: Scalar<C>) -> Result<Self, Error> { | ||
if scalar == <<C::Group as Group>::Field>::zero() { | ||
Err(FieldError::InvalidZeroScalar.into()) | ||
} else { | ||
Ok(Self(scalar)) | ||
} | ||
} | ||
|
||
/// Derive an Identifier from an arbitrary byte string. | ||
/// | ||
/// This feature is not part of the specification and is just a convenient | ||
/// way of creating identifiers. | ||
/// | ||
/// Each possible byte string will map to an uniformly random identifier. | ||
/// Returns an error if the ciphersuite does not support identifier derivation, | ||
/// or if the mapped identifier is zero (which is unpredictable, but should happen | ||
/// with negligible probability). | ||
pub fn derive(s: &[u8]) -> Result<Self, Error> { | ||
let scalar = C::HID(s).ok_or(Error::IdentifierDerivationNotSupported)?; | ||
Self::new(scalar) | ||
} | ||
|
||
/// Serialize the identifier using the ciphersuite encoding. | ||
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization { | ||
<<C::Group as Group>::Field>::serialize(&self.0) | ||
} | ||
|
||
/// Deserialize an Identifier from a serialized buffer. | ||
/// Returns an error if it attempts to deserialize zero. | ||
pub fn deserialize( | ||
buf: &<<C::Group as Group>::Field as Field>::Serialization, | ||
) -> Result<Self, Error> { | ||
let scalar = <<C::Group as Group>::Field>::deserialize(buf)?; | ||
Self::new(scalar) | ||
} | ||
|
||
/// Check if the identifier is valid aka not zero | ||
pub fn is_valid(&self) -> bool { | ||
scalar_is_valid::<C>(&self.0) | ||
} | ||
} | ||
|
||
impl<C> TryFrom<ScalarSerialization<C>> for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
type Error = Error; | ||
|
||
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> { | ||
Self::deserialize(&value.0) | ||
} | ||
} | ||
|
||
impl<C> From<Identifier<C>> for ScalarSerialization<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn from(value: Identifier<C>) -> Self { | ||
Self(value.serialize()) | ||
} | ||
} | ||
|
||
impl<C> Eq for Identifier<C> where C: Ciphersuite {} | ||
|
||
impl<C> Debug for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
f.debug_tuple("Identifier") | ||
.field(&hex::encode(<<C::Group as Group>::Field>::serialize(&self.0).as_ref())) | ||
.finish() | ||
} | ||
} | ||
|
||
#[allow(clippy::derived_hash_with_manual_eq)] | ||
impl<C> Hash for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
<<C::Group as Group>::Field>::serialize(&self.0).as_ref().hash(state) | ||
} | ||
} | ||
|
||
impl<C> Ord for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
let serialized_self = <<C::Group as Group>::Field>::little_endian_serialize(&self.0); | ||
let serialized_other = <<C::Group as Group>::Field>::little_endian_serialize(&other.0); | ||
// The default cmp uses lexicographic order; so we need the elements in big endian | ||
serialized_self | ||
.as_ref() | ||
.iter() | ||
.rev() | ||
.cmp(serialized_other.as_ref().iter().rev()) | ||
} | ||
} | ||
|
||
impl<C> PartialOrd for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl<C> core::ops::Mul<Scalar<C>> for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
type Output = Scalar<C>; | ||
|
||
fn mul(self, scalar: Scalar<C>) -> Scalar<C> { | ||
self.0 * scalar | ||
} | ||
} | ||
|
||
impl<C> core::ops::MulAssign<Identifier<C>> for Scalar<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn mul_assign(&mut self, identifier: Identifier<C>) { | ||
*self = *self * identifier.0 | ||
} | ||
} | ||
|
||
impl<C> core::ops::Sub for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
type Output = Self; | ||
|
||
fn sub(self, rhs: Identifier<C>) -> Self::Output { | ||
Self(self.0 - rhs.0) | ||
} | ||
} | ||
|
||
impl<C> TryFrom<u16> for Identifier<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
type Error = Error; | ||
|
||
fn try_from(n: u16) -> Result<Identifier<C>, Self::Error> { | ||
if n == 0 { | ||
Err(FieldError::InvalidZeroScalar.into()) | ||
} else { | ||
// Classic left-to-right double-and-add algorithm that skips the first bit 1 (since | ||
// identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too. | ||
let one = <<C::Group as Group>::Field>::one(); | ||
let mut sum = <<C::Group as Group>::Field>::one(); | ||
|
||
let bits = (n.to_be_bytes().len() as u32) * 8; | ||
for i in (0..(bits - n.leading_zeros() - 1)).rev() { | ||
sum = sum + sum; | ||
if n & (1 << i) != 0 { | ||
sum = sum + one; | ||
} | ||
} | ||
Ok(Self(sum)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use core::fmt::Debug; | ||
|
||
use crate::{ | ||
error::Error, | ||
identifier::Identifier, | ||
serialization::{Deserialize, ElementSerialization, Serialize}, | ||
traits::{Ciphersuite, Element, Group}, | ||
util::element_is_valid, | ||
verifying_key::VerifyingKey, | ||
Header, | ||
}; | ||
use alloc::collections::BTreeMap; | ||
use sp_std::vec::Vec; | ||
|
||
/// A public group element that represents a single signer's public verification share. | ||
#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] | ||
#[serde(bound = "C: Ciphersuite")] | ||
#[serde(try_from = "ElementSerialization<C>")] | ||
#[serde(into = "ElementSerialization<C>")] | ||
pub struct VerifyingShare<C>(pub(super) Element<C>) | ||
where | ||
C: Ciphersuite; | ||
|
||
impl<C> VerifyingShare<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
/// Create a new [`VerifyingShare`] from a element. | ||
pub fn new(element: Element<C>) -> Self { | ||
Self(element) | ||
} | ||
|
||
/// Get the inner element. | ||
#[cfg(feature = "internals")] | ||
pub fn to_element(&self) -> Element<C> { | ||
self.0 | ||
} | ||
|
||
/// Deserialize from bytes | ||
pub fn deserialize(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error> { | ||
<C::Group as Group>::deserialize(&bytes) | ||
.map(|element| Self(element)) | ||
.map_err(|e| e.into()) | ||
} | ||
|
||
/// Serialize to bytes | ||
pub fn serialize(&self) -> <C::Group as Group>::Serialization { | ||
<C::Group as Group>::serialize(&self.0) | ||
} | ||
|
||
/// Verifies that a verifying share is valid aka not zero or the base point | ||
pub fn is_valid(&self) -> bool { | ||
element_is_valid::<C>(&self.0) | ||
} | ||
} | ||
|
||
impl<C> Debug for VerifyingShare<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
f.debug_tuple("VerifyingShare").field(&hex::encode(self.serialize())).finish() | ||
} | ||
} | ||
|
||
impl<C> TryFrom<ElementSerialization<C>> for VerifyingShare<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
type Error = Error; | ||
|
||
fn try_from(value: ElementSerialization<C>) -> Result<Self, Self::Error> { | ||
Self::deserialize(value.0) | ||
} | ||
} | ||
|
||
impl<C> From<VerifyingShare<C>> for ElementSerialization<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
fn from(value: VerifyingShare<C>) -> Self { | ||
Self(value.serialize()) | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] | ||
#[serde(bound = "C: Ciphersuite")] | ||
#[serde(deny_unknown_fields)] | ||
pub struct PublicKeyPackage<C: Ciphersuite> { | ||
/// Serialization header | ||
pub header: Header<C>, | ||
/// The verifying shares for all participants. Used to validate signature | ||
/// shares they generate. | ||
pub verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>>, | ||
/// The joint public key for the entire group. | ||
pub verifying_key: VerifyingKey<C>, | ||
} | ||
|
||
impl<C> PublicKeyPackage<C> | ||
where | ||
C: Ciphersuite, | ||
{ | ||
/// Serialize the struct into a Vec. | ||
pub fn serialize(&self) -> Result<Vec<u8>, Error> { | ||
Serialize::serialize(&self) | ||
} | ||
|
||
/// Deserialize the struct from a slice of bytes. | ||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> { | ||
Deserialize::deserialize(bytes) | ||
} | ||
} | ||
|
||
// Default byte-oriented serialization for structs that need to be communicated. | ||
// | ||
// Note that we still manually implement these methods in each applicable type, | ||
// instead of making these traits `pub` and asking users to import the traits. | ||
// The reason is that ciphersuite traits would need to re-export these traits, | ||
// parametrized with the ciphersuite, but trait aliases are not currently | ||
// supported: <https://github.com/rust-lang/rust/issues/41517> | ||
|
||
#[cfg(feature = "serialization")] | ||
pub(crate) trait Serialize<C: Ciphersuite> { | ||
/// Serialize the struct into a Vec. | ||
fn serialize(&self) -> Result<Vec<u8>, Error>; | ||
} | ||
|
||
#[cfg(feature = "serialization")] | ||
pub(crate) trait Deserialize<C: Ciphersuite> { | ||
/// Deserialize the struct from a slice of bytes. | ||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> | ||
where | ||
Self: std::marker::Sized; | ||
} | ||
|
||
#[cfg(feature = "serialization")] | ||
impl<T: serde::Serialize, C: Ciphersuite> Serialize<C> for T { | ||
fn serialize(&self) -> Result<Vec<u8>, Error> { | ||
postcard::to_stdvec(self).map_err(|_| Error::SerializationError) | ||
} | ||
} | ||
|
||
#[cfg(feature = "serialization")] | ||
impl<T: for<'de> serde::Deserialize<'de>, C: Ciphersuite> Deserialize<C> for T { | ||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> { | ||
postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError) | ||
} | ||
} |
Oops, something went wrong.