mithril_common/crypto_helper/cardano/
key_certification.rs

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