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