mithril_common/crypto_helper/cardano/
codec.rs1use anyhow::{anyhow, Context};
15use hex::FromHex;
16use kes_summed_ed25519::kes::Sum6Kes;
17use kes_summed_ed25519::traits::KesSk;
18use serde::de::DeserializeOwned;
19use serde::{Deserialize, Serialize};
20use serde_with::{As, Bytes};
21use std::fs;
22use std::io::Write;
23use std::path::Path;
24use thiserror::Error;
25
26use crate::StdError;
27
28#[derive(Clone, Serialize, Deserialize)]
35pub struct Sum6KesBytes(#[serde(with = "As::<Bytes>")] pub [u8; 612]);
36
37#[derive(Error, Debug)]
39#[error("Codec parse error")]
40pub struct CodecParseError(#[source] StdError);
41
42#[derive(Clone, Debug, Default, Serialize, Deserialize)]
44struct ShelleyFileFormat {
45 #[serde(rename = "type")]
46 file_type: String,
47 description: String,
48 #[serde(rename = "cborHex")]
49 cbor_hex: String,
50}
51
52pub trait SerDeShelleyFileFormat: Serialize + DeserializeOwned {
55 const TYPE: &'static str;
57
58 const DESCRIPTION: &'static str;
60
61 fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, CodecParseError> {
64 let data = fs::read_to_string(path)
65 .with_context(|| "SerDeShelleyFileFormat can not read data from file {}")
66 .map_err(|e| CodecParseError(anyhow!(e)))?;
67 let file: ShelleyFileFormat = serde_json::from_str(&data)
68 .with_context(|| "SerDeShelleyFileFormat can not unserialize json data")
69 .map_err(|e| CodecParseError(anyhow!(e)))?;
70 let hex_vector = Vec::from_hex(file.cbor_hex)
71 .with_context(|| "SerDeShelleyFileFormat can not unserialize hex data")
72 .map_err(|e| CodecParseError(anyhow!(e)))?;
73 let mut cursor = std::io::Cursor::new(&hex_vector);
74 let a: Self = ciborium::de::from_reader(&mut cursor)
75 .with_context(|| "SerDeShelleyFileFormat can not unserialize cbor data")
76 .map_err(|e| CodecParseError(anyhow!(e)))?;
77
78 Ok(a)
79 }
80
81 fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), CodecParseError> {
84 let mut cursor = std::io::Cursor::new(Vec::new());
85 ciborium::ser::into_writer(&self, &mut cursor)
86 .with_context(|| "SerDeShelleyFileFormat can not serialize data to cbor")
87 .map_err(|e| CodecParseError(anyhow!(e)))?;
88 let cbor_string = hex::encode(cursor.into_inner());
89
90 let file_format = ShelleyFileFormat {
91 file_type: Self::TYPE.to_string(),
92 description: Self::DESCRIPTION.to_string(),
93 cbor_hex: cbor_string,
94 };
95
96 let mut file = fs::File::create(path)
97 .with_context(|| "SerDeShelleyFileFormat can not create file")
98 .map_err(|e| CodecParseError(anyhow!(e)))?;
99 let json_str = serde_json::to_string(&file_format)
100 .with_context(|| "SerDeShelleyFileFormat can not serialize data to json")
101 .map_err(|e| CodecParseError(anyhow!(e)))?;
102
103 write!(file, "{json_str}")
104 .with_context(|| "SerDeShelleyFileFormat can not write data to file")
105 .map_err(|e| CodecParseError(anyhow!(e)))?;
106 Ok(())
107 }
108}
109
110impl SerDeShelleyFileFormat for Sum6KesBytes {
111 const TYPE: &'static str = "KesSigningKey_ed25519_kes_2^6";
112 const DESCRIPTION: &'static str = "KES Signing Key";
113
114 fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, CodecParseError> {
118 let data = fs::read_to_string(path)
119 .with_context(|| "Sum6KesBytes can not read data from file")
120 .map_err(|e| CodecParseError(anyhow!(e)))?;
121 let file: ShelleyFileFormat = serde_json::from_str(&data)
122 .with_context(|| "Sum6KesBytes can not unserialize json data")
123 .map_err(|e| CodecParseError(anyhow!(e)))?;
124 let mut hex_vector = Vec::from_hex(file.cbor_hex)
125 .with_context(|| "Sum6KesBytes can not unserialize hex data")
126 .map_err(|e| CodecParseError(anyhow!(e)))?;
127
128 if (hex_vector[2] & 4u8) == 0 {
130 hex_vector[2] |= 4u8;
132 hex_vector.extend_from_slice(&[0u8; 4]);
134 }
135
136 let mut cursor = std::io::Cursor::new(&hex_vector);
137 let a: Self = ciborium::from_reader(&mut cursor)
138 .with_context(|| "Sum6KesBytes can not unserialize cbor data")
139 .map_err(|e| CodecParseError(anyhow!(e)))?;
140 Ok(a)
141 }
142}
143
144impl<'a> TryFrom<&'a mut Sum6KesBytes> for Sum6Kes<'a> {
145 type Error = CodecParseError;
146
147 fn try_from(value: &'a mut Sum6KesBytes) -> Result<Self, Self::Error> {
148 Self::from_bytes(&mut value.0).map_err(|e| CodecParseError(anyhow!(format!("{e:?}"))))
149 }
150}
151
152#[cfg(test)]
153mod test {
154 use super::*;
155 use crate::test_utils::TempDir;
156
157 #[test]
158 fn compat_with_shelly_format() {
159 let temp_dir = TempDir::create("crypto_helper", "compat_with_shelly_format");
160 let sk_dir = temp_dir.join("dummy.skey");
161 let cbor_string = "590260fe77acdfa56281e4b05198f5136018057a65f425411f0990cac4aca0f2917aa00a3d51e191f6f425d870aca3c6a2a41833621f5729d7bc0e3dfc3ae77d057e5e1253b71def7a54157b9f98973ca3c49edd9f311e5f4b23ac268b56a6ac040c14c6d2217925492e42f00dc89a2a01ff363571df0ca0db5ba37001cee56790cc01cd69c6aa760fca55a65a110305ea3c11da0a27be345a589329a584ebfc499c43c55e8c6db5d9c0b014692533ee78abd7ac1e79f7ec9335c7551d31668369b4d5111db78072f010043e35e5ca7f11acc3c05b26b9c7fe56f02aa41544f00cb7685e87f34c73b617260ade3c7b8d8c4df46693694998f85ad80d2cbab0b575b6ccd65d90574e84368169578bff57f751bc94f7eec5c0d7055ec88891a69545eedbfbd3c5f1b1c1fe09c14099f6b052aa215efdc5cb6cdc84aa810db41dbe8cb7d28f7c4beb75cc53915d3ac75fc9d0bf1c734a46e401e15150c147d013a938b7e07cc4f25a582b914e94783d15896530409b8acbe31ef471de8a1988ac78dfb7510729eff008084885f07df870b65e4f382ca15908e1dcda77384b5c724350de90cec22b1dcbb1cdaed88da08bb4772a82266ec154f5887f89860d0920dba705c45957ef6d93e42f6c9509c966277d368dd0eefa67c8147aa15d40a222f7953a4f34616500b310d00aa1b5b73eb237dc4f76c0c16813d321b2fc5ac97039be25b22509d1201d61f4ccc11cd4ff40fffe39f0e937b4722074d8e073a775d7283b715d46f79ce128e3f1362f35615fa72364d20b6db841193d96e58d9d8e86b516bbd1f05e45b39823a93f6e9f29d9e01acf2c12c072d1c64e0afbbabf6903ef542e".to_string();
162
163 let file_format = ShelleyFileFormat {
164 file_type: Sum6KesBytes::TYPE.to_string(),
165 description: Sum6KesBytes::DESCRIPTION.to_string(),
166 cbor_hex: cbor_string,
167 };
168
169 let mut file =
170 fs::File::create(sk_dir.clone()).expect("Unexpected error with file creation.");
171 let json_str =
172 serde_json::to_string(&file_format).expect("Unexpected error with serialisation.");
173
174 write!(file, "{json_str}").expect("Unexpected error writing to file.");
175
176 let mut kes_sk_bytes =
177 Sum6KesBytes::from_file(&sk_dir).expect("Failure parsing Shelley file format.");
178
179 assert!(Sum6Kes::try_from(&mut kes_sk_bytes).is_ok());
180 }
181}