diff --git a/Cargo.toml b/Cargo.toml index 1d50531..e7a6788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "gosh_bls_lib" -version = "0.3.1" +version = "0.4.0" [profile.profiling] debug = 1 @@ -34,6 +34,8 @@ hex = "0.4.3" rand = "0.7" rand_chacha = "0.2" tvm_types = { git = "https://github.com/tvmlabs/tvm-types", tag = "3.0.1" } +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.117" [[bin]] name = "bls_keypair_gen" diff --git a/src/bin/bls_keypair_gen.rs b/src/bin/bls_keypair_gen.rs index ac35091..0b75a7a 100644 --- a/src/bin/bls_keypair_gen.rs +++ b/src/bin/bls_keypair_gen.rs @@ -4,35 +4,73 @@ use clap::Parser; use gosh_bls_lib::bls::gen_bls_key_pair; use gosh_bls_lib::bls::gen_bls_key_pair_based_on_key_material; use gosh_bls_lib::bls::BLS_SECRET_KEY_LEN; +use gosh_bls_lib::serde_bls::BLSKeyPair; + +const DEFAULT_FILE_STEM: &str = "bls_"; +const FILE_EXTENSION: &str = ".keys.json"; #[derive(Parser)] #[command(version, about, long_about = None)] struct Args { - /// Hex string with 32 bytes of key material for bls + /// Hex string with 32 bytes of key material #[arg(short, long)] key_material: Option, + /// Number of key pairs that should be generated + #[arg(short, long, conflicts_with = "key_material")] + number: Option, + /// Output directory for generated keys (use only for generating several keys) + #[arg(short, long, conflicts_with = "path", requires = "number")] + output_dir: Option, + /// Output file path (use only for generating a single key) + #[arg(short, long, conflicts_with_all = ["output_dir", "number"])] + path: Option, + /// Output file stem (used to generate several files: .keys.json + #[arg(long, conflicts_with = "path", requires = "number")] + output_stem: Option, } -fn main() { +fn main() -> anyhow::Result<()> { let args: Args = Args::parse(); - let (public, secret) = if let Some(key_material) = args.key_material { + // Generate first key pair, that will be used as seed in case of generating several keys + let first_key = BLSKeyPair::from(if let Some(key_material) = args.key_material { let mut secret_key = [0_u8; BLS_SECRET_KEY_LEN]; hex::decode_to_slice(key_material.trim_start_matches("0x"), &mut secret_key) - .expect("Failed to decode secret key from hex"); + .map_err(|e| anyhow::format_err!("Failed to decode secret key from hex: {e}"))?; gen_bls_key_pair_based_on_key_material(&secret_key) - .expect("Failed to generate BLS key pair") + .map_err(|e| anyhow::format_err!("Failed to generate BLS key pair: {e}"))? } else { - gen_bls_key_pair().expect("Failed to generate BLS key pair") - }; - - println!( - r#"{{ - "public": "{}", - "secret": "{}" -}}"#, - hex::encode(public), - hex::encode(secret) - ); + gen_bls_key_pair() + .map_err(|e| anyhow::format_err!("Failed to generate BLS key pair: {e}"))? + }); + + if let Some(path) = args.path { + return first_key.save_to_file(path); + } + if let Some(number) = args.number { + let file_stem = args.output_stem.unwrap_or(DEFAULT_FILE_STEM.to_string()); + let output_dir = args.output_dir.unwrap_or(".".to_string()); + if !std::path::Path::new(&output_dir).exists() { + std::fs::create_dir_all(&output_dir)?; + } + let full_path_stem = format!("{}/{}", output_dir.trim_end_matches('/'), file_stem); + + let path = format!("{}{}{}", full_path_stem, 0, FILE_EXTENSION); + first_key.save_to_file(path)?; + + for i in 1..number { + let key_pair = BLSKeyPair::from( + gen_bls_key_pair() + .map_err(|e| anyhow::format_err!("Failed to generate BLS key pair: {e}"))?, + ); + let path = format!("{}{}{}", full_path_stem, i, FILE_EXTENSION); + key_pair.save_to_file(path)?; + } + return Ok(()); + } + + println!("{}", first_key.to_string()?); + + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index a7884e6..9658276 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod bls; +pub mod serde_bls; diff --git a/src/serde_bls.rs b/src/serde_bls.rs new file mode 100644 index 0000000..5e85805 --- /dev/null +++ b/src/serde_bls.rs @@ -0,0 +1,45 @@ +use serde::Serialize; +use std::path::Path; + +use crate::bls::BLS_PUBLIC_KEY_LEN; +use crate::bls::BLS_SECRET_KEY_LEN; + +pub struct BLSKeyPair { + pub public: [u8; BLS_PUBLIC_KEY_LEN], + pub secret: [u8; BLS_SECRET_KEY_LEN], +} + +#[derive(Serialize)] +struct WrappedBLSKeyPair { + public: String, + secret: String, +} + +impl WrappedBLSKeyPair { + pub fn new(key_pair: &BLSKeyPair) -> Self { + WrappedBLSKeyPair { + public: hex::encode(key_pair.public), + secret: hex::encode(key_pair.secret), + } + } +} + +impl BLSKeyPair { + pub fn from(data: ([u8; BLS_PUBLIC_KEY_LEN], [u8; BLS_SECRET_KEY_LEN])) -> Self { + Self { + public: data.0, + secret: data.1, + } + } + + pub fn to_string(&self) -> anyhow::Result { + let wrapped = WrappedBLSKeyPair::new(self); + serde_json::to_string_pretty(&wrapped) + .map_err(|e| anyhow::format_err!("Failed to serialize BLSKeyPair: {e}")) + } + + pub fn save_to_file(&self, path: impl AsRef) -> anyhow::Result<()> { + std::fs::write(path, self.to_string()?) + .map_err(|e| anyhow::format_err!("Failed to save BLSKetPait: {e}")) + } +}