add Ed25519 ciphersuite (#164)

* add Ed25519 ciphersuite

* clippy fixes

* fixes after rebase

* update share generation test name

* use max/min_signers

* expand authors

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
Conrado Gouvea 2022-10-27 15:33:32 -03:00 committed by GitHub
parent 201d6adc4d
commit ac5f44ade8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 575 additions and 8 deletions

View File

@ -5,4 +5,6 @@ members = [
#"frost-redjubjub",
"frost-ristretto255",
"frost-p256",
"frost-ed25519",
"gendoc"
]

View File

@ -33,4 +33,7 @@ pub enum Error {
/// This element MUST NOT be the identity.
#[error("Invalid for this element to be the identity.")]
InvalidIdentityElement,
/// This element MUST have (large) prime order.
#[error("Invalid for this element to not have large prime order.")]
InvalidNonPrimeOrderElement,
}

48
frost-ed25519/Cargo.toml Normal file
View File

@ -0,0 +1,48 @@
[package]
name = "frost-ed25519"
edition = "2021"
# When releasing to crates.io:
# - Update html_root_url
# - Update CHANGELOG.md
# - Create git tag.
version = "0.1.0"
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>",
"Conrado Gouvea <conradoplg@gmail.com>"]
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/ZcashFoundation/frost"
categories = ["cryptography"]
keywords = ["cryptography", "crypto", "ristretto", "threshold", "signature"]
description = "A Schnorr signature scheme over the prime-order Ristretto group that supports FROST ."
[package.metadata.docs.rs]
features = ["nightly"]
[dependencies]
byteorder = "1.4"
curve25519-dalek = { version = "4.0.0-pre.1", features = ["serde"] }
digest = "0.10"
frost-core = { path = "../frost-core", features = ["test-impl"] }
frost-ristretto255 = { path = "../frost-ristretto255" }
hex = { version = "0.4.3", features = ["serde"] }
rand_core = "0.6"
serde = { version = "1", optional = true, features = ["derive"] }
sha2 = "0.10.2"
thiserror = "1.0"
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }
[dev-dependencies]
bincode = "1"
criterion = "0.4"
ed25519-dalek = "1.0.1"
ed25519-zebra = "3.0.0"
lazy_static = "1.4"
proptest = "1.0"
proptest-derive = "0.3"
rand = "0.8"
rand_chacha = "0.3"
serde_json = "1.0"
[features]
nightly = []
default = ["serde"]

32
frost-ed25519/README.md Normal file
View File

@ -0,0 +1,32 @@
An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers
of signers (FROST).
## Examples
Creating a `Signature` with a single signer, serializing and deserializing it, and verifying the
signature:
```rust
use rand::thread_rng;
use frost_ed25519::*;
let msg = b"Hello!";
// Generate a secret key and sign the message
let sk = SigningKey::new(thread_rng());
let sig = sk.sign(thread_rng(), msg);
// Types can be converted to raw byte arrays using `from_bytes`/`to_bytes`
let sig_bytes = sig.to_bytes();
let pk_bytes = VerifyingKey::from(&sk).to_bytes();
// Deserialize and verify the signature.
let sig = Signature::from_bytes(sig_bytes)?;
assert!(
VerifyingKey::from_bytes(pk_bytes)
.and_then(|pk| pk.verify(msg, &sig))
.is_ok()
);
# Ok::<(), Error>(())
```

304
frost-ed25519/src/lib.rs Normal file
View File

