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