1#![warn(rustdoc::broken_intra_doc_links)]
5#![warn(rustdoc::bare_urls)]
6#![no_std]
7
8extern crate alloc;
9
10use alloc::{borrow::Cow, collections::BTreeMap};
11use core::marker::PhantomData;
12
13use ark_ec::{models::CurveConfig, CurveGroup, PrimeGroup};
14
15use ark_ff::{fields::Field as ArkField, UniformRand};
16use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
17pub use frost_core::{self as frost, Ciphersuite, Field, FieldError, Group, GroupError};
18use frost_core::{compute_group_commitment, BindingFactorList};
19use mina_curves::pasta::{PallasParameters, ProjectivePallas};
20
21use num_traits::identities::Zero;
22use rand_core::{CryptoRng, RngCore};
23
24pub type Error<M> = frost_core::Error<BluePallas<M>>;
25
26use crate::{
27 hasher::{hash_to_array, hash_to_scalar},
28 negate::NegateY,
29};
30
31pub mod errors;
32pub mod hasher;
33pub mod keys;
34mod negate;
35pub mod signing_utilities;
36
37pub trait ChallengeMessage:
39 Clone + core::fmt::Debug + PartialEq + Eq + Sized + Send + Sync + 'static
40{
41 fn challenge(
42 r: &frost_core::Element<BluePallas<Self>>,
43 verifying_key: &frost_core::VerifyingKey<BluePallas<Self>>,
44 message: &[u8],
45 ) -> Result<frost_core::Challenge<BluePallas<Self>>, frost_core::Error<BluePallas<Self>>>;
46}
47
48pub const GROUP_SIZE: usize = 33; pub const FIELD_SIZE: usize = 32; #[derive(Clone, Copy)]
53pub struct PallasScalarField;
54
55impl Field for PallasScalarField {
56 type Scalar = <PallasParameters as CurveConfig>::ScalarField;
58 type Serialization = [u8; FIELD_SIZE];
59 fn zero() -> Self::Scalar {
60 Self::Scalar::zero()
61 }
62 fn one() -> Self::Scalar {
63 Self::Scalar::ONE
64 }
65 fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
66 <Self::Scalar as ArkField>::inverse(scalar).ok_or(FieldError::InvalidZeroScalar)
67 }
68 fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
69 Self::Scalar::rand(rng)
70 }
71
72 fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
73 let mut buf = [0u8; FIELD_SIZE];
75 scalar
76 .serialize_compressed(&mut buf[..])
77 .expect("Serialization should not fail for valid scalars");
78
79 buf
80 }
81
82 fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
83 Self::serialize(scalar)
84 }
85
86 fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
88 let scalar = <Self::Scalar as CanonicalDeserialize>::deserialize_compressed(&buf[..])
89 .map_err(|_| FieldError::MalformedScalar)?;
90 Ok(scalar)
91 }
92}
93
94#[derive(Clone, Copy, PartialEq, Eq)]
96pub struct PallasGroup {}
97
98impl Group for PallasGroup {
99 type Element = ProjectivePallas;
100 type Field = PallasScalarField;
101 type Serialization = [u8; GROUP_SIZE]; fn cofactor() -> <Self::Field as Field>::Scalar {
104 Self::Field::one()
105 }
106 fn identity() -> Self::Element {
107 Self::Element::zero()
108 }
109 fn generator() -> Self::Element {
110 Self::Element::generator()
111 }
112 fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
113 if element.is_zero() {
116 return Err(GroupError::InvalidIdentityElement);
117 }
118
119 let mut buf: Self::Serialization = [0u8; GROUP_SIZE];
120 element
121 .serialize_compressed(&mut buf[..])
122 .map_err(|_| GroupError::MalformedElement)?;
123
124 Ok(buf)
125 }
126 fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
127 let point = <Self::Element as CanonicalDeserialize>::deserialize_compressed(&buf[..])
128 .map_err(|_| GroupError::MalformedElement);
129
130 match point {
132 Ok(p) if p.is_zero() => Err(GroupError::InvalidIdentityElement),
133 Ok(p) => Ok(p),
134 Err(_) => Err(GroupError::MalformedElement),
135 }
136 }
137}
138
139pub const CONTEXT_STRING: &str = "bluepallas";
142const HASH_SIZE: usize = 32; #[derive(PartialEq, Eq, Debug)]
149pub struct BluePallas<M>(PhantomData<M>);
150
151impl<M> Copy for BluePallas<M> {}
153
154impl<M> Clone for BluePallas<M> {
155 fn clone(&self) -> Self {
156 *self
157 }
158}
159
160impl<M> Ciphersuite for BluePallas<M>
161where
162 M: ChallengeMessage,
163{
164 const ID: &'static str = CONTEXT_STRING;
165
166 type Group = PallasGroup;
167 type HashOutput = [u8; HASH_SIZE];
168
169 type SignatureSerialization = [u8; HASH_SIZE];
170 fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
171 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])
172 }
173 fn H2(_m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
174 unimplemented!("H2 is not implemented on purpose, please see the `challenge` function");
175 }
176 fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
177 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])
178 }
179 fn H4(m: &[u8]) -> Self::HashOutput {
180 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
181 }
182 fn H5(m: &[u8]) -> Self::HashOutput {
183 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
184 }
185
186 fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
187 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))
188 }
189
190 fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
191 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))
192 }
193
194 fn challenge(
195 r: &frost_core::Element<Self>,
196 verifying_key: &frost_core::VerifyingKey<Self>,
197 message: &[u8],
198 ) -> Result<frost_core::Challenge<Self>, frost_core::Error<Self>> {
199 M::challenge(r, verifying_key, message)
200 }
201
202 fn pre_commitment_aggregate<'a>(
206 signing_package: &'a frost_core::SigningPackage<Self>,
207 binding_factor_list: &'a BindingFactorList<Self>,
208 ) -> Result<Cow<'a, frost_core::SigningPackage<Self>>, frost_core::Error<Self>> {
209 use ark_ff::{BigInteger, PrimeField};
210 let commit = compute_group_commitment(signing_package, binding_factor_list)?;
212
213 if commit.to_element().into_affine().y.into_bigint().is_even() {
214 return Ok(Cow::Borrowed(signing_package));
215 }
216
217 let negated_commitments =
219 <frost_core::SigningPackage<Self> as NegateY>::negate_y(signing_package);
220
221 Ok(Cow::Owned(negated_commitments))
222 }
223
224 fn pre_commitment_sign<'a>(
229 signing_package: &'a frost_core::SigningPackage<Self>,
230 signing_nonces: &'a frost_core::round1::SigningNonces<Self>,
231 binding_factor_list: &'a BindingFactorList<Self>,
232 ) -> Result<
233 (
234 Cow<'a, frost_core::SigningPackage<Self>>,
235 Cow<'a, frost_core::round1::SigningNonces<Self>>,
236 ),
237 frost_core::Error<Self>,
238 > {
239 use ark_ff::{BigInteger, PrimeField};
240 let commit = compute_group_commitment(signing_package, binding_factor_list)?;
242
243 if commit.to_element().into_affine().y.into_bigint().is_even() {
244 return Ok((
245 Cow::Borrowed(signing_package),
246 Cow::Borrowed(signing_nonces),
247 ));
248 }
249
250 let negated_nonce =
252 <frost_core::round1::SigningNonces<Self> as NegateY>::negate_y(signing_nonces);
253 let negated_commitments =
254 <frost_core::SigningPackage<Self> as NegateY>::negate_y(signing_package);
255
256 Ok((Cow::Owned(negated_commitments), Cow::Owned(negated_nonce)))
257 }
258}
259
260pub type Identifier<M> = frost::Identifier<BluePallas<M>>;
262
263pub type SigningPackage<M> = frost::SigningPackage<BluePallas<M>>;
266
267pub type Signature<M> = frost::Signature<BluePallas<M>>;
269
270pub type SigningKey<M> = frost::SigningKey<BluePallas<M>>;
272
273pub type VerifyingKey<M> = frost::VerifyingKey<BluePallas<M>>;
275
276pub mod round1 {
278 use crate::{keys::SigningShare, ChallengeMessage};
279
280 use super::*;
281 pub type SigningNonces<M> = frost::round1::SigningNonces<BluePallas<M>>;
287
288 pub type SigningCommitments<M> = frost::round1::SigningCommitments<BluePallas<M>>;
293
294 pub type NonceCommitment<M> = frost::round1::NonceCommitment<BluePallas<M>>;
296
297 pub fn commit<M, RNG>(
302 secret: &SigningShare<M>,
303 rng: &mut RNG,
304 ) -> (SigningNonces<M>, SigningCommitments<M>)
305 where
306 M: ChallengeMessage,
307 RNG: CryptoRng + RngCore,
308 {
309 frost::round1::commit::<BluePallas<M>, RNG>(secret, rng)
310 }
311}
312
313pub mod round2 {
315 use super::*;
316 use crate::{round1::SigningNonces, ChallengeMessage};
317
318 pub type SignatureShare<M> = frost::round2::SignatureShare<BluePallas<M>>;
321
322 pub fn sign<M>(
323 signing_package: &SigningPackage<M>,
324 signer_nonces: &SigningNonces<M>,
325 key_package: &frost::keys::KeyPackage<BluePallas<M>>,
326 ) -> Result<SignatureShare<M>, Error<M>>
327 where
328 M: ChallengeMessage,
329 {
330 frost::round2::sign::<BluePallas<M>>(signing_package, signer_nonces, key_package)
331 }
332}
333
334pub fn aggregate<M>(
335 signing_package: &SigningPackage<M>,
336 signature_shares: &BTreeMap<Identifier<M>, frost::round2::SignatureShare<BluePallas<M>>>,
337 pubkey_package: &frost::keys::PublicKeyPackage<BluePallas<M>>,
338) -> Result<Signature<M>, Error<M>>
339where
340 M: ChallengeMessage,
341{
342 frost::aggregate(signing_package, signature_shares, pubkey_package)
343}