Skip to main content

csv_rs/certs/csv/cert/
mod.rs

1// Copyright (C) Hygon Info Technologies Ltd.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Operations that can be done on a CSV certificate.
6
7pub mod key;
8
9use crate::{
10    certs::{ca, Algorithm, Signer, Usage, Verifiable},
11    crypto::{self, key::ecc, sig::ecdsa, sm, PrivateKey, PublicKey, Signature},
12    util::*,
13};
14use serde::{Deserialize, Serialize};
15use serde_big_array::BigArray;
16use std::io::{Error, ErrorKind, Read, Result, Write};
17
18#[repr(C)]
19#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
20pub struct Data {
21    pub firmware: crate::Version,
22    pub reserved1: u16,
23    pub pubkey: key::PubKey,
24    pub uid_size: u16,
25    #[serde(with = "BigArray")]
26    pub user_id: [u8; 254],
27    pub sid: [u8; 16],
28    #[serde(with = "BigArray")]
29    pub reserved2: [u8; 608],
30}
31
32#[repr(C)]
33#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
34pub struct Body {
35    pub ver: u32,
36    pub data: Data,
37}
38
39#[repr(C)]
40#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
41pub struct Signatures {
42    usage: Usage,
43    algo: Algorithm,
44    signature: ecdsa::Signature,
45    #[serde(with = "BigArray")]
46    _reserved: [u8; 368],
47}
48
49impl Default for Signatures {
50    fn default() -> Self {
51        let _reserved = [0u8; 368];
52        Signatures {
53            usage: Usage::INV,
54            algo: Algorithm::NONE,
55            signature: ecdsa::Signature::default(),
56            _reserved,
57        }
58    }
59}
60
61impl TryFrom<&crypto::Signature> for Signatures {
62    type Error = Error;
63
64    #[inline]
65    fn try_from(value: &crypto::Signature) -> Result<Self> {
66        let algo = value.algo.unwrap_or(Algorithm::NONE);
67        Ok(Signatures {
68            usage: value.usage,
69            algo,
70            signature: ecdsa::Signature::try_from(&value.sig[..])?,
71            _reserved: [0u8; 368],
72        })
73    }
74}
75
76#[repr(C)]
77#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
78pub struct Certificate {
79    pub body: Body,
80    pub sigs: [Signatures; 2],
81}
82
83impl Certificate {
84    /// Writes the certificate content to a file.
85    pub fn write_to_file(&self, path: &std::path::Path) -> Result<()> {
86        let mut file = std::fs::File::create(path)?;
87        let encoded = bincode::serialize(self).map_err(|e| Error::new(ErrorKind::Other, e))?;
88        file.write_all(&encoded)?;
89        Ok(())
90    }
91}
92
93impl TryFrom<&Signatures> for Option<Signature> {
94    type Error = Error;
95
96    #[inline]
97    fn try_from(value: &Signatures) -> Result<Self> {
98        if value.is_empty() {
99            return Ok(None);
100        }
101
102        let usage = value.usage;
103        let algo = value.algo;
104        let sig = Vec::try_from(&value.signature)?;
105        Ok(Some(Signature {
106            sig,
107            usage,
108            algo: Some(algo),
109            id: None,
110        }))
111    }
112}
113
114impl TryFrom<&Certificate> for [Option<Signature>; 2] {
115    type Error = Error;
116
117    #[inline]
118    fn try_from(value: &Certificate) -> Result<Self> {
119        Ok([(&value.sigs[0]).try_into()?, (&value.sigs[1]).try_into()?])
120    }
121}
122
123impl TryFrom<&Certificate> for PublicKey {
124    type Error = Error;
125
126    fn try_from(value: &Certificate) -> Result<Self> {
127        let key = value.body.data.pubkey.key;
128        Ok(Self {
129            id: None,
130            key,
131            usage: value.body.data.pubkey.usage,
132            algo: Some(value.body.data.pubkey.algo),
133        })
134    }
135}
136
137impl Signer<Certificate> for PrivateKey<Usage> {
138    type Output = ();
139
140    fn sign(&self, target: &mut Certificate, uid: String) -> Result<()> {
141        let slot = if target.sigs[0].is_empty() {
142            &mut target.sigs[0]
143        } else if target.sigs[1].is_empty() {
144            &mut target.sigs[1]
145        } else {
146            return Err(ErrorKind::InvalidInput.into());
147        };
148
149        let mut msg: Vec<u8> = Vec::new();
150        msg.save(&target.body)?;
151
152        let sig = sm::SM2::sign(self.key, uid.as_bytes(), &msg)?;
153
154        let sig = crate::crypto::Signature {
155            usage: self.usage,
156            sig,
157            algo: Some(self.usage.try_into()?),
158            id: self.id,
159        };
160
161        *slot = Signatures::try_from(&sig)?;
162
163        Ok(())
164    }
165}
166
167impl Verifiable for (&Certificate, &Certificate) {
168    type Output = ();
169
170    fn verify(self) -> Result<()> {
171        let key: PublicKey = self.0.try_into()?;
172
173        let sigs: [Option<Signature>; 2] = self.1.try_into()?;
174        for sig in sigs.iter().flatten() {
175            if key
176                .verify(
177                    self.1,
178                    &self.0.body.data.user_id[..self.0.body.data.uid_size as usize],
179                    sig,
180                )
181                .is_ok()
182            {
183                return Ok(());
184            }
185        }
186
187        Err(ErrorKind::InvalidInput.into())
188    }
189}
190
191impl Verifiable for (&ca::cert::Certificate, &Certificate) {
192    type Output = ();
193
194    fn verify(self) -> Result<()> {
195        let key: PublicKey = self.0.try_into()?;
196        let sigs: [Option<Signature>; 2] = self.1.try_into()?;
197        for sig in sigs.iter().flatten() {
198            if key
199                .verify(
200                    self.1,
201                    &self.0.body.user_id[..self.0.body.uid_size as usize],
202                    sig,
203                )
204                .is_ok()
205            {
206                return Ok(());
207            }
208        }
209        Err(ErrorKind::InvalidInput.into())
210    }
211}
212
213impl codicon::Decoder<()> for Signatures {
214    type Error = Error;
215
216    #[inline]
217    fn decode(mut reader: impl Read, _: ()) -> Result<Self> {
218        let mut _reserved = [0u8; 368];
219        let usage: Usage = reader.load()?;
220        let algo: Algorithm = reader.load()?;
221        let signature: ecdsa::Signature = reader.load()?;
222        reader.read_exact(&mut _reserved)?;
223        Ok(Self {
224            usage,
225            algo,
226            signature,
227            _reserved,
228        })
229    }
230}
231
232impl codicon::Decoder<()> for Certificate {
233    type Error = Error;
234
235    fn decode(mut reader: impl Read, _: ()) -> Result<Self> {
236        let body: Body = reader.load()?;
237        let sig1 = Signatures::decode(&mut reader, ())?;
238        let sig2 = Signatures::decode(&mut reader, ())?;
239        Ok(Self {
240            body,
241            sigs: [sig1, sig2],
242        })
243    }
244}
245
246impl codicon::Encoder<crate::Body> for Certificate {
247    type Error = Error;
248
249    fn encode(&self, mut writer: impl Write, _: crate::Body) -> Result<()> {
250        writer.save(&self.body)
251    }
252}
253
254impl TryFrom<&Certificate> for Usage {
255    type Error = Error;
256
257    fn try_from(value: &Certificate) -> Result<Self> {
258        Ok(value.body.data.pubkey.usage)
259    }
260}
261
262impl Signatures {
263    pub fn is_empty(&self) -> bool {
264        match self.usage {
265            Usage::CEK | Usage::HRK | Usage::HSK | Usage::OCA | Usage::PDH | Usage::PEK => {
266                !matches!(self.algo, Algorithm::SM2_SA | Algorithm::SM2_DH)
267            }
268            _ => true,
269        }
270    }
271}
272
273impl Body {
274    pub fn generate(usage: Usage, uid: Option<String>) -> Result<(Body, PrivateKey<Usage>)> {
275        let uid: String = if let Some(value) = uid {
276            value
277        } else {
278            String::try_from(usage)?
279        };
280
281        let Ok(uid_size) = uid.len().try_into() else {
282            return Err(ErrorKind::InvalidInput.into());
283        };
284
285        let mut user_id_vec = Vec::from(uid.as_bytes());
286        user_id_vec.resize(254, 0);
287        let mut user_id: [u8; 254] = [0; 254];
288        user_id.copy_from_slice(&user_id_vec);
289        let (pubkey, prv) = key::PubKey::generate(usage, None)?;
290        Ok((
291            Body {
292                ver: 1u32.to_le(),
293                data: Data {
294                    firmware: Default::default(),
295                    reserved1: 0,
296                    pubkey,
297                    uid_size,
298                    user_id,
299                    sid: [0u8; 16],
300                    reserved2: [0u8; 608],
301                },
302            },
303            prv,
304        ))
305    }
306}
307
308impl Certificate {
309    /// Generates a private key and its public certificate.
310    pub fn generate(usage: Usage, uid: Option<String>) -> Result<(Self, PrivateKey<Usage>)> {
311        let (body, prv) = Body::generate(usage, uid)?;
312        Ok((
313            Self {
314                body,
315                sigs: [Signatures::default(), Signatures::default()],
316            },
317            prv,
318        ))
319    }
320
321    /// Encrypts a buffer with the certificate's SM2 public key.
322    pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
323        let key: PublicKey = self.try_into()?;
324        key.encrypt(data)
325    }
326}
327
328/// Downloads the HSK CEK certificate from the hygon certificate server.
329#[cfg(feature = "network")]
330pub async fn download_hskcek(
331    sn: &[u8],
332) -> std::result::Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
333    // Convert serial number bytes to string and trim null terminator
334    let chip_id = std::str::from_utf8(sn)?.trim_end_matches('\0');
335    let kds_url = format!("https://cert.hygon.cn/hsk_cek?snumber={chip_id}");
336    log::trace!("kds_url: {}", kds_url);
337    // Create async HTTP client (recommend reusing client in production)
338    let response = reqwest::Client::new()
339        .get(&kds_url)
340        .header("User-Agent", "Reqwest")
341        .send()
342        .await?; // Async await for request completion
343
344    // Async read response body
345    let response_body = response.bytes().await?.to_vec();
346    Ok(response_body)
347}
348
349/// Retrieves certificate data either from local storage or via network download.
350#[cfg(feature = "network")]
351pub async fn get_certificate_data(
352    chip_id: &[u8; 16],
353) -> std::result::Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
354    // 1. Get certificate directory path (from env var or use default)
355    let cert_dir =
356        std::env::var("HSK_CEK_CERT_PATH").unwrap_or_else(|_| "/opt/dcu/certs".to_string());
357
358    // 2.Convert chip_id to string
359    let chip_id_str =
360        std::str::from_utf8(chip_id).map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
361
362    // 3. Build full certificate path
363    let cert_path = format!("{}/{}_hsk_cek.cert", cert_dir, chip_id_str);
364
365    // 4. Check file existence and read or download
366    if tokio::fs::metadata(&cert_path).await.is_ok() {
367        log::debug!("Reading certificate from: {}", cert_path);
368        tokio::fs::read(cert_path).await.map_err(Into::into)
369    } else {
370        log::debug!(
371            "Certificate not found at {}, attempting download",
372            cert_path
373        );
374        download_hskcek(chip_id).await
375    }
376}