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::{
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
37// Protocol types alias
38type D = Blake2b<U32>;
39
40/// The KES period that is used to check if the KES keys is expired
41pub type KESPeriod = u32;
42
43/// New registration error
44#[derive(Error, Debug)]
45pub enum ProtocolRegistrationErrorWrapper {
46    /// Error raised when a party id is needed but not provided
47    ///
48    /// Used only for testing when SPO pool id is not certified
49    #[error("missing party id")]
50    PartyIdMissing,
51
52    /// Error raised when a party id is not available in the Cardano_stake distribution
53    #[error("party id does not exist in the stake distribution")]
54    PartyIdNonExisting,
55
56    /// Error raised when the operational certificate is missing
57    #[error("missing operational certificate")]
58    OpCertMissing,
59
60    /// Error raised when an operational certificate is invalid
61    #[error("invalid operational certificate")]
62    OpCertInvalid,
63
64    /// Error raised when a KES Signature verification fails
65    #[error("KES signature verification error: CurrentKesPeriod={0}, StartKesPeriod={1}")]
66    KesSignatureInvalid(u32, u64),
67
68    /// Error raised when a KES Signature is needed but not provided
69    #[error("missing KES signature")]
70    KesSignatureMissing,
71
72    /// Error raised when a KES Period is needed but not provided
73    #[error("missing KES period")]
74    KesPeriodMissing,
75
76    /// Error raised when a pool address encoding fails
77    #[error("pool address encoding error")]
78    PoolAddressEncoding,
79
80    /// Error raised when a core registration error occurs
81    #[error("core registration error")]
82    CoreRegister(#[source] RegisterError),
83}
84
85/// New initializer error
86#[derive(Error, Debug)]
87pub enum ProtocolInitializerErrorWrapper {
88    /// Error raised when the underlying protocol initializer fails
89    #[error("protocol initializer error")]
90    ProtocolInitializer(#[source] StdError),
91
92    /// Error raised when a KES update error occurs
93    #[error("KES key cannot be updated for period {0}")]
94    KesUpdate(KESPeriod),
95
96    /// Period of key file does not match with period provided by user
97    #[error("Period of key file, {0}, does not match with period provided by user, {1}")]
98    KesMismatch(KESPeriod, KESPeriod),
99}
100
101/// Wrapper structure for [MithrilStm:StmInitializer](mithril_stm::stm::StmInitializer).
102/// It now obtains a KES signature over the Mithril key. This allows the signers prove
103/// their correct identity with respect to a Cardano PoolID.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct StmInitializerWrapper {
106    /// The StmInitializer
107    stm_initializer: StmInitializer,
108
109    /// The KES signature over the Mithril key
110    ///
111    /// None is used only for testing when SPO pool id is not certified
112    kes_signature: Option<Sum6KesSig>,
113}
114
115/// Wrapper structure for [MithrilStm:KeyReg](mithril_stm::key_reg::KeyReg).
116/// The wrapper not only contains a map between `Mithril vkey <-> Stake`, but also
117/// a map `PoolID <-> Stake`. This information is recovered from the node state, and
118/// is used to verify the identity of a Mithril signer. Furthermore, the `register` function
119/// of the wrapper forces the registrar to check that the KES signature over the Mithril key
120/// is valid with respect to the PoolID.
121#[derive(Debug, Clone)]
122pub struct KeyRegWrapper {
123    stm_key_reg: KeyReg,
124    stake_distribution: HashMap<ProtocolPartyId, Stake>,
125}
126
127impl StmInitializerWrapper {
128    /// Builds an `StmInitializer` that is ready to register with the key registration service.
129    /// This function generates the signing and verification key with a PoP, signs the verification
130    /// key with a provided KES signing key, and initializes the structure.
131    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            // We need to perform the evolutions
156            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    /// Extract the verification key.
175    pub fn verification_key(&self) -> StmVerificationKeyPoP {
176        self.stm_initializer.verification_key()
177    }
178
179    /// Extract the verification key signature.
180    pub fn verification_key_signature(&self) -> Option<ProtocolSignerVerificationKeySignature> {
181        self.kes_signature.map(|k| k.into())
182    }
183
184    /// Extract the protocol parameters of the initializer
185    pub fn get_protocol_parameters(&self) -> ProtocolParameters {
186        self.stm_initializer.params
187    }
188
189    /// Extract the stake of the party
190    pub fn get_stake(&self) -> Stake {
191        self.stm_initializer.stake
192    }
193
194    /// Build the `avk` for the given list of parties.
195    ///
196    /// Note that if this StmInitializer was modified *between* the last call to `register`,
197    /// then the resulting `StmSigner` may not be able to produce valid signatures.
198    ///
199    /// Returns a `StmSignerWrapper` specialized to
200    /// * this `StmSignerWrapper`'s ID and current stake
201    /// * this `StmSignerWrapper`'s parameter valuation
202    /// * the `avk` as built from the current registered parties (according to the registration service)
203    /// * the current total stake (according to the registration service)
204    /// # Error
205    /// This function fails if the initializer is not registered.
206    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    /// Convert to bytes
216    /// # Layout
217    /// * StmInitialiser
218    /// * KesSignature
219    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    /// Convert a slice of bytes to an `StmInitializerWrapper`
230    /// # Error
231    /// The function fails if the given string of bytes is not of required size.
232    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        /// Override the protocol parameters of the `StmInitializer` for testing purposes only.
251        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    /// New Initialisation function. We temporarily keep the other init function,
259    /// but we should eventually transition to only use this one.
260    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    /// Register a new party. For a successful registration, the registrar needs to
268    /// provide the OpCert (in cbor form), the cold VK, a KES signature, and a
269    /// Mithril key (with its corresponding Proof of Possession).
270    pub fn register(
271        &mut self,
272        party_id: Option<ProtocolPartyId>, // Used for only for testing when SPO pool id is not certified
273        opcert: Option<ProtocolOpCert>, // Used for only for testing when SPO pool id is not certified
274        kes_sig: Option<ProtocolSignerVerificationKeySignature>, // Used for only for testing when SPO pool id is not certified
275        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    /// Finalize the key registration.
322    /// This function disables `KeyReg::register`, consumes the instance of `self`, and returns a `ClosedKeyReg`.
323    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}