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}