frost_core/traits.rs
1//! Traits used to abstract Ciphersuites.
2
3use core::{
4 fmt::Debug,
5 ops::{Add, Mul, Sub},
6};
7
8use alloc::{borrow::Cow, collections::BTreeMap, vec::Vec};
9use rand_core::{CryptoRng, RngCore};
10
11use crate::{
12 challenge,
13 keys::{KeyPackage, PublicKeyPackage, SecretShare, VerifyingShare},
14 random_nonzero,
15 round1::{self, SigningNonces},
16 round2::{self, SignatureShare},
17 BindingFactor, BindingFactorList, Challenge, Error, FieldError, GroupCommitment, GroupError,
18 Identifier, Signature, SigningKey, SigningPackage, VerifyingKey,
19};
20
21/// A prime order finite field GF(q) over which all scalar values for our prime order group can be
22/// multiplied are defined.
23///
24/// This trait does not have to be implemented for a finite field scalar itself, it can be a
25/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
26/// implementation underneath, so that this trait does not have to be implemented for types you
27/// don't own.
28pub trait Field: Copy {
29 /// An element of the scalar field GF(p).
30 /// The Eq/PartialEq implementation MUST be constant-time.
31 type Scalar: Add<Output = Self::Scalar>
32 + Copy
33 + Clone
34 + Eq
35 + Mul<Output = Self::Scalar>
36 + PartialEq
37 + Sub<Output = Self::Scalar>
38 + Send
39 + Sync;
40
41 /// A unique byte array buf of fixed length N.
42 type Serialization: Clone + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug;
43
44 /// Returns the zero element of the field, the additive identity.
45 fn zero() -> Self::Scalar;
46
47 /// Returns the one element of the field, the multiplicative identity.
48 fn one() -> Self::Scalar;
49
50 /// Computes the multiplicative inverse of an element of the scalar field, failing if the
51 /// element is zero.
52 fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError>;
53
54 /// Generate a random scalar from the entire space [0, l-1]
55 ///
56 /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.6>
57 fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
58
59 /// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
60 /// fixed length Ne.
61 ///
62 /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.16>
63 fn serialize(scalar: &Self::Scalar) -> Self::Serialization;
64
65 /// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
66 /// fixed length Ne, in little-endian order.
67 ///
68 /// This is used internally.
69 fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization;
70
71 /// A member function of a [`Field`] that attempts to map a byte array `buf` to a [`Scalar`].
72 ///
73 /// Fails if the input is not a valid byte representation of an [`Scalar`] of the
74 /// [`Field`]. This function can raise an [`Error`] if deserialization fails.
75 ///
76 /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.18>
77 fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError>;
78}
79
80/// An element of the [`Ciphersuite`] `C`'s [`Group`]'s scalar [`Field`].
81pub type Scalar<C> = <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar;
82
83/// A prime-order group (or subgroup) that provides everything we need to create and verify Schnorr
84/// signatures.
85///
86/// This trait does not have to be implemented for the curve/element/point itself, it can be a
87/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
88/// implementation underneath, so that this trait does not have to be implemented for types you
89/// don't own.
90pub trait Group: Copy + PartialEq {
91 /// A prime order finite field GF(q) over which all scalar values for our prime order group can
92 /// be multiplied are defined.
93 type Field: Field;
94
95 /// An element of our group that we will be computing over.
96 type Element: Add<Output = Self::Element>
97 + Copy
98 + Clone
99 + Eq
100 + Mul<<Self::Field as Field>::Scalar, Output = Self::Element>
101 + PartialEq
102 + Sub<Output = Self::Element>
103 + Send
104 + Sync;
105
106 /// A unique byte array buf of fixed length N.
107 ///
108 /// Little-endian!
109 type Serialization: Clone + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug;
110
111 /// The order of the the quotient group when the prime order subgroup divides the order of the
112 /// full curve group.
113 ///
114 /// If using a prime order elliptic curve, the cofactor should be 1 in the scalar field.
115 fn cofactor() -> <Self::Field as Field>::Scalar;
116
117 /// Additive [identity] of the prime order group.
118 ///
119 /// [identity]: https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.4
120 fn identity() -> Self::Element;
121
122 /// The fixed generator element of the prime order group.
123 ///
124 /// The 'base' of ['ScalarBaseMult()'] from the spec.
125 ///
126 /// [`ScalarBaseMult()`]: https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.10
127 fn generator() -> Self::Element;
128
129 /// A member function of a group _G_ that maps an [`Element`] to a unique
130 /// byte array buf of fixed length Ne. This function raises an error if the
131 /// element is the identity element of the group.
132 ///
133 /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.12>
134 fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError>;
135
136 /// A member function of a [`Group`] that attempts to map a byte array `buf` to an [`Element`].
137 ///
138 /// Fails if the input is not a valid byte representation of an [`Element`] of the
139 /// [`Group`]. This function can raise an [`Error`] if deserialization fails or if the
140 /// resulting [`Element`] is the identity element of the group
141 ///
142 /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.14>
143 fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError>;
144}
145
146/// An element of the [`Ciphersuite`] `C`'s [`Group`].
147pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
148
149/// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
150/// function.
151///
152/// [FROST ciphersuite]: https://datatracker.ietf.org/doc/html/rfc9591#name-ciphersuites
153// See https://github.com/ZcashFoundation/frost/issues/693 for reasoning about the 'static bound.
154pub trait Ciphersuite: Copy + PartialEq + Debug + 'static + Send + Sync {
155 /// The ciphersuite ID string. It should be equal to the contextString in
156 /// the spec. For new ciphersuites, this should be a string that identifies
157 /// the ciphersuite; it's recommended to use a similar format to the
158 /// ciphersuites in the FROST spec, e.g. "FROST-RISTRETTO255-SHA512-v1".
159 const ID: &'static str;
160
161 /// The prime order group (or subgroup) that this ciphersuite operates over.
162 type Group: Group;
163
164 /// A unique byte array of fixed length.
165 type HashOutput: AsRef<[u8]>;
166
167 /// A unique byte array of fixed length that is the `Group::ElementSerialization` +
168 /// `Group::ScalarSerialization`
169 type SignatureSerialization: Clone
170 + AsRef<[u8]>
171 + AsMut<[u8]>
172 + for<'a> TryFrom<&'a [u8]>
173 + Debug;
174
175 /// [H1] for a FROST ciphersuite.
176 ///
177 /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
178 ///
179 /// [H1]: https://datatracker.ietf.org/doc/html/rfc9591#name-cryptographic-hash-function
180 fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
181
182 /// [H2] for a FROST ciphersuite.
183 ///
184 /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
185 ///
186 /// [H2]: https://datatracker.ietf.org/doc/html/rfc9591#name-cryptographic-hash-function
187 fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
188
189 /// [H3] for a FROST ciphersuite.
190 ///
191 /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
192 ///
193 /// [H3]: https://datatracker.ietf.org/doc/html/rfc9591#name-cryptographic-hash-function
194 fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
195
196 /// [H4] for a FROST ciphersuite.
197 ///
198 /// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
199 ///
200 /// [H4]: https://datatracker.ietf.org/doc/html/rfc9591#name-cryptographic-hash-function
201 fn H4(m: &[u8]) -> Self::HashOutput;
202
203 /// [H5] for a FROST ciphersuite.
204 ///
205 /// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
206 ///
207 /// [H5]: https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#cryptographic-hash
208 fn H5(m: &[u8]) -> Self::HashOutput;
209
210 /// Hash function for a FROST ciphersuite, used for the DKG.
211 ///
212 /// The DKG it not part of the specification, thus this is optional.
213 /// It can return None if DKG is not supported by the Ciphersuite. This is
214 /// the default implementation.
215 ///
216 /// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
217 fn HDKG(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
218 None
219 }
220
221 /// Hash function for a FROST ciphersuite, used for deriving identifiers from strings.
222 ///
223 /// This feature is not part of the specification and is just a convenient
224 /// way of creating identifiers. Therefore it can return None if this is not supported by the
225 /// Ciphersuite. This is the default implementation.
226 ///
227 /// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
228 fn HID(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
229 None
230 }
231
232 // The following are optional methods that allow customizing steps of the
233 // protocol if required.
234
235 /// Optional. Do regular (non-FROST) signing with a [`SigningKey`]. Called
236 /// by [`SigningKey::sign()`]. This is not used by FROST. Can be overridden
237 /// if required which is useful if FROST signing has been changed by the
238 /// other Ciphersuite trait methods and regular signing should be changed
239 /// accordingly to match.
240 fn single_sign<R: RngCore + CryptoRng>(
241 signing_key: &SigningKey<Self>,
242 rng: R,
243 message: &[u8],
244 ) -> Signature<Self> {
245 signing_key.default_sign(rng, message)
246 }
247
248 /// Optional. Verify a signature for this ciphersuite. Called by
249 /// [`VerifyingKey::verify()`]. The default implementation uses the
250 /// "cofactored" equation (it multiplies by the cofactor returned by
251 /// [`Group::cofactor()`]).
252 ///
253 /// # Cryptographic Safety
254 ///
255 /// You may override this to provide a tailored implementation, but if the
256 /// ciphersuite defines it, it must also multiply by the cofactor to comply
257 /// with the RFC. Note that batch verification (see
258 /// [`crate::batch::Verifier`]) also uses the default implementation
259 /// regardless whether a tailored implementation was provided.
260 fn verify_signature(
261 message: &[u8],
262 signature: &Signature<Self>,
263 public_key: &VerifyingKey<Self>,
264 ) -> Result<(), Error<Self>> {
265 let (message, signature, public_key) = <Self>::pre_verify(message, signature, public_key)?;
266
267 let c = <Self>::challenge(&signature.R, &public_key, &message)?;
268
269 public_key.verify_prehashed(c, &signature)
270 }
271
272 /// Optional. Pre-process [`round2::sign()`] inputs. The default
273 /// implementation returns them as-is. [`Cow`] is used so implementations
274 /// can choose to return the same passed reference or a modified clone.
275 #[allow(clippy::type_complexity)]
276 fn pre_sign<'a>(
277 signing_package: &'a SigningPackage<Self>,
278 signer_nonces: &'a round1::SigningNonces<Self>,
279 key_package: &'a KeyPackage<Self>,
280 ) -> Result<
281 (
282 Cow<'a, SigningPackage<Self>>,
283 Cow<'a, round1::SigningNonces<Self>>,
284 Cow<'a, KeyPackage<Self>>,
285 ),
286 Error<Self>,
287 > {
288 Ok((
289 Cow::Borrowed(signing_package),
290 Cow::Borrowed(signer_nonces),
291 Cow::Borrowed(key_package),
292 ))
293 }
294
295 /// Optional. Pre-process [`crate::aggregate()`] and
296 /// [`crate::verify_signature_share()`] inputs. In the latter case, "dummy"
297 /// container BTreeMap and PublicKeyPackage are passed with the relevant
298 /// values (PublicKeyPackage.min_signers will be None). The default
299 /// implementation returns them as-is. [`Cow`] is used so implementations
300 /// can choose to return the same passed reference or a modified clone.
301 #[allow(clippy::type_complexity)]
302 fn pre_aggregate<'a>(
303 signing_package: &'a SigningPackage<Self>,
304 signature_shares: &'a BTreeMap<Identifier<Self>, round2::SignatureShare<Self>>,
305 public_key_package: &'a PublicKeyPackage<Self>,
306 ) -> Result<
307 (
308 Cow<'a, SigningPackage<Self>>,
309 Cow<'a, BTreeMap<Identifier<Self>, round2::SignatureShare<Self>>>,
310 Cow<'a, PublicKeyPackage<Self>>,
311 ),
312 Error<Self>,
313 > {
314 Ok((
315 Cow::Borrowed(signing_package),
316 Cow::Borrowed(signature_shares),
317 Cow::Borrowed(public_key_package),
318 ))
319 }
320
321 /// Optional. Pre-process [`VerifyingKey::verify()`] inputs. The default
322 /// implementation returns them as-is. [`Cow`] is used so implementations
323 /// can choose to return the same passed reference or a modified clone.
324 #[allow(clippy::type_complexity)]
325 fn pre_verify<'a>(
326 msg: &'a [u8],
327 signature: &'a Signature<Self>,
328 public_key: &'a VerifyingKey<Self>,
329 ) -> Result<
330 (
331 Cow<'a, [u8]>,
332 Cow<'a, Signature<Self>>,
333 Cow<'a, VerifyingKey<Self>>,
334 ),
335 Error<Self>,
336 > {
337 Ok((
338 Cow::Borrowed(msg),
339 Cow::Borrowed(signature),
340 Cow::Borrowed(public_key),
341 ))
342 }
343
344 /// Optional. Pre-process [`crate::compute_group_commitment()`] inputs in
345 /// [`round2::sign()`].
346 #[allow(clippy::type_complexity)]
347 fn pre_commitment_sign<'a>(
348 signing_package: &'a SigningPackage<Self>,
349 signing_nonces: &'a SigningNonces<Self>,
350 _binding_factor_list: &'a BindingFactorList<Self>,
351 ) -> Result<(Cow<'a, SigningPackage<Self>>, Cow<'a, SigningNonces<Self>>), Error<Self>> {
352 Ok((
353 Cow::Borrowed(signing_package),
354 Cow::Borrowed(signing_nonces),
355 ))
356 }
357
358 /// Optional. Pre-process [`crate::compute_group_commitment()`] inputs in
359 /// [`crate::aggregate()`].
360 fn pre_commitment_aggregate<'a>(
361 signing_package: &'a SigningPackage<Self>,
362 _binding_factor_list: &'a BindingFactorList<Self>,
363 ) -> Result<Cow<'a, SigningPackage<Self>>, Error<Self>> {
364 Ok(Cow::Borrowed(signing_package))
365 }
366
367 /// Optional. Generate a nonce and a commitment to it. Used by
368 /// [`SigningKey`] for regular (non-FROST) signing and internally by the DKG
369 /// to generate proof-of-knowledge signatures.
370 fn generate_nonce<R: RngCore + CryptoRng>(
371 rng: &mut R,
372 ) -> (
373 <<Self::Group as Group>::Field as Field>::Scalar,
374 <Self::Group as Group>::Element,
375 ) {
376 let k = random_nonzero::<Self, R>(rng);
377 let R = <Self::Group>::generator() * k;
378 (k, R)
379 }
380
381 /// Optional. Generates the challenge as is required for Schnorr signatures.
382 /// Called by [`round2::sign()`] and [`crate::aggregate()`].
383 fn challenge(
384 R: &Element<Self>,
385 verifying_key: &VerifyingKey<Self>,
386 message: &[u8],
387 ) -> Result<Challenge<Self>, Error<Self>> {
388 challenge(R, verifying_key, message)
389 }
390
391 /// Optional. Compute the signature share for a particular signer on a given
392 /// challenge. Called by [`round2::sign()`].
393 fn compute_signature_share(
394 _group_commitment: &GroupCommitment<Self>,
395 signer_nonces: &round1::SigningNonces<Self>,
396 binding_factor: BindingFactor<Self>,
397 lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
398 key_package: &KeyPackage<Self>,
399 challenge: Challenge<Self>,
400 ) -> round2::SignatureShare<Self> {
401 round2::compute_signature_share(
402 signer_nonces,
403 binding_factor,
404 lambda_i,
405 key_package,
406 challenge,
407 )
408 }
409
410 /// Optional. Verify a signing share. Called by [`crate::aggregate()`] if
411 /// cheater detection is enabled.
412 fn verify_share(
413 _group_commitment: &GroupCommitment<Self>,
414 signature_share: &SignatureShare<Self>,
415 identifier: Identifier<Self>,
416 group_commitment_share: &round1::GroupCommitmentShare<Self>,
417 verifying_share: &VerifyingShare<Self>,
418 lambda_i: Scalar<Self>,
419 challenge: &Challenge<Self>,
420 ) -> Result<(), Error<Self>> {
421 signature_share.verify(
422 identifier,
423 group_commitment_share,
424 verifying_share,
425 lambda_i,
426 challenge,
427 )
428 }
429
430 /// Optional. Converts a signature to its
431 /// [`Ciphersuite::SignatureSerialization`] in bytes.
432 ///
433 /// The default implementation serializes a signature by serializing its `R`
434 /// point and `z` component independently, and then concatenating them.
435 fn serialize_signature(signature: &Signature<Self>) -> Result<Vec<u8>, Error<Self>> {
436 signature.default_serialize()
437 }
438
439 /// Optional. Converts bytes as [`Ciphersuite::SignatureSerialization`] into
440 /// a `Signature<C>`.
441 ///
442 /// The default implementation assumes the serialization is a serialized `R`
443 /// point followed by a serialized `z` component with no padding or extra
444 /// fields.
445 fn deserialize_signature(bytes: &[u8]) -> Result<Signature<Self>, Error<Self>> {
446 Signature::<Self>::default_deserialize(bytes)
447 }
448
449 /// Post-process the output of the DKG for a given participant.
450 fn post_dkg(
451 key_package: KeyPackage<Self>,
452 public_key_package: PublicKeyPackage<Self>,
453 ) -> Result<(KeyPackage<Self>, PublicKeyPackage<Self>), Error<Self>> {
454 Ok((key_package, public_key_package))
455 }
456
457 /// Post-process the output of the key generation for a participant.
458 #[allow(clippy::type_complexity)]
459 fn post_generate(
460 secret_shares: BTreeMap<Identifier<Self>, SecretShare<Self>>,
461 public_key_package: PublicKeyPackage<Self>,
462 ) -> Result<
463 (
464 BTreeMap<Identifier<Self>, SecretShare<Self>>,
465 PublicKeyPackage<Self>,
466 ),
467 Error<Self>,
468 > {
469 Ok((secret_shares, public_key_package))
470 }
471}