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
295#[cfg(any(test, feature = "test_tools"))]
296mod test_extensions {
297    use crate::test::crypto_helper::ProtocolInitializerTestExtension;
298
299    use super::*;
300
301    impl ProtocolInitializerTestExtension for StmInitializerWrapper {
302        fn override_protocol_parameters(&mut self, protocol_parameters: &ProtocolParameters) {
303            self.stm_initializer.params = protocol_parameters.to_owned();
304        }
305    }
306}
307
308#[cfg(test)]
309mod test {
310    use crate::crypto_helper::cardano::kes::KesSignerStandard;
311    use crate::crypto_helper::{OpCert, SerDeShelleyFileFormat};
312    use crate::test::crypto_helper::{
313        KesCryptographicMaterialForTest, KesPartyIndexForTest, create_kes_cryptographic_material,
314    };
315
316    use rand_chacha::ChaCha20Rng;
317    use rand_core::SeedableRng;
318
319    use super::*;
320
321    #[test]
322    fn test_vector_key_reg() {
323        let params = Parameters {
324            m: 5,
325            k: 5,
326            phi_f: 1.0,
327        };
328        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
329        let KesCryptographicMaterialForTest {
330            party_id: party_id_1,
331            operational_certificate_file: operational_certificate_file_1,
332            kes_secret_key_file: kes_secret_key_file_1,
333        } = create_kes_cryptographic_material(
334            1 as KesPartyIndexForTest,
335            0 as KesPeriod,
336            "test_vector_key_reg",
337        );
338        let KesCryptographicMaterialForTest {
339            party_id: party_id_2,
340            operational_certificate_file: operational_certificate_file_2,
341            kes_secret_key_file: kes_secret_key_file_2,
342        } = create_kes_cryptographic_material(
343            2 as KesPartyIndexForTest,
344            0 as KesPeriod,
345            "test_vector_key_reg",
346        );
347
348        let mut key_reg = KeyRegWrapper::init(&vec![(party_id_1, 10), (party_id_2, 3)]);
349
350        let initializer_1 = StmInitializerWrapper::setup(
351            params,
352            Some(Arc::new(KesSignerStandard::new(
353                kes_secret_key_file_1,
354                operational_certificate_file_1.clone(),
355            ))),
356            Some(0),
357            10,
358            &mut rng,
359        )
360        .unwrap();
361
362        let opcert1 = OpCert::from_file(operational_certificate_file_1)
363            .expect("opcert deserialization should not fail")
364            .into();
365
366        let key_registration_1 = key_reg.register(
367            None,
368            Some(opcert1),
369            initializer_1.verification_key_signature(),
370            Some(0),
371            initializer_1
372                .stm_initializer
373                .get_verification_key_proof_of_possession()
374                .into(),
375        );
376        assert!(key_registration_1.is_ok());
377
378        let initializer_2 = StmInitializerWrapper::setup(
379            params,
380            Some(Arc::new(KesSignerStandard::new(
381                kes_secret_key_file_2,
382                operational_certificate_file_2.clone(),
383            ))),
384            Some(0),
385            10,
386            &mut rng,
387        )
388        .unwrap();
389
390        let opcert2 = OpCert::from_file(operational_certificate_file_2)
391            .expect("opcert deserialization should not fail")
392            .into();
393
394        let key_registration_2 = key_reg.register(
395            None,
396            Some(opcert2),
397            initializer_2.verification_key_signature(),
398            Some(0),
399            initializer_2
400                .stm_initializer
401                .get_verification_key_proof_of_possession()
402                .into(),
403        );
404        assert!(key_registration_2.is_ok())
405    }
406
407    const GOLDEN_STM_INITIALIZER_WRAPPER_JSON: &str = r#"
408    {
409        "stm_initializer": {
410            "stake": 9497432569,
411            "params": {
412                "m": 20973,
413                "k": 2422,
414                "phi_f": 0.2
415            },
416            "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],
417            "pk": {
418                "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],
419                "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]
420            }
421        },
422        "kes_signature": {
423            "sigma": {
424                "sigma": {
425                    "sigma": {
426                        "sigma": {
427                            "sigma": {
428                                "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],
429                                "lhs_pk": [
430                                    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],
431                                    "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]
432                                    },
433                            "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],
434                            "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]
435                        },
436                        "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],
437                        "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]
438                    },
439                    "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],
440                    "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]
441                },
442                "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],
443                "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]
444            },
445            "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],
446            "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]
447        }
448    }
449    "#;
450
451    #[test]
452    fn golden_initializer_deserialization() {
453        let _: StmInitializerWrapper = serde_json::from_str(GOLDEN_STM_INITIALIZER_WRAPPER_JSON)
454            .expect("Deserializing a StmInitializerWrapper should not fail");
455    }
456
457    #[test]
458    fn test_initializer_wrapper_conversions() {
459        let stm_initializer_wrapper_json = GOLDEN_STM_INITIALIZER_WRAPPER_JSON;
460
461        let stm_initializer_wrapper_from_json: StmInitializerWrapper =
462            serde_json::from_str(stm_initializer_wrapper_json)
463                .expect("Deserializing a StmInitializerWrapper should not fail");
464        let stm_initializer_wrapper_from_json_to_json =
465            serde_json::to_string(&stm_initializer_wrapper_from_json)
466                .expect("Serializing a StmInitializerWrapper to json should not fail");
467
468        let stm_initializer_wrapper_from_bytes =
469            StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
470                .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
471        let stm_initializer_wrapper_from_bytes_to_json =
472            serde_json::to_string(&stm_initializer_wrapper_from_bytes)
473                .expect("Serializing a StmInitializerWrapper to json should not fail");
474
475        assert_eq!(
476            stm_initializer_wrapper_from_json_to_json,
477            stm_initializer_wrapper_from_bytes_to_json
478        );
479
480        let mut stm_initializer_wrapper_from_json = stm_initializer_wrapper_from_json;
481        stm_initializer_wrapper_from_json.kes_signature = None;
482
483        let stm_initializer_wrapper_from_bytes =
484            StmInitializerWrapper::from_bytes(&stm_initializer_wrapper_from_json.to_bytes())
485                .expect("Deserializing a StmInitializerWrapper from bytes should not fail");
486        assert_eq!(None, stm_initializer_wrapper_from_bytes.kes_signature);
487    }
488}