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::new(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.get_verification_key_proof_of_possession().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.get_verification_key_proof_of_possession()
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            .create_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
219/// Wrapper structure for [MithrilStm:KeyRegistration](mithril_stm::key_reg::KeyRegistration).
220/// The wrapper not only contains a map between `Mithril vkey <-> Stake`, but also
221/// a map `PoolID <-> Stake`. This information is recovered from the node state, and
222/// is used to verify the identity of a Mithril signer. Furthermore, the `register` function
223/// of the wrapper forces the registrar to check that the KES signature over the Mithril key
224/// is valid with respect to the PoolID.
225#[derive(Debug, Clone)]
226pub struct KeyRegWrapper {
227    kes_verifier: Arc<dyn KesVerifier>,
228    stm_key_reg: KeyRegistration,
229    stake_distribution: HashMap<ProtocolPartyId, Stake>,
230}
231
232impl KeyRegWrapper {
233    /// New Initialisation function. We temporarily keep the other init function,
234    /// but we should eventually transition to only use this one.
235    pub fn init(stake_dist: &ProtocolStakeDistribution) -> Self {
236        Self {
237            kes_verifier: Arc::new(KesVerifierStandard),
238            stm_key_reg: KeyRegistration::init(),
239            stake_distribution: HashMap::from_iter(stake_dist.to_vec()),
240        }
241    }
242
243    /// Register a new party. For a successful registration, the registrar needs to
244    /// provide the OpCert (in cbor form), the cold VK, a KES signature, and a
245    /// Mithril key (with its corresponding Proof of Possession).
246    pub fn register(
247        &mut self,
248        party_id: Option<ProtocolPartyId>, // Used for only for testing when SPO pool id is not certified
249        opcert: Option<ProtocolOpCert>, // Used for only for testing when SPO pool id is not certified
250        kes_sig: Option<ProtocolSignerVerificationKeySignature>, // Used for only for testing when SPO pool id is not certified
251        kes_period: Option<KesPeriod>,
252        pk: ProtocolSignerVerificationKey,
253    ) -> Result<ProtocolPartyId, ProtocolRegistrationErrorWrapper> {
254        let pool_id_bech32: ProtocolPartyId = if let Some(opcert) = opcert {
255            let signature = kes_sig.ok_or(ProtocolRegistrationErrorWrapper::KesSignatureMissing)?;
256            let kes_period =
257                kes_period.ok_or(ProtocolRegistrationErrorWrapper::KesPeriodMissing)?;
258            if self
259                .kes_verifier
260                .verify(&pk.to_bytes(), &signature, &opcert, kes_period)
261                .is_ok()
262            {
263                opcert
264                    .compute_protocol_party_id()
265                    .map_err(|_| ProtocolRegistrationErrorWrapper::PoolAddressEncoding)?
266            } else {
267                return Err(ProtocolRegistrationErrorWrapper::KesSignatureInvalid(
268                    kes_period,
269                    opcert.start_kes_period,
270                ));
271            }
272        } else {
273            if cfg!(not(feature = "allow_skip_signer_certification")) {
274                Err(ProtocolRegistrationErrorWrapper::OpCertMissing)?
275            }
276            party_id.ok_or(ProtocolRegistrationErrorWrapper::PartyIdMissing)?
277        };
278
279        if let Some(&stake) = self.stake_distribution.get(&pool_id_bech32) {
280            self.stm_key_reg
281                .register(stake, pk.into())
282                .map_err(ProtocolRegistrationErrorWrapper::CoreRegister)?;
283            return Ok(pool_id_bech32);
284        }
285        Err(ProtocolRegistrationErrorWrapper::PartyIdNonExisting)
286    }
287
288    /// Finalize the key registration.
289    /// This function disables `ClosedKeyRegistration::register`, consumes the instance of `self`, and returns a `ClosedKeyRegistration`.
290    pub fn close<D: Digest + FixedOutput>(self) -> ClosedKeyRegistration<D> {
291        self.stm_key_reg.close()
292    }
293}
294
295mod test_extensions {
296    use crate::test::crypto_helper::ProtocolInitializerTestExtension;
297
298    use super::*;
299
300    impl ProtocolInitializerTestExtension for StmInitializerWrapper {
301        fn override_protocol_parameters(&mut self, protocol_parameters: &ProtocolParameters) {
302            self.stm_initializer.params = protocol_parameters.to_owned();
303        }
304    }
305}
306
307#[cfg(test)]
308mod test {
309    use crate::crypto_helper::cardano::kes::KesSignerStandard;
310    use crate::crypto_helper::{OpCert, SerDeShelleyFileFormat};
311    use crate::test::crypto_helper::{
312        KesCryptographicMaterialForTest, KesPartyIndexForTest, create_kes_cryptographic_material,
313    };
314
315    use rand_chacha::ChaCha20Rng;
316    use rand_core::SeedableRng;
317
318    use super::*;
319
320    #[test]
321    fn test_vector_key_reg() {
322        let params = Parameters {
323            m: 5,
324            k: 5,
325            phi_f: 1.0,
326        };
327        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
328        let KesCryptographicMaterialForTest {
329            party_id: party_id_1,
330            operational_certificate_file: operational_certificate_file_1,
331            kes_secret_key_file: kes_secret_key_file_1,
332        } = create_kes_cryptographic_material(
333            1 as KesPartyIndexForTest,
334            0 as KesPeriod,
335            "test_vector_key_reg",
336        );
337        let KesCryptographicMaterialForTest {
338            party_id: party_id_2,
339            operational_certificate_file: operational_certificate_file_2,
340            kes_secret_key_file: kes_secret_key_file_2,
341        } = create_kes_cryptographic_material(
342            2 as KesPartyIndexForTest,
343            0 as KesPeriod,
344            "test_vector_key_reg",
345        );
346
347        let mut key_reg = KeyRegWrapper::init(&vec![(party_id_1, 10), (party_id_2, 3)]);
348
349        let initializer_1 = StmInitializerWrapper::setup(
350            params,
351            Some(Arc::new(KesSignerStandard::new(
352                kes_secret_key_file_1,
353                operational_certificate_file_1.clone(),
354            ))),
355            Some(0),
356            10,
357            &mut rng,
358        )
359        .unwrap();
360
361        let opcert1 = OpCert::from_file(operational_certificate_file_1)
362            .expect("opcert deserialization should not fail")
363            .into();
364
365        let key_registration_1 = key_reg.register(
366            None,
367            Some(opcert1),
368            initializer_1.verification_key_signature(),
369            Some(0),
370            initializer_1
371                .stm_initializer
372                .get_verification_key_proof_of_possession()
373                .into(),
374        );
375        assert!(key_registration_1.is_ok());
376
377        let initializer_2 = StmInitializerWrapper::setup(
378            params,
379            Some(Arc::new(KesSignerStandard::new(
380                kes_secret_key_file_2,
381                operational_certificate_file_2.clone(),
382            ))),
383            Some(0),
384            10,
385            &mut rng,
386        )
387        .unwrap();
388
389        let opcert2 = OpCert::from_file(operational_certificate_file_2)
390            .expect("opcert deserialization should not fail")
391            .into();
392
393        let key_registration_2 = key_reg.register(
394            None,
395            Some(opcert2),
396            initializer_2.verification_key_signature(),
397            Some(0),
398            initializer_2
399                .stm_initializer
400                .get_verification_key_proof_of_possession()
401                .into(),
402        );
403        assert!(key_registration_2.is_ok())
404    }
405
406    const GOLDEN_STM_INITIALIZER_WRAPPER_JSON: &str = r#"
407    {
408        "stm_initializer": {
409            "stake": 9497432569,
410            "params": {
411                "m": 20973,
412                "k": 2422,
413                "phi_f": 0.2
414            },
415            "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],
416            "pk": {
417                "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],
418                "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]
419            }
420        },
421        "kes_signature": {
422            "sigma": {
423                "sigma": {
424                    "sigma": {
425                        "sigma": {
426                            "sigma": {
427                                "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],
428                                "lhs_pk": [
429                                    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],
430                                    "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]
431                                    },
432                            "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],
433                            "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]
434                        },
435                        "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],
436                        "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]
437                    },
438                    "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],
439                    "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]
440                },
441                "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],
442                "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]
443            },
444            "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],
445            "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]
446        }
447    }
448    "#;
449
450    #[test]
451    fn golden_initializer_deserialization() {
452        let _: StmInitializerWrapper = serde_json::from_str(GOLDEN_STM_INITIALIZER_WRAPPER_JSON)
453            .expect("Deserializing a StmInitializerWrapper should not fail");
454    }
455
456    #[test]
457    fn test_initializer_wrapper_conversions() {
458        let stm_initializer_wrapper_json = GOLDEN_STM_INITIALIZER_WRAPPER_JSON;
459
460        let stm_initializer_wrapper_from_json: StmInitializerWrapper =
461            serde_json::from_str(stm_initializer_wrapper_json)
462                .expect("Deserializing a StmInitializerWrapper should not fail");
463        let stm_initializer_wrapper_from_json_to_json =
464            serde_json::to_string(&stm_initializer_wrapper_from_json)
465                .expect("Serializing a StmInitializerWrapper to json should not fail");
466
467        let stm_initializer_wrapper_from_bytes =
468            StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
469                .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
470        let stm_initializer_wrapper_from_bytes_to_json =
471            serde_json::to_string(&stm_initializer_wrapper_from_bytes)
472                .expect("Serializing a StmInitializerWrapper to json should not fail");
473
474        assert_eq!(
475            stm_initializer_wrapper_from_json_to_json,
476            stm_initializer_wrapper_from_bytes_to_json
477        );
478
479        let mut stm_initializer_wrapper_from_json = stm_initializer_wrapper_from_json;
480        stm_initializer_wrapper_from_json.kes_signature = None;
481
482        let stm_initializer_wrapper_from_bytes =
483            StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
484                .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
485        assert_eq!(None, stm_initializer_wrapper_from_bytes.kes_signature);
486    }
487}