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