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        KesEvolutions, 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();
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 - kes_period_start;
53
54        // We need to perform the evolutions
55        for evolution in 0..*kes_evolutions {
56            kes_sk
57                .update()
58                .map_err(|_| KesSignError::UpdateKey(KesEvolutions(evolution)))?;
59        }
60
61        Ok((kes_sk.sign(message), operational_certificate))
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    use crate::crypto_helper::KesEvolutions;
70    use crate::crypto_helper::cardano::kes::{KesVerifier, KesVerifierStandard};
71    use crate::current_function;
72    use crate::test::crypto_helper::{
73        KesCryptographicMaterialForTest, KesPartyIndexForTest, create_kes_cryptographic_material,
74    };
75
76    #[test]
77    fn create_valid_signature_for_message() {
78        let start_kes_period = KesPeriod(10);
79        let kes_evolutions = KesEvolutions(32);
80        let signing_kes_period = start_kes_period + kes_evolutions;
81        let KesCryptographicMaterialForTest {
82            party_id: _,
83            operational_certificate_file,
84            kes_secret_key_file,
85        } = create_kes_cryptographic_material(
86            1 as KesPartyIndexForTest,
87            start_kes_period,
88            current_function!(),
89        );
90        let message = b"Test message for KES signing";
91        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
92
93        let (signature, op_cert) = kes_signer
94            .sign(message, signing_kes_period)
95            .expect("Signing should not fail");
96
97        KesVerifierStandard
98            .verify(message, &signature, &op_cert, kes_evolutions)
99            .expect("Signature verification should not fail");
100    }
101
102    #[test]
103    fn create_invalid_signature_for_different_message() {
104        let start_kes_period = KesPeriod(10);
105        let kes_evolutions = KesEvolutions(32);
106        let signing_kes_period = start_kes_period + kes_evolutions;
107        let KesCryptographicMaterialForTest {
108            party_id: _,
109            operational_certificate_file,
110            kes_secret_key_file,
111        } = create_kes_cryptographic_material(
112            1 as KesPartyIndexForTest,
113            start_kes_period,
114            current_function!(),
115        );
116        let message = b"Test message for KES signing";
117        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
118
119        let (signature, op_cert) = kes_signer
120            .sign(message, signing_kes_period)
121            .expect("Signing should not fail");
122
123        KesVerifierStandard
124            .verify(b"Different message", &signature, &op_cert, kes_evolutions)
125            .expect_err("Signature verification should fail");
126    }
127
128    #[test]
129    fn create_invalid_signature_for_invalid_current_kes_period() {
130        let start_kes_period = KesPeriod(10);
131        let signing_kes_period = KesPeriod(5);
132        let KesCryptographicMaterialForTest {
133            party_id: _,
134            operational_certificate_file,
135            kes_secret_key_file,
136        } = create_kes_cryptographic_material(
137            1 as KesPartyIndexForTest,
138            start_kes_period,
139            current_function!(),
140        );
141        let message = b"Test message for KES signing";
142        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
143
144        let res = kes_signer
145            .sign(message, signing_kes_period)
146            .expect_err("Signing should fail");
147        assert_eq!(
148            res.downcast_ref::<KesSignError>(),
149            Some(&KesSignError::PeriodMismatch(
150                start_kes_period,
151                signing_kes_period
152            ))
153        );
154    }
155
156    #[test]
157    fn create_invalid_signature_for_invalid_kes_evolutions() {
158        const MAX_KES_EVOLUTIONS: KesEvolutions = KesEvolutions(63);
159        let start_kes_period = KesPeriod(10);
160        let signing_kes_period = start_kes_period + MAX_KES_EVOLUTIONS + 1;
161        let KesCryptographicMaterialForTest {
162            party_id: _,
163            operational_certificate_file,
164            kes_secret_key_file,
165        } = create_kes_cryptographic_material(
166            1 as KesPartyIndexForTest,
167            start_kes_period,
168            current_function!(),
169        );
170        let message = b"Test message for KES signing";
171        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
172
173        let res = kes_signer
174            .sign(message, signing_kes_period)
175            .expect_err("Signing should fail");
176        assert_eq!(
177            res.downcast_ref::<KesSignError>(),
178            Some(&KesSignError::UpdateKey(MAX_KES_EVOLUTIONS))
179        );
180    }
181}