mithril_common/crypto_helper/cardano/kes/
signer_with_key.rs

1use std::path::PathBuf;
2
3use anyhow::{Context, anyhow};
4use kes_summed_ed25519::{
5    kes::{Sum6Kes, Sum6KesSig},
6    traits::KesSk,
7};
8
9use crate::{
10    StdResult,
11    crypto_helper::{
12        KesPeriod, OpCert, SerDeShelleyFileFormat, Sum6KesBytes,
13        cardano::{KesSignError, KesSigner},
14    },
15};
16
17/// Standard KES Signer implementation which uses a KES secret key.
18pub struct KesSignerStandard {
19    kes_sk_path: PathBuf,
20    operational_certificate_path: PathBuf,
21}
22
23impl KesSignerStandard {
24    /// Create a new instance of `StandardKesSigner`.
25    pub fn new(kes_sk_path: PathBuf, operational_certificate_path: PathBuf) -> Self {
26        Self {
27            kes_sk_path,
28            operational_certificate_path,
29        }
30    }
31}
32
33impl KesSigner for KesSignerStandard {
34    fn sign(
35        &self,
36        message: &[u8],
37        current_kes_period: KesPeriod,
38    ) -> StdResult<(Sum6KesSig, OpCert)> {
39        let mut kes_sk_bytes = Sum6KesBytes::from_file(&self.kes_sk_path)
40            .with_context(|| "StandardKesSigner can not read KES secret key from file")?;
41        let mut kes_sk = Sum6Kes::try_from(&mut kes_sk_bytes)
42            .with_context(|| "StandardKesSigner can not use KES secret key")?;
43        let operational_certificate = OpCert::from_file(&self.operational_certificate_path)
44            .with_context(|| "StandardKesSigner can not read operational certificate from file")?;
45        let kes_period_start = operational_certificate.get_start_kes_period() as u32;
46        if kes_period_start > current_kes_period {
47            return Err(anyhow!(KesSignError::PeriodMismatch(
48                kes_period_start,
49                current_kes_period
50            )));
51        }
52        let kes_evolutions = current_kes_period.saturating_sub(kes_period_start);
53
54        // We need to perform the evolutions
55        for evolution in 0..kes_evolutions {
56            kes_sk.update().map_err(|_| KesSignError::UpdateKey(evolution))?;
57        }
58
59        Ok((kes_sk.sign(message), operational_certificate))
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    use crate::crypto_helper::cardano::kes::{KesVerifier, KesVerifierStandard};
68    use crate::current_function;
69    use crate::test::crypto_helper::{
70        KesCryptographicMaterialForTest, KesPartyIndexForTest, create_kes_cryptographic_material,
71    };
72
73    #[test]
74    fn create_valid_signature_for_message() {
75        let start_kes_period = 10 as KesPeriod;
76        let kes_evolutions = 32;
77        let signing_kes_period = start_kes_period + kes_evolutions;
78        let KesCryptographicMaterialForTest {
79            party_id: _,
80            operational_certificate_file,
81            kes_secret_key_file,
82        } = create_kes_cryptographic_material(
83            1 as KesPartyIndexForTest,
84            start_kes_period,
85            current_function!(),
86        );
87        let message = b"Test message for KES signing";
88        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
89
90        let (signature, op_cert) = kes_signer
91            .sign(message, signing_kes_period)
92            .expect("Signing should not fail");
93
94        KesVerifierStandard
95            .verify(message, &signature, &op_cert, kes_evolutions)
96            .expect("Signature verification should not fail");
97    }
98
99    #[test]
100    fn create_invalid_signature_for_different_message() {
101        let start_kes_period = 10 as KesPeriod;
102        let kes_evolutions = 32;
103        let signing_kes_period = start_kes_period + kes_evolutions;
104        let KesCryptographicMaterialForTest {
105            party_id: _,
106            operational_certificate_file,
107            kes_secret_key_file,
108        } = create_kes_cryptographic_material(
109            1 as KesPartyIndexForTest,
110            start_kes_period,
111            current_function!(),
112        );
113        let message = b"Test message for KES signing";
114        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
115
116        let (signature, op_cert) = kes_signer
117            .sign(message, signing_kes_period)
118            .expect("Signing should not fail");
119
120        KesVerifierStandard
121            .verify(b"Different message", &signature, &op_cert, kes_evolutions)
122            .expect_err("Signature verification should fail");
123    }
124
125    #[test]
126    fn create_invalid_signature_for_invalid_current_kes_period() {
127        let start_kes_period = 10 as KesPeriod;
128        let signing_kes_period = 5;
129        let KesCryptographicMaterialForTest {
130            party_id: _,
131            operational_certificate_file,
132            kes_secret_key_file,
133        } = create_kes_cryptographic_material(
134            1 as KesPartyIndexForTest,
135            start_kes_period,
136            current_function!(),
137        );
138        let message = b"Test message for KES signing";
139        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
140
141        let res = kes_signer
142            .sign(message, signing_kes_period)
143            .expect_err("Signing should fail");
144        assert_eq!(
145            res.downcast_ref::<KesSignError>(),
146            Some(&KesSignError::PeriodMismatch(
147                start_kes_period,
148                signing_kes_period
149            ))
150        );
151    }
152
153    #[test]
154    fn create_invalid_signature_for_invalid_kes_evolutions() {
155        const MAX_KES_EVOLUTIONS: KesPeriod = 63;
156        let start_kes_period = 10 as KesPeriod;
157        let signing_kes_period = start_kes_period + MAX_KES_EVOLUTIONS + 1;
158        let KesCryptographicMaterialForTest {
159            party_id: _,
160            operational_certificate_file,
161            kes_secret_key_file,
162        } = create_kes_cryptographic_material(
163            1 as KesPartyIndexForTest,
164            start_kes_period,
165            current_function!(),
166        );
167        let message = b"Test message for KES signing";
168        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
169
170        let res = kes_signer
171            .sign(message, signing_kes_period)
172            .expect_err("Signing should fail");
173        assert_eq!(
174            res.downcast_ref::<KesSignError>(),
175            Some(&KesSignError::UpdateKey(MAX_KES_EVOLUTIONS))
176        );
177    }
178}