1use crate::{
7 crypto_helper::{
8 cardano::SerDeShelleyFileFormat,
9 types::{
10 ProtocolParameters, ProtocolPartyId, ProtocolSignerVerificationKey,
11 ProtocolSignerVerificationKeySignature, ProtocolStakeDistribution,
12 },
13 ProtocolOpCert,
14 },
15 StdError, StdResult,
16};
17
18use mithril_stm::{
19 ClosedKeyReg, KeyReg, RegisterError, Stake, StmInitializer, StmParameters, StmSigner,
20 StmVerificationKeyPoP,
21};
22
23use crate::crypto_helper::cardano::Sum6KesBytes;
24use anyhow::{anyhow, Context};
25use blake2::{
26 digest::{consts::U32, FixedOutput},
27 Blake2b, Digest,
28};
29use kes_summed_ed25519::kes::{Sum6Kes, Sum6KesSig};
30use kes_summed_ed25519::traits::{KesSig, KesSk};
31use rand_core::{CryptoRng, RngCore};
32use serde::{Deserialize, Serialize};
33use std::collections::HashMap;
34use std::path::Path;
35use thiserror::Error;
36
37type D = Blake2b<U32>;
39
40pub type KESPeriod = u32;
42
43#[derive(Error, Debug)]
45pub enum ProtocolRegistrationErrorWrapper {
46 #[error("missing party id")]
50 PartyIdMissing,
51
52 #[error("party id does not exist in the stake distribution")]
54 PartyIdNonExisting,
55
56 #[error("missing operational certificate")]
58 OpCertMissing,
59
60 #[error("invalid operational certificate")]
62 OpCertInvalid,
63
64 #[error("KES signature verification error: CurrentKesPeriod={0}, StartKesPeriod={1}")]
66 KesSignatureInvalid(u32, u64),
67
68 #[error("missing KES signature")]
70 KesSignatureMissing,
71
72 #[error("missing KES period")]
74 KesPeriodMissing,
75
76 #[error("pool address encoding error")]
78 PoolAddressEncoding,
79
80 #[error("core registration error")]
82 CoreRegister(#[source] RegisterError),
83}
84
85#[derive(Error, Debug)]
87pub enum ProtocolInitializerErrorWrapper {
88 #[error("protocol initializer error")]
90 ProtocolInitializer(#[source] StdError),
91
92 #[error("KES key cannot be updated for period {0}")]
94 KesUpdate(KESPeriod),
95
96 #[error("Period of key file, {0}, does not match with period provided by user, {1}")]
98 KesMismatch(KESPeriod, KESPeriod),
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct StmInitializerWrapper {
106 stm_initializer: StmInitializer,
108
109 kes_signature: Option<Sum6KesSig>,
113}
114
115#[derive(Debug, Clone)]
122pub struct KeyRegWrapper {
123 stm_key_reg: KeyReg,
124 stake_distribution: HashMap<ProtocolPartyId, Stake>,
125}
126
127impl StmInitializerWrapper {
128 pub fn setup<R: RngCore + CryptoRng, P: AsRef<Path>>(
132 params: StmParameters,
133 kes_sk_path: Option<P>,
134 kes_period: Option<KESPeriod>,
135 stake: Stake,
136 rng: &mut R,
137 ) -> StdResult<Self> {
138 let stm_initializer = StmInitializer::setup(params, stake, rng);
139 let kes_signature = if let Some(kes_sk_path) = kes_sk_path {
140 let mut kes_sk_bytes = Sum6KesBytes::from_file(kes_sk_path)
141 .map_err(|e| anyhow!(e))
142 .with_context(|| "StmInitializerWrapper can not read KES secret key from file")?;
143 let mut kes_sk = Sum6Kes::try_from(&mut kes_sk_bytes)
144 .map_err(|e| ProtocolInitializerErrorWrapper::ProtocolInitializer(anyhow!(e)))
145 .with_context(|| "StmInitializerWrapper can not use KES secret key")?;
146 let kes_sk_period = kes_sk.get_period();
147 let provided_period = kes_period.unwrap_or_default();
148 if kes_sk_period > provided_period {
149 return Err(anyhow!(ProtocolInitializerErrorWrapper::KesMismatch(
150 kes_sk_period,
151 provided_period,
152 )));
153 }
154
155 for period in kes_sk_period..provided_period {
157 kes_sk
158 .update()
159 .map_err(|_| ProtocolInitializerErrorWrapper::KesUpdate(period))?;
160 }
161
162 Some(kes_sk.sign(&stm_initializer.verification_key().to_bytes()))
163 } else {
164 println!("WARNING: Non certified signer registration by providing only a Pool Id is decommissioned and must be used for tests only!");
165 None
166 };
167
168 Ok(Self {
169 stm_initializer,
170 kes_signature,
171 })
172 }
173
174 pub fn verification_key(&self) -> StmVerificationKeyPoP {
176 self.stm_initializer.verification_key()
177 }
178
179 pub fn verification_key_signature(&self) -> Option<ProtocolSignerVerificationKeySignature> {
181 self.kes_signature.map(|k| k.into())
182 }
183
184 pub fn get_protocol_parameters(&self) -> ProtocolParameters {
186 self.stm_initializer.params
187 }
188
189 pub fn get_stake(&self) -> Stake {
191 self.stm_initializer.stake
192 }
193
194 pub fn new_signer(
207 self,
208 closed_reg: ClosedKeyReg<D>,
209 ) -> Result<StmSigner<D>, ProtocolRegistrationErrorWrapper> {
210 self.stm_initializer
211 .new_signer(closed_reg)
212 .map_err(ProtocolRegistrationErrorWrapper::CoreRegister)
213 }
214
215 pub fn to_bytes(&self) -> Vec<u8> {
220 let mut out = Vec::new();
221 out.extend_from_slice(&self.stm_initializer.to_bytes());
222 if let Some(kes_signature) = &self.kes_signature {
223 out.extend_from_slice(&kes_signature.to_bytes());
224 }
225
226 out
227 }
228
229 pub fn from_bytes(bytes: &[u8]) -> Result<Self, RegisterError> {
233 let stm_initializer = StmInitializer::from_bytes(&bytes[..256])?;
234 let kes_signature = if bytes[256..].is_empty() {
235 None
236 } else {
237 Some(
238 Sum6KesSig::from_bytes(&bytes[256..])
239 .map_err(|_| RegisterError::SerializationError)?,
240 )
241 };
242
243 Ok(Self {
244 stm_initializer,
245 kes_signature,
246 })
247 }
248
249 cfg_test_tools! {
250 pub fn override_protocol_parameters(&mut self, protocol_parameters: &ProtocolParameters) {
252 self.stm_initializer.params = protocol_parameters.to_owned();
253 }
254 }
255}
256
257impl KeyRegWrapper {
258 pub fn init(stake_dist: &ProtocolStakeDistribution) -> Self {
261 Self {
262 stm_key_reg: KeyReg::init(),
263 stake_distribution: HashMap::from_iter(stake_dist.to_vec()),
264 }
265 }
266
267 pub fn register(
271 &mut self,
272 party_id: Option<ProtocolPartyId>, opcert: Option<ProtocolOpCert>, kes_sig: Option<ProtocolSignerVerificationKeySignature>, kes_period: Option<KESPeriod>,
276 pk: ProtocolSignerVerificationKey,
277 ) -> Result<ProtocolPartyId, ProtocolRegistrationErrorWrapper> {
278 let pool_id_bech32: ProtocolPartyId = if let Some(opcert) = opcert {
279 opcert
280 .validate()
281 .map_err(|_| ProtocolRegistrationErrorWrapper::OpCertInvalid)?;
282 let mut pool_id = None;
283 let sig = kes_sig.ok_or(ProtocolRegistrationErrorWrapper::KesSignatureMissing)?;
284 let kes_period =
285 kes_period.ok_or(ProtocolRegistrationErrorWrapper::KesPeriodMissing)?;
286 let kes_period_try_min = std::cmp::max(0, kes_period.saturating_sub(1));
287 let kes_period_try_max = std::cmp::min(64, kes_period.saturating_add(1));
288 for kes_period_try in kes_period_try_min..kes_period_try_max {
289 if sig
290 .verify(kes_period_try, &opcert.kes_vk, &pk.to_bytes())
291 .is_ok()
292 {
293 pool_id = Some(
294 opcert
295 .compute_protocol_party_id()
296 .map_err(|_| ProtocolRegistrationErrorWrapper::PoolAddressEncoding)?,
297 );
298 break;
299 }
300 }
301 pool_id.ok_or(ProtocolRegistrationErrorWrapper::KesSignatureInvalid(
302 kes_period,
303 opcert.start_kes_period,
304 ))?
305 } else {
306 if cfg!(not(feature = "allow_skip_signer_certification")) {
307 Err(ProtocolRegistrationErrorWrapper::OpCertMissing)?
308 }
309 party_id.ok_or(ProtocolRegistrationErrorWrapper::PartyIdMissing)?
310 };
311
312 if let Some(&stake) = self.stake_distribution.get(&pool_id_bech32) {
313 self.stm_key_reg
314 .register(stake, pk.into())
315 .map_err(ProtocolRegistrationErrorWrapper::CoreRegister)?;
316 return Ok(pool_id_bech32);
317 }
318 Err(ProtocolRegistrationErrorWrapper::PartyIdNonExisting)
319 }
320
321 pub fn close<D: Digest + FixedOutput>(self) -> ClosedKeyReg<D> {
324 self.stm_key_reg.close()
325 }
326}
327
328#[cfg(test)]
329mod test {
330 use super::*;
331 use crate::crypto_helper::{cardano::ColdKeyGenerator, OpCert};
332
333 use crate::test_utils::TempDir;
334 use rand_chacha::ChaCha20Rng;
335 use rand_core::SeedableRng;
336 use std::path::PathBuf;
337
338 fn setup_temp_directory(test_name: &str) -> PathBuf {
339 TempDir::create("mithril_cardano_key_certification", test_name)
340 }
341
342 fn create_cryptographic_material(party_idx: u64) -> (ProtocolPartyId, PathBuf, PathBuf) {
343 let temp_dir = setup_temp_directory(&format!("create_cryptographic_material_{party_idx}"));
344 let keypair = ColdKeyGenerator::create_deterministic_keypair([party_idx as u8; 32]);
345 let mut dummy_buffer = [0u8; Sum6Kes::SIZE + 4];
346 let mut dummy_seed = [party_idx as u8; 32];
347 let (kes_secret_key, kes_verification_key) =
348 Sum6Kes::keygen(&mut dummy_buffer, &mut dummy_seed);
349 let mut kes_bytes = Sum6KesBytes([0u8; Sum6Kes::SIZE + 4]);
350 kes_bytes.0.copy_from_slice(&kes_secret_key.clone_sk());
351 let operational_certificate = OpCert::new(kes_verification_key, 0, 0, keypair);
352 let kes_secret_key_file = temp_dir.join(format!("kes{party_idx}.skey"));
353 kes_bytes
354 .to_file(&kes_secret_key_file)
355 .expect("KES secret key file export should not fail");
356 let operational_certificate_file = temp_dir.join(format!("pool{party_idx}.cert"));
357 operational_certificate
358 .to_file(&operational_certificate_file)
359 .expect("operational certificate file export should not fail");
360 let party_id = operational_certificate
361 .compute_protocol_party_id()
362 .expect("compute protocol party id should not fail");
363 (party_id, operational_certificate_file, kes_secret_key_file)
364 }
365
366 #[test]
367 fn test_vector_key_reg() {
368 let params = StmParameters {
369 m: 5,
370 k: 5,
371 phi_f: 1.0,
372 };
373 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
374
375 let (party_id_1, operational_certificate_file_1, kes_secret_key_file_1) =
376 create_cryptographic_material(1);
377 let (party_id_2, operational_certificate_file_2, kes_secret_key_file_2) =
378 create_cryptographic_material(2);
379
380 let mut key_reg = KeyRegWrapper::init(&vec![(party_id_1, 10), (party_id_2, 3)]);
381
382 let initializer_1 = StmInitializerWrapper::setup(
383 params,
384 Some(kes_secret_key_file_1),
385 Some(0),
386 10,
387 &mut rng,
388 )
389 .unwrap();
390
391 let opcert1 = OpCert::from_file(operational_certificate_file_1)
392 .expect("opcert deserialization should not fail")
393 .into();
394
395 let key_registration_1 = key_reg.register(
396 None,
397 Some(opcert1),
398 initializer_1.verification_key_signature(),
399 Some(0),
400 initializer_1.stm_initializer.verification_key().into(),
401 );
402 assert!(key_registration_1.is_ok());
403
404 let initializer_2 = StmInitializerWrapper::setup(
405 params,
406 Some(kes_secret_key_file_2),
407 Some(0),
408 10,
409 &mut rng,
410 )
411 .unwrap();
412
413 let opcert2 = OpCert::from_file(operational_certificate_file_2)
414 .expect("opcert deserialization should not fail")
415 .into();
416
417 let key_registration_2 = key_reg.register(
418 None,
419 Some(opcert2),
420 initializer_2.verification_key_signature(),
421 Some(0),
422 initializer_2.stm_initializer.verification_key().into(),
423 );
424 assert!(key_registration_2.is_ok())
425 }
426
427 const GOLDEN_STM_INITIALIZER_WRAPPER_JSON: &str = r#"
428 {
429 "stm_initializer": {
430 "stake": 9497432569,
431 "params": {
432 "m": 20973,
433 "k": 2422,
434 "phi_f": 0.2
435 },
436 "sk": [49, 181, 118, 110, 190, 161, 107, 218, 165, 20, 147, 129, 193, 79, 160, 0, 37, 23, 102, 223, 88, 174, 208, 70, 97, 79, 174, 51, 28, 0, 192, 210],
437 "pk": {
438 "vk": [173, 149, 133, 21, 100, 254, 36, 74, 165, 174, 56, 9, 145, 190, 48, 14, 12, 193, 243, 3, 200, 148, 221, 124, 170, 143, 89, 5, 168, 0, 226, 125, 61, 181, 190, 80, 62, 199, 99, 161, 117, 49, 65, 34, 81, 96, 34, 81, 2, 235, 173, 57, 58, 128, 49, 22, 242, 42, 30, 137, 6, 51, 77, 57, 142, 192, 140, 161, 206, 206, 213, 114, 156, 191, 127, 167, 167, 9, 39, 29, 97, 166, 134, 76, 55, 179, 72, 29, 41, 251, 14, 71, 89, 181, 31, 115],
439 "pop": [171, 0, 214, 91, 37, 208, 228, 71, 228, 31, 138, 0, 237, 175, 24, 45, 160, 117, 14, 210, 23, 46, 235, 83, 45, 9, 58, 207, 18, 36, 31, 160, 252, 111, 69, 102, 248, 205, 46, 71, 24, 38, 41, 77, 29, 129, 95, 16, 136, 114, 250, 44, 230, 184, 222, 122, 120, 58, 249, 103, 48, 121, 141, 244, 243, 26, 252, 60, 230, 64, 75, 3, 86, 107, 198, 198, 117, 242, 107, 104, 219, 209, 211, 255, 174, 203, 43, 141, 34, 146, 25, 181, 212, 38, 194, 99]
440 }
441 },
442 "kes_signature": {
443 "sigma": {
444 "sigma": {
445 "sigma": {
446 "sigma": {
447 "sigma": {
448 "sigma": [71, 225, 146, 98, 81, 62, 28, 21, 7, 157, 88, 4, 226, 126, 27, 133, 146, 171, 216, 170, 77, 17, 38, 146, 98, 202, 35, 87, 166, 162, 25, 207, 105, 174, 48, 225, 152, 68, 19, 109, 72, 241, 69, 111, 22, 214, 72, 20, 81, 56, 181, 104, 69, 121, 173, 194, 37, 60, 16, 155, 86, 99, 253, 7],
449 "lhs_pk": [
450 91, 82, 235, 39, 167, 29, 141, 253, 163, 163, 55, 185, 162, 191, 52, 8, 245, 7, 104, 22, 182, 239, 133, 138, 131, 15, 233, 116, 147, 251, 182, 140],
451 "rhs_pk": [189, 26, 9, 118, 59, 34, 225, 34, 104, 202, 192, 7, 66, 150, 137, 75, 106, 7, 22, 234, 42, 94, 139, 65, 241, 65, 1, 190, 153, 16, 221, 87]
452 },
453 "lhs_pk": [206, 50, 185, 93, 20, 234, 100, 168, 163, 125, 95, 201, 162, 104, 35, 2, 205, 41, 180, 73, 107, 140, 79, 182, 173, 17, 172, 49, 51, 85, 180, 5],
454 "rhs_pk": [68, 40, 90, 110, 254, 68, 87, 12, 19, 21, 252, 197, 69, 255, 33, 172, 140, 70, 79, 39, 71, 217, 12, 254, 82, 125, 123, 148, 221, 217, 141, 194]
455 },
456 "lhs_pk": [155, 2, 30, 71, 52, 89, 112, 247, 108, 177, 144, 212, 206, 254, 87, 126, 180, 207, 146, 223, 164, 246, 178, 62, 148, 96, 39, 136, 106, 36, 253, 56],
457 "rhs_pk": [155, 140, 124, 154, 235, 97, 51, 77, 208, 24, 45, 219, 199, 232, 222, 26, 160, 62, 38, 253, 121, 241, 219, 233, 36, 50, 60, 182, 127, 255, 132, 245]
458 },
459 "lhs_pk": [172, 176, 18, 228, 203, 85, 44, 151, 221, 13, 91, 250, 67, 232, 114, 16, 251, 13, 115, 233, 214, 194, 102, 199, 200, 124, 30, 190, 143, 18, 85, 75],
460 "rhs_pk": [100, 192, 98, 123, 150, 116, 55, 42, 207, 44, 181, 31, 203, 65, 237, 13, 55, 246, 185, 211, 149, 245, 245, 219, 183, 41, 237, 253, 128, 231, 161, 226]
461 },
462 "lhs_pk": [112, 16, 177, 142, 158, 1, 36, 210, 87, 165, 5, 195, 199, 61, 13, 195, 219, 26, 231, 103, 163, 223, 54, 16, 106, 0, 252, 69, 242, 31, 210, 167],
463 "rhs_pk": [15, 246, 81, 72, 172, 15, 170, 235, 10, 64, 229, 233, 169, 140, 179, 209, 244, 183, 3, 59, 2, 252, 233, 229, 13, 190, 196, 208, 109, 30, 73, 113]
464 },
465 "lhs_pk": [114, 238, 75, 184, 228, 147, 37, 72, 134, 65, 139, 64, 81, 114, 157, 148, 197, 108, 80, 89, 30, 235, 75, 108, 193, 53, 185, 15, 57, 61, 181, 119],
466 "rhs_pk": [82, 28, 113, 114, 168, 192, 222, 110, 96, 15, 28, 179, 164, 180, 76, 87, 254, 72, 48, 154, 167, 102, 220, 74, 76, 136, 45, 105, 243, 87, 165, 212]
467 }
468 }
469 "#;
470
471 #[test]
472 fn golden_initializer_deserialization() {
473 let _: StmInitializerWrapper = serde_json::from_str(GOLDEN_STM_INITIALIZER_WRAPPER_JSON)
474 .expect("Deserializing a StmInitializerWrapper should not fail");
475 }
476
477 #[test]
478 fn test_initializer_wrapper_conversions() {
479 let stm_initializer_wrapper_json = GOLDEN_STM_INITIALIZER_WRAPPER_JSON;
480
481 let stm_initializer_wrapper_from_json: StmInitializerWrapper =
482 serde_json::from_str(stm_initializer_wrapper_json)
483 .expect("Deserializing a StmInitializerWrapper should not fail");
484 let stm_initializer_wrapper_from_json_to_json =
485 serde_json::to_string(&stm_initializer_wrapper_from_json)
486 .expect("Serializing a StmInitializerWrapper to json should not fail");
487
488 let stm_initializer_wrapper_from_bytes =
489 StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
490 .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
491 let stm_initializer_wrapper_from_bytes_to_json =
492 serde_json::to_string(&stm_initializer_wrapper_from_bytes)
493 .expect("Serializing a StmInitializerWrapper to json should not fail");
494
495 assert_eq!(
496 stm_initializer_wrapper_from_json_to_json,
497 stm_initializer_wrapper_from_bytes_to_json
498 );
499
500 let mut stm_initializer_wrapper_from_json = stm_initializer_wrapper_from_json;
501 stm_initializer_wrapper_from_json.kes_signature = None;
502
503 let stm_initializer_wrapper_from_bytes =
504 StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
505 .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
506 assert_eq!(None, stm_initializer_wrapper_from_bytes.kes_signature);
507 }
508}