@ -0,0 +1,304 @@
#![allow(non_snake_case)]
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT,
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
traits::Identity,
};
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha512};
use frost_core::{frost, Ciphersuite, Field, Group};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite scalar field.
pub type Ed25519ScalarField = frost_ristretto255::RistrettoScalarField;
#[derive(Clone, Copy, PartialEq, Eq)]
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite group.
pub struct Ed25519Group;
impl Group for Ed25519Group {
type Field = Ed25519ScalarField;
type Element = EdwardsPoint;
type Serialization = [u8; 32];
fn cofactor() -> <Self::Field as Field>::Scalar {
Scalar::one()
}
fn identity() -> Self::Element {
EdwardsPoint::identity()
}
fn generator() -> Self::Element {
ED25519_BASEPOINT_POINT
}
fn serialize(element: &Self::Element) -> Self::Serialization {
element.compress().to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
match CompressedEdwardsY::from_slice(buf.as_ref()).decompress() {
Some(point) => {
if point == Self::identity() {
Err(Error::InvalidIdentityElement)
} else if point.is_torsion_free() {
Ok(point)
} else {
Err(Error::InvalidNonPrimeOrderElement)
}
}
None => Err(Error::MalformedElement),
}
}
}
/// Context string 'FROST-RISTRETTO255-SHA512-v5' from the ciphersuite in the [spec]
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-1
const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite.
pub struct Ed25519Sha512;
impl Ciphersuite for Ed25519Sha512 {
type Group = Ed25519Group;
type HashOutput = [u8; 64];
type SignatureSerialization = [u8; 64];
/// H1 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.1
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("rho")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H2 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.2
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new().chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H3 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.3
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("nonce")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
}
/// H4 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.4
fn H4(m: &[u8]) -> Self::HashOutput {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("msg")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
output
}
/// H5 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.5
fn H5(m: &[u8]) -> Self::HashOutput {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("com")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
output
}
/// HDKG for FROST(Ed25519, SHA-512)
fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
let h = Sha512::new()
.chain(CONTEXT_STRING.as_bytes())
.chain("dkg")
.chain(m);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
Some(<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output))
}
}
type E = Ed25519Sha512;
/// A FROST(Ed25519, SHA-512) participant identifier.
pub type Identifier = frost::Identifier<E>;
/// FROST(Ed25519, SHA-512) keys, key generation, key shares.
pub mod keys {
use super::*;
/// Allows all participants' keys to be generated using a central, trusted
/// dealer.
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: RNG,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), &'static str> {
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
}
/// Secret and public key material generated by a dealer performing
/// [`keygen_with_dealer`].
///
/// # Security
///
/// To derive a FROST(Ed25519, SHA-512) keypair, the receiver of the [`SecretShare`] *must* call
/// .into(), which under the hood also performs validation.
pub type SecretShare = frost::keys::SecretShare<E>;
/// A FROST(Ed25519, SHA-512) keypair, which can be generated either by a trusted dealer or using
/// a DKG.
///
/// When using a central dealer, [`SecretShare`]s are distributed to
/// participants, who then perform verification, before deriving
/// [`KeyPackage`]s, which they store to later use during signing.
pub type KeyPackage = frost::keys::KeyPackage<E>;
/// Public data that contains all the signers' public keys as well as the
/// group public key.
///
/// Used for verification purposes before publishing a signature.
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<E>;
}
/// FROST(Ed25519, SHA-512) Round 1 functionality and types.
pub mod round1 {
use frost_core::frost::keys::SigningShare;
use super::*;
/// Comprised of FROST(Ed25519, SHA-512) hiding and binding nonces.
///
/// Note that [`SigningNonces`] must be used *only once* for a signing
/// operation; re-using nonces will result in leakage of a signer's long-lived
/// signing key.
pub type SigningNonces = frost::round1::SigningNonces<E>;
/// Published by each participant in the first round of the signing protocol.
///
/// This step can be batched if desired by the implementation. Each
/// SigningCommitment can be used for exactly *one* signature.
pub type SigningCommitments = frost::round1::SigningCommitments<E>;
/// Performed once by each participant selected for the signing operation.
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<E>,
secret: &SigningShare<E>,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<E, RNG>(participant_identifier, secret, rng)
}
}
/// Generated by the coordinator of the signing operation and distributed to
/// each signing party.
pub type SigningPackage = frost::SigningPackage<E>;
/// FROST(Ed25519, SHA-512) Round 2 functionality and types, for signature share generation.
pub mod round2 {
use super::*;
/// A FROST(Ed25519, SHA-512) participant's signature share, which the Coordinator will aggregate with all other signer's
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<E>;
/// Generated by the coordinator of the signing operation and distributed to
/// each signing party
pub type SigningPackage = frost::SigningPackage<E>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
/// of randomizing commitments to be used in that signing operation, including
/// that for this participant.
///
/// Assumes the participant has already determined which nonce corresponds with
/// the commitment that was assigned by the coordinator in the SigningPackage.
pub fn sign(
signing_package: &SigningPackage,
signer_nonces: &round1::SigningNonces,
key_package: &keys::KeyPackage,
) -> Result<SignatureShare, &'static str> {
frost::round2::sign(signing_package, signer_nonces, key_package)
}
}
/// A Schnorr signature on FROST(Ed25519, SHA-512).
pub type Signature = frost_core::Signature<E>;
/// Verifies each FROST(Ed25519, SHA-512) participant's signature share, and if all are valid,
/// aggregates the shares into a signature to publish.
///
/// Resulting signature is compatible with verification of a plain Schnorr
/// signature.
///
/// This operation is performed by a coordinator that can communicate with all
/// the signing participants before publishing the final signature. The
/// coordinator can be one of the participants or a semi-trusted third party
/// (who is trusted to not perform denial of service attacks, but does not learn
/// any secret information). Note that because the coordinator is trusted to
/// report misbehaving parties in order to avoid publishing an invalid
/// signature, if the coordinator themselves is a signer and misbehaves, they
/// can avoid that step. However, at worst, this results in a denial of
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &round2::SigningPackage,
signature_shares: &[round2::SignatureShare],
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, &'static str> {
frost::aggregate(signing_package, signature_shares, pubkeys)
}
/// A signing key for a Schnorr signature on FROST(Ed25519, SHA-512).
pub type SigningKey = frost_core::SigningKey<E>;
/// A valid verifying key for Schnorr signatures on FROST(Ed25519, SHA-512).
pub type VerifyingKey = frost_core::VerifyingKey<E>;

