mithril_common/crypto_helper/cardano/
opcert.rs1use anyhow::anyhow;
4use blake2::{Blake2b, Digest, digest::consts::U28};
5use ed25519_dalek::{
6 Signature as EdSignature, Signer, SigningKey as EdSecretKey, Verifier,
7 VerifyingKey as EdVerificationKey,
8};
9use kes_summed_ed25519::PublicKey as KesPublicKey;
10use nom::AsBytes;
11use serde::de::Error;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13use sha2::Sha256;
14use thiserror::Error;
15
16use crate::StdResult;
17use crate::crypto_helper::cardano::ProtocolRegistrationErrorWrapper;
18use crate::crypto_helper::{ProtocolPartyId, encode_bech32};
19
20use super::SerDeShelleyFileFormat;
21
22#[derive(Error, Debug, PartialEq, Eq)]
24pub enum OpCertError {
25 #[error("pool address encoding error")]
27 PoolAddressEncoding,
28}
29
30#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
32struct RawOpCertWithoutColdVerificationKey(
33 #[serde(with = "serde_bytes")] Vec<u8>,
34 u64,
35 u64,
36 #[serde(with = "serde_bytes")] Vec<u8>,
37);
38
39#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
41struct RawOpCert(RawOpCertWithoutColdVerificationKey, EdVerificationKey);
42
43#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct OpCertWithoutColdVerificationKey {
46 pub(crate) kes_vk: KesPublicKey,
47 pub(crate) issue_number: u64,
48 pub start_kes_period: u64,
50 pub(crate) cert_sig: EdSignature,
51}
52
53impl OpCertWithoutColdVerificationKey {
54 pub fn try_new(
56 kes_vk: &[u8],
57 issue_number: u64,
58 start_kes_period: u64,
59 cert_sig: &[u8],
60 ) -> StdResult<Self> {
61 Ok(Self {
62 kes_vk: KesPublicKey::from_bytes(kes_vk)
63 .map_err(|_| anyhow!("KES vk serialisation error"))?,
64 issue_number,
65 start_kes_period,
66 cert_sig: EdSignature::from_slice(cert_sig)
67 .map_err(|_| anyhow!("ed25519 signature serialisation error"))?,
68 })
69 }
70
71 pub fn kes_vk(&self) -> KesPublicKey {
73 self.kes_vk
74 }
75
76 pub fn issue_number(&self) -> u64 {
78 self.issue_number
79 }
80
81 pub fn start_kes_period(&self) -> u64 {
83 self.start_kes_period
84 }
85
86 pub fn cert_sig(&self) -> EdSignature {
88 self.cert_sig
89 }
90}
91
92impl SerDeShelleyFileFormat for OpCertWithoutColdVerificationKey {
93 const TYPE: &'static str = "NodeOperationalCertificateWithoutColdVerificationKey";
94 const DESCRIPTION: &'static str = "";
95}
96
97impl Serialize for OpCertWithoutColdVerificationKey {
98 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99 where
100 S: Serializer,
101 {
102 let raw_cert = RawOpCertWithoutColdVerificationKey(
103 self.kes_vk.as_bytes().to_vec(),
104 self.issue_number,
105 self.start_kes_period,
106 self.cert_sig.to_bytes().to_vec(),
107 );
108
109 raw_cert.serialize(serializer)
110 }
111}
112
113impl<'de> Deserialize<'de> for OpCertWithoutColdVerificationKey {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: Deserializer<'de>,
117 {
118 let raw_cert = RawOpCertWithoutColdVerificationKey::deserialize(deserializer)?;
119
120 Ok(Self {
121 kes_vk: KesPublicKey::from_bytes(&raw_cert.0)
122 .map_err(|_| Error::custom("KES vk serialisation error"))?,
123 issue_number: raw_cert.1,
124 start_kes_period: raw_cert.2,
125 cert_sig: EdSignature::from_slice(&raw_cert.3)
126 .map_err(|_| Error::custom("ed25519 signature serialisation error"))?,
127 })
128 }
129}
130
131impl From<&OpCertWithoutColdVerificationKey> for RawOpCertWithoutColdVerificationKey {
132 fn from(opcert: &OpCertWithoutColdVerificationKey) -> Self {
133 RawOpCertWithoutColdVerificationKey(
134 opcert.kes_vk.as_bytes().to_vec(),
135 opcert.issue_number,
136 opcert.start_kes_period,
137 opcert.cert_sig.to_bytes().to_vec(),
138 )
139 }
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
144pub struct OpCert {
145 pub(crate) opcert_without_vk: OpCertWithoutColdVerificationKey,
146 pub(crate) cold_vk: EdVerificationKey,
147}
148
149impl SerDeShelleyFileFormat for OpCert {
150 const TYPE: &'static str = "NodeOperationalCertificate";
151 const DESCRIPTION: &'static str = "";
152}
153
154impl OpCert {
155 pub fn new(
157 kes_vk: KesPublicKey,
158 issue_number: u64,
159 start_kes_period: u64,
160 cold_secret_key: EdSecretKey,
161 ) -> Self {
162 let cold_vk: EdVerificationKey = cold_secret_key.verifying_key();
163 let cert_sig = cold_secret_key.sign(&Self::compute_message_to_sign(
164 &kes_vk,
165 issue_number,
166 start_kes_period,
167 ));
168
169 Self {
170 opcert_without_vk: OpCertWithoutColdVerificationKey {
171 kes_vk,
172 issue_number,
173 start_kes_period,
174 cert_sig,
175 },
176 cold_vk,
177 }
178 }
179
180 pub fn get_kes_verification_key(&self) -> KesPublicKey {
182 self.opcert_without_vk.kes_vk
183 }
184
185 pub fn get_issue_number(&self) -> u64 {
187 self.opcert_without_vk.issue_number
188 }
189
190 pub fn get_start_kes_period(&self) -> u64 {
192 self.opcert_without_vk.start_kes_period
193 }
194
195 pub fn get_certificate_signature(&self) -> EdSignature {
197 self.opcert_without_vk.cert_sig
198 }
199
200 pub fn get_opcert_without_cold_verification_key(&self) -> OpCertWithoutColdVerificationKey {
202 self.opcert_without_vk.clone()
203 }
204
205 pub fn get_cold_verification_key(&self) -> EdVerificationKey {
207 self.cold_vk
208 }
209
210 pub(crate) fn compute_message_to_sign(
212 kes_vk: &KesPublicKey,
213 issue_number: u64,
214 start_kes_period: u64,
215 ) -> [u8; 48] {
216 let mut msg = [0u8; 48];
217 msg[..32].copy_from_slice(kes_vk.as_bytes());
218 msg[32..40].copy_from_slice(&issue_number.to_be_bytes());
219 msg[40..48].copy_from_slice(&start_kes_period.to_be_bytes());
220 msg
221 }
222
223 pub fn validate(&self) -> Result<(), ProtocolRegistrationErrorWrapper> {
225 if self
226 .cold_vk
227 .verify(
228 &Self::compute_message_to_sign(
229 &self.opcert_without_vk.kes_vk,
230 self.opcert_without_vk.issue_number,
231 self.opcert_without_vk.start_kes_period,
232 ),
233 &self.opcert_without_vk.cert_sig,
234 )
235 .is_ok()
236 {
237 return Ok(());
238 }
239
240 Err(ProtocolRegistrationErrorWrapper::OpCertInvalid)
241 }
242
243 pub fn compute_protocol_party_id(&self) -> Result<ProtocolPartyId, OpCertError> {
245 let mut hasher = Blake2b::<U28>::new();
246 hasher.update(self.cold_vk.as_bytes());
247 let mut pool_id = [0u8; 28];
248 pool_id.copy_from_slice(hasher.finalize().as_bytes());
249 encode_bech32("pool", &pool_id).map_err(|_| OpCertError::PoolAddressEncoding)
250 }
251
252 pub fn compute_protocol_party_id_as_hash(&self) -> String {
254 let mut hasher = Blake2b::<U28>::new();
255 hasher.update(self.cold_vk.as_bytes());
256 hex::encode(hasher.finalize())
257 }
258
259 pub fn compute_hash(&self) -> String {
261 let mut hasher = Sha256::new();
262 hasher.update(self.opcert_without_vk.kes_vk.as_bytes());
263 hasher.update(self.opcert_without_vk.issue_number.to_be_bytes());
264 hasher.update(self.opcert_without_vk.start_kes_period.to_be_bytes());
265 hasher.update(self.opcert_without_vk.cert_sig.to_bytes());
266 hasher.update(self.cold_vk.as_bytes());
267 hex::encode(hasher.finalize())
268 }
269}
270
271impl Serialize for OpCert {
272 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
273 where
274 S: Serializer,
275 {
276 let raw_opcert_without_vk: RawOpCertWithoutColdVerificationKey =
277 (&self.opcert_without_vk).into();
278 let raw_cert = RawOpCert(raw_opcert_without_vk, self.cold_vk);
279
280 raw_cert.serialize(serializer)
281 }
282}
283
284impl<'de> Deserialize<'de> for OpCert {
285 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
286 where
287 D: Deserializer<'de>,
288 {
289 let raw_cert = RawOpCert::deserialize(deserializer)?;
290 let raw_opcert_without_vk = &raw_cert.0;
291
292 Ok(Self {
293 opcert_without_vk: OpCertWithoutColdVerificationKey {
294 kes_vk: KesPublicKey::from_bytes(&raw_opcert_without_vk.0)
295 .map_err(|_| Error::custom("KES vk serialisation error"))?,
296 issue_number: raw_opcert_without_vk.1,
297 start_kes_period: raw_opcert_without_vk.2,
298 cert_sig: EdSignature::from_slice(&raw_opcert_without_vk.3)
299 .map_err(|_| Error::custom("ed25519 signature serialisation error"))?,
300 },
301 cold_vk: raw_cert.1,
302 })
303 }
304}
305
306impl From<(OpCertWithoutColdVerificationKey, EdVerificationKey)> for OpCert {
307 fn from(
308 (opcert_without_vk, cold_vk): (OpCertWithoutColdVerificationKey, EdVerificationKey),
309 ) -> Self {
310 Self {
311 opcert_without_vk,
312 cold_vk,
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use crate::crypto_helper::cardano::ColdKeyGenerator;
321 use crate::test::TempDir;
322
323 use kes_summed_ed25519::{kes::Sum6Kes, traits::KesSk};
324 use std::path::PathBuf;
325
326 fn setup_temp_directory(test_name: &str) -> PathBuf {
327 TempDir::create("mithril_cardano_opcert", test_name)
328 }
329
330 #[test]
331 fn test_vector_opcert() {
332 let temp_dir = setup_temp_directory("test_vector_opcert");
333 let keypair = ColdKeyGenerator::create_deterministic_keypair([0u8; 32]);
334 let mut dummy_key_buffer = [0u8; Sum6Kes::SIZE + 4];
335 let mut dummy_seed = [0u8; 32];
336 let (_, kes_verification_key) = Sum6Kes::keygen(&mut dummy_key_buffer, &mut dummy_seed);
337 let operational_certificate = OpCert::new(kes_verification_key, 0, 0, keypair);
338 assert!(operational_certificate.validate().is_ok());
339
340 let operation_certificate_file = temp_dir.join("node.cert");
341 operational_certificate
342 .to_file(&operation_certificate_file)
343 .expect("operational certificate file export should not fail");
344
345 let operational_certificate: OpCert = OpCert::from_file(&operation_certificate_file)
346 .expect("operational certificate file import should not fail");
347 assert!(operational_certificate.validate().is_ok());
348
349 let party_id = operational_certificate
350 .compute_protocol_party_id()
351 .expect("compute protocol party_id should not fail");
352 assert_eq!(
353 "pool1mxyec46067n3querj9cxkk0g0zlag93pf3ya9vuyr3wgkq2e6t7".to_string(),
354 party_id
355 );
356
357 let party_id_as_hash = operational_certificate.compute_protocol_party_id_as_hash();
358 assert_eq!(
359 "d9899c574fd7a710732391706b59e878bfd416214c49d2b3841c5c8b".to_string(),
360 party_id_as_hash
361 );
362
363 let operational_certificate_bytes_without_cold_vk = operational_certificate
364 .get_opcert_without_cold_verification_key()
365 .to_cbor_bytes()
366 .expect("compute CBOR bytes should not fail");
367 assert_eq!(
368 "845820e650d7531509bb6cffd7998c28c68e4ec8fa621a0952206ea11eb03fcd7dcb2900005840d4abce27da05ff03c1342cc6ab53135072e1babf9cc05492f59f1ff009f70457aaa862c7158b13be0cfb41d7a91a562589bc110eb2cdaf5d2756048abbea5f05",
369 hex::encode(operational_certificate_bytes_without_cold_vk)
370 );
371 }
372}