1#![warn(rustdoc::broken_intra_doc_links)]
17#![warn(rustdoc::bare_urls)]
18#![no_std]
19
20extern crate alloc;
21
22use alloc::{borrow::Cow, collections::BTreeMap};
23use core::marker::PhantomData;
24
25use ark_ec::{models::CurveConfig, CurveGroup, PrimeGroup};
26
27use ark_ff::{fields::Field as ArkField, UniformRand};
28use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
29pub use frost_core::{self as frost, Ciphersuite, Field, FieldError, Group, GroupError};
30use frost_core::{compute_group_commitment, BindingFactorList};
31use mina_curves::pasta::{PallasParameters, ProjectivePallas};
32
33use num_traits::identities::Zero;
34use rand_core::{CryptoRng, RngCore};
35
36pub type Error<M> = frost_core::Error<BluePallas<M>>;
37
38use crate::{
39 hasher::{hash_to_array, hash_to_scalar},
40 negate::NegateY,
41};
42
43pub mod errors;
44pub mod hasher;
45pub mod keys;
46mod negate;
47pub mod signing_utilities;
48
49pub trait ChallengeMessage:
51 Clone + core::fmt::Debug + PartialEq + Eq + Sized + Send + Sync + 'static
52{
53 fn challenge(
54 r: &frost_core::Element<BluePallas<Self>>,
55 verifying_key: &frost_core::VerifyingKey<BluePallas<Self>>,
56 message: &[u8],
57 ) -> Result<frost_core::Challenge<BluePallas<Self>>, frost_core::Error<BluePallas<Self>>>;
58}
59
60#[derive(Clone, Copy)]
62pub struct PallasScalarField;
63
64impl Field for PallasScalarField {
65 type Scalar = <PallasParameters as CurveConfig>::ScalarField;
67 type Serialization = [u8; 32];
68 fn zero() -> Self::Scalar {
69 Self::Scalar::zero()
70 }
71 fn one() -> Self::Scalar {
72 Self::Scalar::ONE
73 }
74 fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
75 <Self::Scalar as ArkField>::inverse(scalar).ok_or(FieldError::InvalidZeroScalar)
76 }
77 fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
78 Self::Scalar::rand(rng)
79 }
80
81 fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
82 let mut buf = [0u8; 32];
84 scalar
85 .serialize_compressed(&mut buf[..])
86 .expect("Serialization should not fail for valid scalars");
87
88 buf
89 }
90
91 fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
92 Self::serialize(scalar)
93 }
94
95 fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
97 let scalar = <Self::Scalar as CanonicalDeserialize>::deserialize_compressed(&buf[..])
98 .map_err(|_| FieldError::MalformedScalar)?;
99 Ok(scalar)
100 }
101}
102
103#[derive(Clone, Copy, PartialEq, Eq)]
105pub struct PallasGroup {}
106
107impl Group for PallasGroup {
108 type Element = ProjectivePallas;
109 type Field = PallasScalarField;
110 type Serialization = [u8; 32 * 3]; fn cofactor() -> <Self::Field as Field>::Scalar {
113 Self::Field::one()
114 }
115 fn identity() -> Self::Element {
116 Self::Element::zero()
117 }
118 fn generator() -> Self::Element {
119 Self::Element::generator()
120 }
121 fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
122 if element.is_zero() {
125 return Err(GroupError::InvalidIdentityElement);
126 }
127
128 let mut buf: Self::Serialization = [0u8; 96];
129 element
132 .serialize_compressed(&mut buf[..])
133 .map_err(|_| GroupError::MalformedElement)?;
134
135 Ok(buf)
136 }
137 fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
138 let point = <Self::Element as CanonicalDeserialize>::deserialize_compressed(&buf[..])
139 .map_err(|_| GroupError::MalformedElement);
140
141 match point {
143 Ok(p) if p.is_zero() => Err(GroupError::InvalidIdentityElement),
144 Ok(p) => Ok(p),
145 Err(_) => Err(GroupError::MalformedElement),
146 }
147 }
148}
149
150pub const CONTEXT_STRING: &str = "bluepallas";
153const HASH_SIZE: usize = 32; #[derive(PartialEq, Eq, Debug)]
160pub struct BluePallas<M>(PhantomData<M>);
161
162impl<M> Copy for BluePallas<M> {}
164
165impl<M> Clone for BluePallas<M> {
166 fn clone(&self) -> Self {
167 *self
168 }
169}
170
171impl<M> Ciphersuite for BluePallas<M>
172where
173 M: ChallengeMessage,
174{
175 const ID: &'static str = CONTEXT_STRING;
176
177 type Group = PallasGroup;
178 type HashOutput = [u8; HASH_SIZE];
179
180 type SignatureSerialization = [u8; HASH_SIZE];
181 fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
182 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])
183 }
184 fn H2(_m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
185 unimplemented!("H2 is not implemented on purpose, please see the `challenge` function");
186 }
187 fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
188 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])
189 }
190 fn H4(m: &[u8]) -> Self::HashOutput {
191 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
192 }
193 fn H5(m: &[u8]) -> Self::HashOutput {
194 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
195 }
196
197 fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
198 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))
199 }
200
201 fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
202 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))
203 }
204
205 fn challenge(
206 r: &frost_core::Element<Self>,
207 verifying_key: &frost_core::VerifyingKey<Self>,
208 message: &[u8],
209 ) -> Result<frost_core::Challenge<Self>, frost_core::Error<Self>> {
210 M::challenge(r, verifying_key, message)
211 }
212
213 fn pre_commitment_aggregate<'a>(
217 signing_package: &'a frost_core::SigningPackage<Self>,
218 binding_factor_list: &'a BindingFactorList<Self>,
219 ) -> Result<Cow<'a, frost_core::SigningPackage<Self>>, frost_core::Error<Self>> {
220 use ark_ff::{BigInteger, PrimeField};
221 let commit = compute_group_commitment(signing_package, binding_factor_list)?;
223
224 if commit.to_element().into_affine().y.into_bigint().is_even() {
225 return Ok(Cow::Borrowed(signing_package));
226 }
227
228 let negated_commitments =
230 <frost_core::SigningPackage<Self> as NegateY>::negate_y(signing_package);
231
232 Ok(Cow::Owned(negated_commitments))
233 }
234
235 fn pre_commitment_sign<'a>(
240 signing_package: &'a frost_core::SigningPackage<Self>,
241 signing_nonces: &'a frost_core::round1::SigningNonces<Self>,
242 binding_factor_list: &'a BindingFactorList<Self>,
243 ) -> Result<
244 (
245 Cow<'a, frost_core::SigningPackage<Self>>,
246 Cow<'a, frost_core::round1::SigningNonces<Self>>,
247 ),
248 frost_core::Error<Self>,
249 > {
250 use ark_ff::{BigInteger, PrimeField};
251 let commit = compute_group_commitment(signing_package, binding_factor_list)?;
253
254 if commit.to_element().into_affine().y.into_bigint().is_even() {
255 return Ok((
256 Cow::Borrowed(signing_package),
257 Cow::Borrowed(signing_nonces),
258 ));
259 }
260
261 let negated_nonce =
263 <frost_core::round1::SigningNonces<Self> as NegateY>::negate_y(signing_nonces);
264 let negated_commitments =
265 <frost_core::SigningPackage<Self> as NegateY>::negate_y(signing_package);
266
267 Ok((Cow::Owned(negated_commitments), Cow::Owned(negated_nonce)))
268 }
269}
270
271pub type Identifier<M> = frost::Identifier<BluePallas<M>>;
273
274pub type SigningPackage<M> = frost::SigningPackage<BluePallas<M>>;
277
278pub type Signature<M> = frost::Signature<BluePallas<M>>;
280
281pub type SigningKey<M> = frost::SigningKey<BluePallas<M>>;
283
284pub type VerifyingKey<M> = frost::VerifyingKey<BluePallas<M>>;
286
287pub mod round1 {
289 use crate::{keys::SigningShare, ChallengeMessage};
290
291 use super::*;
292 pub type SigningNonces<M> = frost::round1::SigningNonces<BluePallas<M>>;
298
299 pub type SigningCommitments<M> = frost::round1::SigningCommitments<BluePallas<M>>;
304
305 pub type NonceCommitment<M> = frost::round1::NonceCommitment<BluePallas<M>>;
307
308 pub fn commit<M, RNG>(
313 secret: &SigningShare<M>,
314 rng: &mut RNG,
315 ) -> (SigningNonces<M>, SigningCommitments<M>)
316 where
317 M: ChallengeMessage,
318 RNG: CryptoRng + RngCore,
319 {
320 frost::round1::commit::<BluePallas<M>, RNG>(secret, rng)
321 }
322}
323
324pub mod round2 {
326 use super::*;
327 use crate::{round1::SigningNonces, ChallengeMessage};
328
329 pub type SignatureShare<M> = frost::round2::SignatureShare<BluePallas<M>>;
332
333 pub fn sign<M>(
334 signing_package: &SigningPackage<M>,
335 signer_nonces: &SigningNonces<M>,
336 key_package: &frost::keys::KeyPackage<BluePallas<M>>,
337 ) -> Result<SignatureShare<M>, Error<M>>
338 where
339 M: ChallengeMessage,
340 {
341 frost::round2::sign::<BluePallas<M>>(signing_package, signer_nonces, key_package)
342 }
343}
344
345pub fn aggregate<M>(
346 signing_package: &SigningPackage<M>,
347 signature_shares: &BTreeMap<Identifier<M>, frost::round2::SignatureShare<BluePallas<M>>>,
348 pubkey_package: &frost::keys::PublicKeyPackage<BluePallas<M>>,
349) -> Result<Signature<M>, Error<M>>
350where
351 M: ChallengeMessage,
352{
353 frost::aggregate(signing_package, signature_shares, pubkey_package)
354}