diff --git a/Cargo.lock b/Cargo.lock index 864724d5811..130728e3d15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2096,7 +2096,7 @@ dependencies = [ [[package]] name = "mithril-aggregator" -version = "0.3.57" +version = "0.3.58" dependencies = [ "async-trait", "chrono", diff --git a/docs/root/manual/developer-docs/nodes/mithril-aggregator.md b/docs/root/manual/developer-docs/nodes/mithril-aggregator.md index 24999bcdf5f..86270ac0af2 100644 --- a/docs/root/manual/developer-docs/nodes/mithril-aggregator.md +++ b/docs/root/manual/developer-docs/nodes/mithril-aggregator.md @@ -190,6 +190,8 @@ SUBCOMMANDS: import Import payload signed with genesis secret key and create & import a genesis certificate ``` +### bootstrap sub-command (test-only) + Run 'genesis bootstrap' command in release with default configuration, **only in test mode**. This allows the Mithril Aggregator node to bootstrap a `Genesis Certificate`. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates. @@ -203,27 +205,28 @@ Or with a specific `Genesis Secret Key`, **only in test mode**. ./mithril-aggregator genesis bootstrap --genesis-secret-key **YOUR_SECRET_KEY* ``` -Run 'genesis export' command in release with default configuration. -This allows the Mithril Aggregator node to export the `Genesis Payload` that needs to be signed (and later reimported) of the `Genesis Certificate`. The signature of the `Genesis Payload` must be done manually with the owner of the `Genesis Secret Key`. - -```bash -./mithril-aggregator genesis export -``` +### export sub-command -Or with a custom export path (to override the default value `./mithril-genesis-payload.txt`) +Run 'genesis export' command in release. +This allows the Mithril Aggregator node to export the `Genesis Payload` that needs to be signed (and later reimported) of the `Genesis Certificate`. The signature of the `Genesis Payload` must be done manually with the owner of the `Genesis Secret Key`. ```bash ./mithril-aggregator genesis export --target-path **YOUR_TARGET_PATH** ``` -Run 'genesis import' command in release with default configuration. -This allows the Mithril Aggregator node to import the signed payload of the `Genesis Certificate` and create it in the store. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates. +### sign sub-command + +Run 'genesis sign' command in release. +This allows the Mithril Aggregator node to sign the `Genesis Payload` that needs to be reimported. The signature of the `Genesis Payload` must be done manually by the owner of the `Genesis Secret Key`. ```bash -./mithril-aggregator genesis import +./mithril-aggregator genesis sign --to-sign-payload-path **TO_SIGN_PAYLOAD_PATH** --target-signed-payload-path **TARGET_SIGNED_PAYLOAD_PATH** --genesis-secret-key-path **GENESIS_SECRET_KEY_PATH** ``` -Or with a custom export path (to override the default value `./mithril-genesis-signed-payload.txt`) +### import sub-command + +Run 'genesis import' command in release. +This allows the Mithril Aggregator node to import the signed payload of the `Genesis Certificate` and create it in the store. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates. ```bash ./mithril-aggregator genesis import --signed-payload-path **YOUR_SIGNED_PAYLOAD_PATH** @@ -397,6 +400,7 @@ Here are the subcommands available: | **serve** | Aggregator runs its HTTP server in nominal mode and orchestrates multi signatures production | | **help** | Print this message or the help of the given subcommand(s) | | **genesis export** | Export genesis payload to sign with genesis secret key | +| **genesis sign** | Sign genesis payload with genesis secret key | | **genesis import** | Import genesis signature (payload signed with genesis secret key) and create & import a genesis certificate in the store | | **genesis bootstrap** | Bootstrap a genesis certificate (test only usage) | | **era list** | List the supported eras | @@ -450,17 +454,25 @@ General parameters: |-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:| | `genesis_secret_key` | - | - | `GENESIS_SECRET_KEY` | Genesis secret key, :warning: for test only | - | - | - | +`genesis export` command: + +| Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory | +|-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:| +| `target_path` | `--target-path` | - | - | Path of the file to export the payload to. | - | - | - | - | + `genesis import` command: | Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory | |-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:| | `signed_payload_path` | `--signed-payload-path` | - | - | Path of the payload to import. | - | - | - | - | -`genesis export` command: +`genesis sign` command: | Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory | |-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:| -| `target_path` | `--target-path` | - | - | Path of the file to export the payload to. | - | - | - | - | +| `to_sign_payload_path` | `--to-sign-payload-path` | - | - | Path of the payload to sign. | - | - | - | - | +| `target_signed_payload_path` | `--target-signed-payload-path` | - | - | Path of the signed payload to export. | - | - | - | - | +| `genesis_secret_key_path` | `--genesis-secret-key-path` | - | - | Path of the Genesis secret key. | - | - | - | `era list` command: diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index be17a67b3e7..57675e947b5 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-aggregator" -version = "0.3.57" +version = "0.3.58" description = "A Mithril Aggregator server" authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-aggregator/src/commands/genesis_command.rs b/mithril-aggregator/src/commands/genesis_command.rs index 6e42ee810c4..9fab6010ec9 100644 --- a/mithril-aggregator/src/commands/genesis_command.rs +++ b/mithril-aggregator/src/commands/genesis_command.rs @@ -35,6 +35,9 @@ pub enum GenesisSubCommand { /// Genesis certificate import command. Import(ImportGenesisSubCommand), + /// Genesis certificate sign command. + Sign(SignGenesisSubCommand), + /// Genesis certificate bootstrap command. Bootstrap(BootstrapGenesisSubCommand), } @@ -48,6 +51,7 @@ impl GenesisSubCommand { Self::Bootstrap(cmd) => cmd.execute(config_builder).await, Self::Export(cmd) => cmd.execute(config_builder).await, Self::Import(cmd) => cmd.execute(config_builder).await, + Self::Sign(cmd) => cmd.execute(config_builder).await, } } } @@ -124,6 +128,44 @@ impl ImportGenesisSubCommand { } } +#[derive(Parser, Debug, Clone)] +pub struct SignGenesisSubCommand { + /// To Sign Payload Path + #[clap(long)] + to_sign_payload_path: PathBuf, + + /// Target Signed Payload Path + #[clap(long)] + target_signed_payload_path: PathBuf, + + /// Genesis Secret Key Path + #[clap(long)] + genesis_secret_key_path: PathBuf, +} + +impl SignGenesisSubCommand { + pub async fn execute( + &self, + _config_builder: ConfigBuilder, + ) -> Result<(), Box> { + debug!("SIGN GENESIS command"); + println!( + "Genesis sign payload from {} to {}", + self.to_sign_payload_path.to_string_lossy(), + self.target_signed_payload_path.to_string_lossy() + ); + + GenesisTools::sign_genesis_certificate( + &self.to_sign_payload_path, + &self.target_signed_payload_path, + &self.genesis_secret_key_path, + ) + .await + .map_err(|err| format!("genesis-tools: sign error: {err}"))?; + + Ok(()) + } +} #[derive(Parser, Debug, Clone)] pub struct BootstrapGenesisSubCommand { /// Genesis Secret Key (test only) diff --git a/mithril-aggregator/src/tools/genesis.rs b/mithril-aggregator/src/tools/genesis.rs index 3a6a174f90e..6660d81ac59 100644 --- a/mithril-aggregator/src/tools/genesis.rs +++ b/mithril-aggregator/src/tools/genesis.rs @@ -127,6 +127,32 @@ impl GenesisTools { .await } + /// Sign the genesis certificate + pub async fn sign_genesis_certificate( + to_sign_payload_path: &Path, + target_signed_payload_path: &Path, + genesis_secret_key_path: &Path, + ) -> StdResult<()> { + let mut genesis_secret_key_file = File::open(genesis_secret_key_path).unwrap(); + let mut genesis_secret_key_serialized = String::new(); + genesis_secret_key_file.read_to_string(&mut genesis_secret_key_serialized)?; + + let genesis_secret_key = key_decode_hex(&genesis_secret_key_serialized.trim().to_string())?; + let genesis_signer = ProtocolGenesisSigner::from_secret_key(genesis_secret_key); + + let mut to_sign_payload_file = File::open(to_sign_payload_path).unwrap(); + let mut to_sign_payload_buffer = Vec::new(); + to_sign_payload_file.read_to_end(&mut to_sign_payload_buffer)?; + + let genesis_signature = genesis_signer.sign(&to_sign_payload_buffer); + let signed_payload = genesis_signature.to_bytes(); + + let mut target_signed_payload_file = File::create(target_signed_payload_path)?; + target_signed_payload_file.write_all(&signed_payload)?; + + Ok(()) + } + async fn create_and_save_genesis_certificate( &self, genesis_signature: ProtocolGenesisSignature, @@ -162,7 +188,10 @@ mod tests { use super::*; fn get_temp_dir(dir_name: &str) -> PathBuf { - let dir = std::env::temp_dir().join("mithril_test").join(dir_name); + let dir = std::env::temp_dir() + .join("mithril_test") + .join("genesis") + .join(dir_name); if dir.exists() { let _ = fs::remove_dir_all(&dir); @@ -180,20 +209,6 @@ mod tests { clerk.compute_avk() } - fn sign_avk_payload( - payload_path: &Path, - target_path: &Path, - genesis_signer: &ProtocolGenesisSigner, - ) -> StdResult<()> { - let mut payload_file = File::open(payload_path).unwrap(); - let mut payload_buffer = Vec::new(); - payload_file.read_to_end(&mut payload_buffer)?; - let payload_signed = genesis_signer.sign(&payload_buffer).to_bytes(); - let mut target_file = File::create(target_path)?; - target_file.write_all(&payload_signed)?; - Ok(()) - } - fn build_tools( genesis_signer: &ProtocolGenesisSigner, ) -> ( @@ -229,17 +244,26 @@ mod tests { #[tokio::test] async fn export_sign_then_import_genesis_payload() { let test_dir = get_temp_dir("export_payload_to_sign"); - let path = test_dir.join("payload.txt"); + let payload_path = test_dir.join("payload.txt"); let signed_payload_path = test_dir.join("payload-signed.txt"); + let genesis_secret_key_path = test_dir.join("genesis.sk"); let genesis_signer = ProtocolGenesisSigner::create_deterministic_genesis_signer(); let (genesis_tools, certificate_store, genesis_verifier, certificate_verifier) = build_tools(&genesis_signer); + genesis_signer + .export_to_file(&genesis_secret_key_path) + .expect("exporting the secret key should not fail"); genesis_tools - .export_payload_to_sign(&path) + .export_payload_to_sign(&payload_path) .expect("export_payload_to_sign should not fail"); - sign_avk_payload(&path, &signed_payload_path, &genesis_signer) - .expect("sign avk payload should not fail"); + GenesisTools::sign_genesis_certificate( + &payload_path, + &signed_payload_path, + &genesis_secret_key_path, + ) + .await + .expect("sign_genesis_certificate should not fail"); genesis_tools .import_payload_signature(&signed_payload_path) .await diff --git a/mithril-common/src/crypto_helper/genesis.rs b/mithril-common/src/crypto_helper/genesis.rs index d7f2ff19f34..feea5f8b771 100644 --- a/mithril-common/src/crypto_helper/genesis.rs +++ b/mithril-common/src/crypto_helper/genesis.rs @@ -1,10 +1,15 @@ +use crate::StdResult; use ed25519_dalek::{ExpandedSecretKey, SignatureError}; use rand_chacha_dalek_compat::rand_core::{self, CryptoRng, RngCore, SeedableRng}; use rand_chacha_dalek_compat::ChaCha20Rng; use serde::{Deserialize, Serialize}; +use std::{fs::File, io::Write, path::Path}; use thiserror::Error; -use super::{ProtocolGenesisSecretKey, ProtocolGenesisSignature, ProtocolGenesisVerificationKey}; +use super::{ + key_encode_hex, ProtocolGenesisSecretKey, ProtocolGenesisSignature, + ProtocolGenesisVerificationKey, +}; #[derive(Error, Debug)] /// [ProtocolGenesisSigner] and [ProtocolGenesisVerifier] related errors. @@ -18,6 +23,7 @@ pub enum ProtocolGenesisError { /// [Genesis Certificate](https://mithril.network/doc/mithril/mithril-protocol/certificates#the-certificate-chain-design) #[derive(Debug, Serialize, Deserialize)] pub struct ProtocolGenesisSigner { + /// Protocol Genesis secret key pub(crate) secret_key: ProtocolGenesisSecretKey, } @@ -75,6 +81,19 @@ impl ProtocolGenesisSigner { let verification_key = self.create_verification_key(&expanded_secret_key); expanded_secret_key.sign(message, &verification_key) } + + /// Export the secret key from the genesis verifier to a file. TEST ONLY + #[doc(hidden)] + pub fn export_to_file(&self, secret_key_path: &Path) -> StdResult<()> { + let mut genesis_secret_key_file = File::create(secret_key_path)?; + genesis_secret_key_file.write_all( + key_encode_hex(self.secret_key.as_bytes()) + .unwrap() + .as_bytes(), + )?; + + Ok(()) + } } /// A protocol Genesis Verifier that is responsible for verifying the