View File

@ -0,0 +1,24 @@
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
use crate::*;
lazy_static! {
pub static ref ED25519_SHA512: Value =
serde_json::from_str(include_str!("tests/vectors.json").trim())
.expect("Test vector is valid JSON");
}
/// This is testing that Shamir's secret sharing to compute and arbitrary
/// value is working.
#[test]
fn check_share_generation_ed25519_sha512() {
let rng = thread_rng();
frost_core::tests::check_share_generation::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_sign_with_test_vectors() {
frost_core::tests::vectors::check_sign_with_test_vectors::<Ed25519Sha512>(&ED25519_SHA512)
}

View File

@ -0,0 +1,68 @@
{
"config": {
"MAX_PARTICIPANTS": "3",
"NUM_PARTICIPANTS": "2",
"MIN_PARTICIPANTS": "2",
"name": "FROST(Ed25519, SHA-512)",
"group": "ed25519",
"hash": "SHA-512"
},
"inputs": {
"group_secret_key": "7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304",
"group_public_key": "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673",
"message": "74657374",
"share_polynomial_coefficients": [
"178199860edd8c62f5212ee91eff1295d0d670ab4ed4506866bae57e7030b204"
],
"participants": {
"1": {
"participant_share": "929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509"
},
"2": {
"participant_share": "a91e66e012e4364ac9aaa405fcafd370402d9859f7b6685c07eed76bf409e80d"
},
"3": {
"participant_share": "d3cb090a075eb154e82fdb4b3cb507f110040905468bb9c46da8bdea643a9a02"
}
}
},
"round_one_outputs": {
"participant_list": "1,3",
"participants": {
"1": {
"hiding_nonce_randomness": "9d06a6381c7a4493929761a73692776772b274236fb5cfcc7d1b48ac3a9c249f",
"binding_nonce_randomness": "db184d7bc01a3417fe1f2eb3cf5479bb027145e6369a5f879f32d334ab256b23",
"hiding_nonce": "70652da3e8d7533a0e4b9e9104f01b48c396b5b553717784ed8d05c6a36b9609",
"binding_nonce": "4f9e1ad260b5c0e4fe0e0719c6324f89fecd053758f77c957f56967e634a710e",
"hiding_nonce_commitment": "44105304351ceddc58e15ddea35b2cb48e60ced54ceb22c3b0e5d42d098aa1d8",
"binding_nonce_commitment": "b8274b18a12f2cef74ae42f876cec1e31daab5cb162f95a56cd2487409c9d1dd",
"binding_factor_input": "c5b95020cba31a9035835f074f718d0c3af02a318d6b4723bbd1c088f4889dd7b9ff8e79f9a67a9d27605144259a7af18b7cca2539ffa5c4f1366a98645da8f4e077d604fff64f20e2377a37e5a10ce152194d62fe856ef4cd935d4f1cb0088c2083a2722ad3f5a84d778e257da0df2a7cadb004b1f5528352af778b94ee1c2a0100000000000000000000000000000000000000000000000000000000000000",
"binding_factor": "2d5630c36d33258b1208c4205fa759b762d09bfa06b29cf792cf98758c0b3305"
},
"3": {
"hiding_nonce_randomness": "31ca9b07936d6b342a43d97f23b7bec5a5f5a09575a075393868dd8df5c05a54",
"binding_nonce_randomness": "c1db96a85d8b593e14fdb869c0955625478afa6a987ad217e7f2261dcab26819",
"hiding_nonce": "233adcb0ec0eddba5f1cc5268f3f4e6fc1dd97fb1e4a1754e6ddc92ed834ca0b",
"binding_nonce": "b59fc8a32fe02ec0a44c4671f3d1f82ea3924b7c7c0179398fc9137e82757803",
"hiding_nonce_commitment": "d31bd81ce216b1c83912803a574a0285796275cb8b14f6dc92c8b09a6951f0a2",
"binding_nonce_commitment": "e1c863cfd08df775b6747ef2456e9bf9a03cc281a479a95261dc39137fcf0967",
"binding_factor_input": "c5b95020cba31a9035835f074f718d0c3af02a318d6b4723bbd1c088f4889dd7b9ff8e79f9a67a9d27605144259a7af18b7cca2539ffa5c4f1366a98645da8f4e077d604fff64f20e2377a37e5a10ce152194d62fe856ef4cd935d4f1cb0088c2083a2722ad3f5a84d778e257da0df2a7cadb004b1f5528352af778b94ee1c2a0300000000000000000000000000000000000000000000000000000000000000",
"binding_factor": "1137be5cdf3d18e44367acee8485e9a66c3164077af80619b6291e3943bbef04"
}
}
},
"round_two_outputs": {
"participant_list": "1,3",
"participants": {
"1": {
"sig_share": "c4b26af1e91fbc8440a0dad253e72620da624553c5b625fd51e6ea179fc09f05"
},
"3": {
"sig_share": "9369640967d0cb98f4dedfde58a845e0e18e0a7164396358439060ed282b4e08"
}
}
},
"final_output": {
"sig": "ae11c539fdc709b78fef5ee1f5a2250297e3e1b62a86a86c26d93c389934ba0e571ccffa50f0871d357fbab1ac8f6c00bcf14fc429f0885595764b05c8ebed0d"
}
}

View File

@ -0,0 +1,40 @@
use curve25519_dalek::{edwards::EdwardsPoint, traits::Identity};
use frost_core::{Ciphersuite, Group};
use frost_ed25519::*;
use rand::thread_rng;
#[test]
fn check_sign_with_dealer() {
let rng = thread_rng();
frost_core::tests::check_sign_with_dealer::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_sign_with_dkg() {
let rng = thread_rng();
frost_core::tests::check_sign_with_dkg::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::batch_verify::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_bad_batch_verify() {
let rng = thread_rng();
frost_core::tests::batch::bad_batch_verify::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_deserialize_identity() {
let encoded_identity = EdwardsPoint::identity().compress().to_bytes();
let r = <<Ed25519Sha512 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::InvalidIdentityElement));
}

View File

@ -0,0 +1,33 @@
use frost_core::tests::proptests::{tweak_strategy, SignatureCase};
use frost_ed25519::*;
use proptest::prelude::*;
use rand_chacha::ChaChaRng;
use rand_core::SeedableRng;
proptest! {
#[test]
fn tweak_signature(
tweaks in prop::collection::vec(tweak_strategy(), (0,5)),
rng_seed in prop::array::uniform32(any::<u8>()),
) {
// Use a deterministic RNG so that test failures can be reproduced.
// Seeding with 64 bits of entropy is INSECURE and this code should
// not be copied outside of this test!
let mut rng = ChaChaRng::from_seed(rng_seed);
// Create a test case for each signature type.
let msg = b"test message for proptests";
let mut sig = SignatureCase::<Ed25519Sha512>::new(&mut rng, msg.to_vec());
// Apply tweaks to each case.
for t in &tweaks {
sig.apply_tweak(t);
}
assert!(sig.check());
}
}

View File

@ -6,7 +6,8 @@ edition = "2021"
# - Update CHANGELOG.md
# - Create git tag.
version = "0.1.0"
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>"]
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>",
"Conrado Gouvea <conradoplg@gmail.com>"]
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/ZcashFoundation/frost"

View File

@ -263,7 +263,7 @@ pub mod keys {
/// # Security
///
/// To derive a FROST(P-256, SHA-256) keypair, the receiver of the [`SecretShare`] *must* call
/// .try_into(), which under the hood also performs validation.
/// .into(), which under the hood also performs validation.
pub type SecretShare = frost::keys::SecretShare<P>;
/// A FROST(P-256, SHA-256) keypair, which can be generated either by a trusted dealer or using

View File

@ -117,7 +117,7 @@ impl Group for RistrettoGroup {
}
}
/// Context string 'FROST-RISTRETTO255-SHA512-v5' from the ciphersuite in the [spec]
/// Context string from the ciphersuite in the [spec].
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.2-1
const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v11";

View File

@ -79,7 +79,7 @@ fn read_docs(filename: &str, suite_names_code: &[&str]) -> Vec<(String, String,
/// new_suite_names_doc: replacements to use in the documentation of the given file
/// for each reference in `old_suite_names_doc`.
fn write_docs(
docs: Vec<(String, String, usize, usize)>,
docs: &[(String, String, usize, usize)],
filename: &str,
suite_names_code: &[&str],
old_suite_names_doc: &[&str],
@ -90,10 +90,14 @@ fn write_docs(
// To be able to replace the documentation properly, start from the end, which
// will keep the string positions consistent
for ((old_name, _, old_start, old_end), (new_name, new_doc, _, _)) in
for ((_old_name, _, old_start, old_end), (_new_name, new_doc, _, _)) in
zip(old_docs.iter().rev(), docs.iter().rev())
{
assert_eq!(old_name, new_name, "source code does not match");
// This is a sanity check to test if we're replacing the right comment.
// It was commented out due to an exception (Ed25519 scalar is defined
// as the Ristretto25519 scalar instead of its own struct)
// assert_eq!(old_name, new_name, "source code does not match");
// Replaces ciphersuite-references in documentation
let mut new_doc = new_doc.to_string();
for (old_n, new_n) in zip(old_suite_names_doc.iter(), new_suite_names_doc.iter()) {
@ -114,10 +118,18 @@ fn main() {
// To add a new ciphersuite, just copy this call and replace the required strings.
write_docs(
docs,
&docs,
"frost-p256/src/lib.rs",
&["P256Sha256", "P256", "<P>"],
old_suite_names_doc,
&["FROST(P-256, SHA-256)"],
)
);
write_docs(
&docs,
"frost-ed25519/src/lib.rs",
&["Ed25519Sha512", "Ed25519", "<E>"],
old_suite_names_doc,
&["FROST(Ed25519, SHA-512)"],
);
}