diff --git a/Cargo.lock b/Cargo.lock index a90bdf98899..9958e62d4ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1801,8 +1801,9 @@ dependencies = [ [[package]] name = "kes-summed-ed25519" -version = "0.1.0" -source = "git+https://github.com/input-output-hk/kes?rev=1418efa#1418efaacaca52ba48bdee0bab56bea1bb1d1ce4" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff5ba36dcf3798c53cfe222f22038ac63b1a3ef676a68af50a990d2775280cfb" dependencies = [ "blake2 0.9.2", "ed25519-dalek", @@ -2059,7 +2060,7 @@ dependencies = [ [[package]] name = "mithril-common" -version = "0.2.0" +version = "0.2.1" dependencies = [ "async-trait", "bech32", diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index 4a22d2ec2f8..de125155a24 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-common" -version = "0.2.0" +version = "0.2.1" authors = { workspace = true } edition = { workspace = true } documentation = { workspace = true } @@ -27,7 +27,7 @@ glob = "0.3" hex = "0.4.3" http = "0.2.6" jsonschema = "0.16.0" -kes-summed-ed25519 = { git = "https://github.com/input-output-hk/kes", rev = "1418efa", features = ["serde_enabled"] } +kes-summed-ed25519 = { version = "0.1.1", features = ["serde_enabled"] } mockall = "0.11.0" nom = "7.1" rand-chacha-dalek-compat = { package = "rand_chacha", version = "0.2" } diff --git a/mithril-common/src/crypto_helper/cardano/codec.rs b/mithril-common/src/crypto_helper/cardano/codec.rs index 8a8630719ff..abee41f8f7f 100644 --- a/mithril-common/src/crypto_helper/cardano/codec.rs +++ b/mithril-common/src/crypto_helper/cardano/codec.rs @@ -55,7 +55,8 @@ pub trait SerDeShelleyFileFormat: Serialize + DeserializeOwned { /// The description of the Cardano key const DESCRIPTION: &'static str; - /// Deserialize a Cardano key from file + /// Deserialize a type `T: Serialize + DeserializeOwned` from file following Cardano + /// Shelley file format. fn from_file>(path: P) -> Result { let data = fs::read_to_string(path)?; let file: ShelleyFileFormat = serde_json::from_str(&data)?; @@ -65,7 +66,8 @@ pub trait SerDeShelleyFileFormat: Serialize + DeserializeOwned { Ok(a) } - /// Serialize a Cardano Key to file + /// Serialize a type `T: Serialize + DeserializeOwned` to file following Cardano + /// Shelley file format. fn to_file>(&self, path: P) -> Result<(), ParseError> { let cbor_string = hex::encode(serde_cbor::to_vec(&self)?); @@ -86,4 +88,54 @@ pub trait SerDeShelleyFileFormat: Serialize + DeserializeOwned { impl SerDeShelleyFileFormat for Sum6Kes { const TYPE: &'static str = "KesSigningKey_ed25519_kes_2^6"; const DESCRIPTION: &'static str = "KES Signing Key"; + + /// Deserialize a Cardano key from file. Cardano KES key Shelley format does not + /// contain the period (it is always zero). Therefore we need to include it in the + /// deserialisation. + fn from_file>(path: P) -> Result { + let data = fs::read_to_string(path)?; + let file: ShelleyFileFormat = serde_json::from_str(&data)?; + let mut hex_vector = Vec::from_hex(file.cbor_hex)?; + + // We check whether the serialisation was performed by the haskell library or the rust library + if (hex_vector[2] & 4u8) == 0 { + // First we need to change the cbor format to notify about the extra 4 bytes: + hex_vector[2] |= 4u8; + // Then we append the bytes representing the period = 0 + hex_vector.extend_from_slice(&[0u8; 4]); + } + + let a: Self = serde_cbor::from_slice(&hex_vector)?; + Ok(a) + } +} + +#[cfg(all(test))] +mod test { + use super::*; + + #[test] + fn compat_with_shelly_format() { + let temp_dir = std::env::temp_dir().join("testing"); + fs::create_dir_all(&temp_dir).expect("temp dir creation should not fail"); + let sk_dir = temp_dir.join("dummy.skey"); + let cbor_string = "590260fe77acdfa56281e4b05198f5136018057a65f425411f0990cac4aca0f2917aa00a3d51e191f6f425d870aca3c6a2a41833621f5729d7bc0e3dfc3ae77d057e5e1253b71def7a54157b9f98973ca3c49edd9f311e5f4b23ac268b56a6ac040c14c6d2217925492e42f00dc89a2a01ff363571df0ca0db5ba37001cee56790cc01cd69c6aa760fca55a65a110305ea3c11da0a27be345a589329a584ebfc499c43c55e8c6db5d9c0b014692533ee78abd7ac1e79f7ec9335c7551d31668369b4d5111db78072f010043e35e5ca7f11acc3c05b26b9c7fe56f02aa41544f00cb7685e87f34c73b617260ade3c7b8d8c4df46693694998f85ad80d2cbab0b575b6ccd65d90574e84368169578bff57f751bc94f7eec5c0d7055ec88891a69545eedbfbd3c5f1b1c1fe09c14099f6b052aa215efdc5cb6cdc84aa810db41dbe8cb7d28f7c4beb75cc53915d3ac75fc9d0bf1c734a46e401e15150c147d013a938b7e07cc4f25a582b914e94783d15896530409b8acbe31ef471de8a1988ac78dfb7510729eff008084885f07df870b65e4f382ca15908e1dcda77384b5c724350de90cec22b1dcbb1cdaed88da08bb4772a82266ec154f5887f89860d0920dba705c45957ef6d93e42f6c9509c966277d368dd0eefa67c8147aa15d40a222f7953a4f34616500b310d00aa1b5b73eb237dc4f76c0c16813d321b2fc5ac97039be25b22509d1201d61f4ccc11cd4ff40fffe39f0e937b4722074d8e073a775d7283b715d46f79ce128e3f1362f35615fa72364d20b6db841193d96e58d9d8e86b516bbd1f05e45b39823a93f6e9f29d9e01acf2c12c072d1c64e0afbbabf6903ef542e".to_string(); + + let file_format = ShelleyFileFormat { + file_type: Sum6Kes::TYPE.to_string(), + description: Sum6Kes::DESCRIPTION.to_string(), + cbor_hex: cbor_string, + }; + + let mut file = + fs::File::create(sk_dir.clone()).expect("Unexpected error with file creation."); + let json_str = + serde_json::to_string(&file_format).expect("Unexpected error with serialisation."); + + write!(file, "{}", json_str).expect("Unexpected error writing to file."); + + let kes_sk = Sum6Kes::from_file(&sk_dir); + + assert!(kes_sk.is_ok(), "Failure parsing Shelley file format."); + } } diff --git a/mithril-common/src/crypto_helper/cardano/key_certification.rs b/mithril-common/src/crypto_helper/cardano/key_certification.rs index 01b57fe0a33..abab5e57dc4 100644 --- a/mithril-common/src/crypto_helper/cardano/key_certification.rs +++ b/mithril-common/src/crypto_helper/cardano/key_certification.rs @@ -29,7 +29,7 @@ use thiserror::Error; type D = Blake2b; /// The KES period that is used to check if the KES keys is expired -pub type KESPeriod = usize; +pub type KESPeriod = u32; /// New registration error #[derive(Error, Debug, PartialEq, Eq)] @@ -53,7 +53,7 @@ pub enum ProtocolRegistrationErrorWrapper { /// Error raised when a KES Signature verification fails #[error("KES signature verification error: CurrentKesPeriod={0}, StartKesPeriod={1}")] - KesSignatureInvalid(usize, u64), + KesSignatureInvalid(u32, u64), /// Error raised when a KES Signature is needed but not provided #[error("missing KES signature")] @@ -82,6 +82,10 @@ pub enum ProtocolInitializerErrorWrapper { /// Error raised when a KES update error occurs #[error("KES key cannot be updated for period {0}")] KesUpdate(KESPeriod), + + /// Period of key file does not match with period provided by user + #[error("Period of key file, {0}, does not match with period provided by user, {1}")] + KesMismatch(KESPeriod, KESPeriod), } /// Wrapper structure for [MithrilStm:StmInitializer](mithril_stm::stm::StmInitializer). /// It now obtains a KES signature over the Mithril key. This allows the signers prove @@ -119,17 +123,23 @@ impl StmInitializerWrapper { let kes_signature = if let Some(kes_sk_path) = kes_sk_path { let mut kes_sk: Sum6Kes = Sum6Kes::from_file(kes_sk_path)?; - // We need to perform the evolutions, as the key is stored in evolution 0 in `kes.skey` - for period in 0..kes_period.unwrap_or_default() { + let kes_sk_period = kes_sk.get_period(); + let provided_period = kes_period.unwrap_or_default(); + if kes_sk_period > provided_period { + return Err(ProtocolInitializerErrorWrapper::KesMismatch( + kes_sk_period, + provided_period, + )); + } + + // We need to perform the evolutions + for period in kes_sk_period..provided_period { kes_sk - .update(period) + .update() .map_err(|_| ProtocolInitializerErrorWrapper::KesUpdate(period))?; } - Some(kes_sk.sign( - kes_period.unwrap_or_default(), - &stm_initializer.verification_key().to_bytes(), - )) + Some(kes_sk.sign(&stm_initializer.verification_key().to_bytes())) } else { println!("WARNING: Non certified signer registration by providing only a Pool Id is decommissionned and must be used for tests only!"); None @@ -148,7 +158,7 @@ impl StmInitializerWrapper { /// Extract the verification key signature. pub fn verification_key_signature(&self) -> Option { - self.kes_signature.clone() + self.kes_signature } /// Extract the stake of the party diff --git a/mithril-common/src/crypto_helper/cardano/opcert.rs b/mithril-common/src/crypto_helper/cardano/opcert.rs index 8ae8987da45..6c97516b159 100644 --- a/mithril-common/src/crypto_helper/cardano/opcert.rs +++ b/mithril-common/src/crypto_helper/cardano/opcert.rs @@ -9,7 +9,7 @@ use blake2::{digest::consts::U28, Blake2b, Digest}; #[cfg(any(test, feature = "test_only"))] use ed25519_dalek::{Keypair as EdKeypair, Signer}; use ed25519_dalek::{PublicKey as EdPublicKey, Signature as EdSignature, Verifier}; -use kes_summed_ed25519::common::PublicKey as KesPublicKey; +use kes_summed_ed25519::PublicKey as KesPublicKey; use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use sha2::Sha256; diff --git a/mithril-signer/src/single_signer.rs b/mithril-signer/src/single_signer.rs index df9e57a4cc5..0b25c518f57 100644 --- a/mithril-signer/src/single_signer.rs +++ b/mithril-signer/src/single_signer.rs @@ -4,8 +4,9 @@ use std::path::PathBuf; use thiserror::Error; use mithril_common::crypto_helper::{ - key_decode_hex, key_encode_hex, ProtocolClerk, ProtocolInitializer, ProtocolKeyRegistration, - ProtocolPartyId, ProtocolRegistrationError, ProtocolSigner, ProtocolStakeDistribution, + key_decode_hex, key_encode_hex, KESPeriod, ProtocolClerk, ProtocolInitializer, + ProtocolKeyRegistration, ProtocolPartyId, ProtocolRegistrationError, ProtocolSigner, + ProtocolStakeDistribution, }; use mithril_common::entities::{ PartyId, ProtocolMessage, ProtocolParameters, SignerWithStake, SingleSignatures, Stake, @@ -38,7 +39,7 @@ impl MithrilProtocolInitializerBuilder { stake: &Stake, protocol_parameters: &ProtocolParameters, kes_secret_key_path: Option, - kes_period: Option, + kes_period: Option, ) -> Result { let mut rng = rand_core::OsRng; let protocol_initializer = ProtocolInitializer::setup(