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