mithril_common/crypto_helper/cardano/kes/
signer_with_key.rs

1use std::path::PathBuf;
2
3use anyhow::{anyhow, Context};
4use kes_summed_ed25519::{
5    kes::{Sum6Kes, Sum6KesSig},
6    traits::KesSk,
7};
8
9use crate::{
10    crypto_helper::{
11        cardano::{KesSignError, KesSigner},
12        KesPeriod, OpCert, SerDeShelleyFileFormat, Sum6KesBytes,
13    },
14    StdResult,
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(&self, message: &[u8], kes_period: KesPeriod) -> StdResult<(Sum6KesSig, OpCert)> {
35        let mut kes_sk_bytes = Sum6KesBytes::from_file(&self.kes_sk_path)
36            .map_err(|e| anyhow!(e))
37            .with_context(|| "StandardKesSigner can not read KES secret key from file")?;
38        let mut kes_sk = Sum6Kes::try_from(&mut kes_sk_bytes)
39            .map_err(|e| anyhow!(e))
40            .with_context(|| "StandardKesSigner can not use KES secret key")?;
41        let kes_sk_period = kes_sk.get_period();
42        if kes_sk_period > kes_period {
43            return Err(anyhow!(KesSignError::PeriodMismatch(
44                kes_sk_period,
45                kes_period
46            )));
47        }
48
49        // We need to perform the evolutions
50        for period in kes_sk_period..kes_period {
51            kes_sk
52                .update()
53                .map_err(|_| KesSignError::UpdateKey(period))?;
54        }
55
56        let operational_certificate = OpCert::from_file(&self.operational_certificate_path)
57            .map_err(|e| anyhow!(e))
58            .with_context(|| "StandardKesSigner can not read operational certificate from file")?;
59
60        Ok((kes_sk.sign(message), operational_certificate))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    use crate::crypto_helper::cardano::{
69        kes::tests_setup::create_kes_cryptographic_material,
70        tests_setup::KesCryptographicMaterialForTest, KesVerifier, KesVerifierStandard,
71    };
72
73    type PartyIndex = u64;
74
75    #[test]
76    fn create_valid_signature_for_message() {
77        let KesCryptographicMaterialForTest {
78            party_id: _,
79            operational_certificate_file,
80            kes_secret_key_file,
81        } = create_kes_cryptographic_material(
82            1 as PartyIndex,
83            0 as KesPeriod,
84            "create_valid_signature_for_message",
85        );
86        let message = b"Test message for KES signing";
87        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
88        let kes_signing_period = 1;
89
90        let (signature, op_cert) = kes_signer
91            .sign(message, kes_signing_period)
92            .expect("Signing should not fail");
93
94        KesVerifierStandard
95            .verify(message, &signature, &op_cert, kes_signing_period)
96            .expect("Signature verification should not fail");
97    }
98
99    #[test]
100    fn create_invalid_signature_for_different_message() {
101        let KesCryptographicMaterialForTest {
102            party_id: _,
103            operational_certificate_file,
104            kes_secret_key_file,
105        } = create_kes_cryptographic_material(
106            1 as PartyIndex,
107            0 as KesPeriod,
108            "create_invalid_signature_for_different_message",
109        );
110        let message = b"Test message for KES signing";
111        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
112        let kes_signing_period = 1;
113
114        let (signature, op_cert) = kes_signer
115            .sign(message, kes_signing_period)
116            .expect("Signing should not fail");
117
118        KesVerifierStandard
119            .verify(
120                b"Different message",
121                &signature,
122                &op_cert,
123                kes_signing_period,
124            )
125            .expect_err("Signature verification should fail");
126    }
127
128    #[test]
129    fn create_invalid_signature_for_invalid_kes_period() {
130        let kes_period_start = 5 as KesPeriod;
131        let KesCryptographicMaterialForTest {
132            party_id: _,
133            operational_certificate_file,
134            kes_secret_key_file,
135        } = create_kes_cryptographic_material(
136            1 as PartyIndex,
137            kes_period_start,
138            "create_invalid_signature_for_invalid_kes_period",
139        );
140        let message = b"Test message for KES signing";
141        let kes_signer = KesSignerStandard::new(kes_secret_key_file, operational_certificate_file);
142        let kes_signing_period = 2;
143        assert!(
144            kes_signing_period < kes_period_start,
145            "KES signing period should be less than the KES period of the key"
146        );
147
148        kes_signer
149            .sign(message, kes_signing_period)
150            .expect_err("Signing should fail");
151    }
152}