mithril_common/crypto_helper/cardano/
key_certification.rs

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