Skip to main content

csv_rs/api/launch/
mod.rs

1// Copyright (C) Hygon Info Technologies Ltd.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Modules for interfacing with CSV Firmware
6//! Rust-fridenly API wrappers to communicate the the FFI functions.
7
8pub(crate) mod ioctl;
9use ioctl::*;
10
11pub(crate) mod types;
12use types::*;
13
14use crate::{
15    certs::{csv::Certificate, Signer, Usage, Verifiable},
16    crypto::{sig::ecdsa, sm, PrivateKey, PublicKey, Signature},
17    util::*,
18    Version,
19};
20
21use bitflags::bitflags;
22use serde::{Deserialize, Serialize};
23use serde_big_array::BigArray;
24use std::{
25    convert::*,
26    io::{Read, Result, Write},
27    mem::MaybeUninit,
28    os::unix::io::AsRawFd,
29};
30
31/// Launcher type-state that indicates a brand new launch.
32pub struct New;
33
34/// Launcher type-state that indicates an in-progress launch.
35pub struct Started(Handle);
36
37/// Launcher type-state that indicates the availability of a measurement.
38pub struct Measured(Handle, Measurement);
39
40/// Facilitates the correct execution of the CSV launch process.
41pub struct Launcher<T, U: AsRawFd, V: AsRawFd> {
42    state: T,
43    vm_fd: U,
44    csv: V,
45}
46
47impl<T, U: AsRawFd, V: AsRawFd> Launcher<T, U, V> {
48    /// Give access to the vm fd to create vCPUs or such.
49    pub fn as_mut_vmfd(&mut self) -> &mut U {
50        &mut self.vm_fd
51    }
52}
53
54impl<U: AsRawFd, V: AsRawFd> Launcher<New, U, V> {
55    /// Begin the CSV launch process.
56    pub fn new(kvm: U, csv: V) -> Result<Self> {
57        let mut launcher = Launcher {
58            vm_fd: kvm,
59            csv,
60            state: New,
61        };
62
63        let mut cmd = Command::from(&mut launcher.csv, &Init);
64        INIT.ioctl(&mut launcher.vm_fd, &mut cmd)
65            .map_err(|e| cmd.encapsulate(e))?;
66
67        Ok(launcher)
68    }
69
70    /// Begin the CSV2 launch process.
71    pub fn new_es(kvm: U, csv: V) -> Result<Self> {
72        let mut launcher = Launcher {
73            vm_fd: kvm,
74            csv,
75            state: New,
76        };
77
78        let mut cmd = Command::from(&mut launcher.csv, &EsInit);
79        ES_INIT
80            .ioctl(&mut launcher.vm_fd, &mut cmd)
81            .map_err(|e| cmd.encapsulate(e))?;
82
83        Ok(launcher)
84    }
85
86    /// Create an encrypted guest context.
87    pub fn start(mut self, start: Start) -> Result<Launcher<Started, U, V>> {
88        let mut launch_start = LaunchStart::new(&start.policy, &start.cert, &start.session);
89        let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
90        LAUNCH_START
91            .ioctl(&mut self.vm_fd, &mut cmd)
92            .map_err(|e| cmd.encapsulate(e))?;
93
94        let next = Launcher {
95            state: Started(launch_start.into()),
96            vm_fd: self.vm_fd,
97            csv: self.csv,
98        };
99
100        Ok(next)
101    }
102
103    /// Create an encrypted guest context.
104    pub fn start_raw(
105        mut self,
106        policy: &Policy,
107        cert: &Certificate,
108        session: &Session,
109    ) -> Result<Launcher<Started, U, V>> {
110        let mut launch_start = LaunchStart::new(policy, cert, session);
111        let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
112        LAUNCH_START
113            .ioctl(&mut self.vm_fd, &mut cmd)
114            .map_err(|e| cmd.encapsulate(e))?;
115
116        let next = Launcher {
117            state: Started(launch_start.into()),
118            vm_fd: self.vm_fd,
119            csv: self.csv,
120        };
121
122        Ok(next)
123    }
124
125    /// Create an encrypted guest context.
126    pub fn start_with_policy_only(mut self, policy: Policy) -> Result<Launcher<Started, U, V>> {
127        let mut launch_start = LaunchStart::with_policy_only(&policy);
128        let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
129        LAUNCH_START
130            .ioctl(&mut self.vm_fd, &mut cmd)
131            .map_err(|e| cmd.encapsulate(e))?;
132
133        let next = Launcher {
134            state: Started(launch_start.into()),
135            vm_fd: self.vm_fd,
136            csv: self.csv,
137        };
138
139        Ok(next)
140    }
141}
142
143impl<U: AsRawFd, V: AsRawFd> Launcher<Started, U, V> {
144    /// Encrypt guest data with its VEK.
145    pub fn update_data(&mut self, data: &[u8]) -> Result<()> {
146        let launch_update_data = LaunchUpdateData::new(data);
147        let mut cmd = Command::from(&mut self.csv, &launch_update_data);
148
149        KvmEncRegion::new(data).register(&mut self.vm_fd)?;
150
151        LAUNCH_UPDATE_DATA
152            .ioctl(&mut self.vm_fd, &mut cmd)
153            .map_err(|e| cmd.encapsulate(e))?;
154
155        Ok(())
156    }
157
158    /// Register the encrypted memory region to a virtual machine.
159    /// Corresponds to the `KVM_MEMORY_ENCRYPT_REG_REGION` ioctl.
160    pub fn register_kvm_enc_region(&mut self, data: &[u8]) -> Result<()> {
161        KvmEncRegion::new(data).register(&mut self.vm_fd)?;
162        Ok(())
163    }
164
165    /// Encrypt guest data with its VEK, while the KVM encrypted memory region is not registered.
166    pub fn update_data_without_registration(&mut self, data: &[u8]) -> Result<()> {
167        let launch_update_data = LaunchUpdateData::new(data);
168        let mut cmd = Command::from(&mut self.csv, &launch_update_data);
169
170        LAUNCH_UPDATE_DATA
171            .ioctl(&mut self.vm_fd, &mut cmd)
172            .map_err(|e| cmd.encapsulate(e))?;
173
174        Ok(())
175    }
176
177    /// Encrypt the VMSA on CSV2.
178    pub fn update_vmsa(&mut self) -> Result<()> {
179        let launch_update_vmsa = LaunchUpdateVmsa::new();
180        let mut cmd = Command::from(&mut self.csv, &launch_update_vmsa);
181
182        LAUNCH_UPDATE_VMSA
183            .ioctl(&mut self.vm_fd, &mut cmd)
184            .map_err(|e| cmd.encapsulate(e))?;
185
186        Ok(())
187    }
188
189    /// Request a measurement from the CSV firmware.
190    pub fn measure(mut self) -> Result<Launcher<Measured, U, V>> {
191        let mut measurement = MaybeUninit::uninit();
192        let mut launch_measure = LaunchMeasure::new(&mut measurement);
193        let mut cmd = Command::from_mut(&mut self.csv, &mut launch_measure);
194        LAUNCH_MEASUREMENT
195            .ioctl(&mut self.vm_fd, &mut cmd)
196            .map_err(|e| cmd.encapsulate(e))?;
197
198        let next = Launcher {
199            state: Measured(self.state.0, unsafe { measurement.assume_init() }),
200            vm_fd: self.vm_fd,
201            csv: self.csv,
202        };
203
204        Ok(next)
205    }
206}
207
208impl<U: AsRawFd, V: AsRawFd> Launcher<Measured, U, V> {
209    /// Get the measurement that the CSV platform recorded.
210    pub fn measurement(&self) -> Measurement {
211        self.state.1
212    }
213
214    /// Get the attestation report.
215    pub fn get_attestation_report(&mut self, mnonce: [u8; 16]) -> Result<Box<AttestationReport>> {
216        let mut ar = MaybeUninit::uninit();
217        let mut attestation = Attestation::new(&mut ar, mnonce);
218        let mut cmd = Command::from_mut(&mut self.csv, &mut attestation);
219        ATTESTATION
220            .ioctl(&mut self.vm_fd, &mut cmd)
221            .map_err(|e| cmd.encapsulate(e))?;
222
223        Ok(Box::new(unsafe { ar.assume_init() }))
224    }
225
226    /// Inject a secret into the guest.
227    ///
228    /// ## Remarks
229    ///
230    /// This should only be called after a successful attestation flow.
231    pub fn inject(&mut self, secret: &Secret, guest: usize) -> Result<()> {
232        let launch_secret = LaunchSecret::new(&secret.header, guest, &secret.ciphertext[..]);
233        let mut cmd = Command::from(&mut self.csv, &launch_secret);
234        LAUNCH_SECRET
235            .ioctl(&mut self.vm_fd, &mut cmd)
236            .map_err(|e| cmd.encapsulate(e))?;
237        Ok(())
238    }
239
240    /// Complete the CSV launch process.
241    pub fn finish(mut self) -> Result<Handle> {
242        let mut cmd = Command::from(&mut self.csv, &LaunchFinish);
243        LAUNCH_FINISH
244            .ioctl(&mut self.vm_fd, &mut cmd)
245            .map_err(|e| cmd.encapsulate(e))?;
246        Ok(self.state.0)
247    }
248}
249
250bitflags! {
251    /// Configurable CSV Policy options.
252    #[derive(Default, Deserialize, Serialize)]
253    pub struct PolicyFlags: u16 {
254        /// When set, debugging the guest is forbidden.
255        const NO_DEBUG        = 0b00000001u16.to_le();
256
257        /// When set, sharing keys with other guests is prohibited.
258        const NO_KEY_SHARING  = 0b00000010u16.to_le();
259
260        /// When set, CSV2 protections are required.
261        const ENCRYPTED_STATE = 0b00000100u16.to_le();
262
263        /// When set, the guest may not be sent to another platform.
264        const NO_SEND         = 0b00001000u16.to_le();
265
266        /// When set, the guest may not be transmitted to a platform
267        /// that is outside of the domain.
268        const DOMAIN          = 0b00010000u16.to_le();
269
270        /// When set, the guest may not be transmitted to another
271        /// platform that is not CSV-capable.
272        const CSV             = 0b00100000u16.to_le();
273    }
274}
275
276/// Describes a policy that the HYGON Secure Processor will
277/// enforce.
278#[repr(C)]
279#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
280pub struct Policy {
281    /// The various policy optons are encoded as bit flags.
282    pub flags: PolicyFlags,
283
284    /// The desired minimum platform firmware version.
285    pub minfw: Version,
286}
287
288/// Convert a policy represented as a u32 to a Policy struct.
289impl From<u32> for Policy {
290    fn from(p: u32) -> Self {
291        let flags = p as u16;
292        let flags = PolicyFlags::from_bits_truncate(flags);
293
294        let p = p >> 16;
295        let p = p as u16;
296        let minfw = Version::from(p);
297
298        Self { flags, minfw }
299    }
300}
301
302/// the SessionBody of the session
303#[repr(C)]
304#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
305pub struct SessionBody {
306    /// Used for deriving a shared secret between the tenant
307    /// and the HYGON SP.
308    pub nonce: [u8; 16],
309
310    /// The TEK and TIK concatenated together and wrapped by
311    /// the Key Encryption Key and the Key Integrity Key.
312    /// (KIK (KEK (TEK|TIK))).
313    pub wrap_tk: [u8; 32],
314
315    /// The initialization vector.
316    pub wrap_iv: [u8; 16],
317
318    /// Integrity protection for the wrapped keys (see the
319    /// `wrap_tk` field of this struct).
320    pub wrap_mac: [u8; 32],
321
322    /// The integrity-protected CSV session data.
323    pub session_mac: [u8; 32],
324
325    // following 2 attr are not used, keep it for backward compatibility
326    pub key_id: [u8; 16],
327
328    /// used in random public key convert from command data buffer
329    #[serde(with = "BigArray")]
330    pub rnd_pub_key_data: [u8; 148],
331
332    /// ms_enc
333    #[serde(with = "BigArray")]
334    pub ms_enc: [u8; 256],
335
336    /// vm_digest
337    pub vm_digest: [u8; 32],
338
339    /// pubkey_digest
340    pub pubkey_digest: [u8; 32],
341
342    /// vm_id
343    pub vm_id: [u8; 16],
344
345    /// vm_version
346    pub vm_version: [u8; 16],
347
348    /// user_data
349    #[serde(with = "BigArray")]
350    pub user_data: [u8; 64],
351}
352
353#[repr(C)]
354#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
355pub struct SessionSig {
356    /// sig R
357    pub r: [u8; 32],
358    /// sig S
359    pub s: [u8; 32],
360}
361
362impl From<ecdsa::Signature> for SessionSig {
363    #[inline]
364    fn from(value: ecdsa::Signature) -> Self {
365        let mut r = [0u8; 32];
366        let mut s = [0u8; 32];
367        for (i, b) in value.r.iter().take(32).cloned().enumerate() {
368            r[i] = b;
369        }
370        for (i, b) in value.s.iter().take(32).cloned().enumerate() {
371            s[i] = b;
372        }
373        SessionSig { r, s }
374    }
375}
376
377impl From<SessionSig> for ecdsa::Signature {
378    #[inline]
379    fn from(value: SessionSig) -> Self {
380        let mut r = [0u8; 72];
381        let mut s = [0u8; 72];
382        for (i, b) in value.r.iter().cloned().enumerate() {
383            r[i] = b;
384        }
385        for (i, b) in value.s.iter().cloned().enumerate() {
386            s[i] = b;
387        }
388        ecdsa::Signature { r, s }
389    }
390}
391
392/// A secure channel between the tenant and the HYGON Secure
393/// Processor.
394#[repr(C)]
395#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
396pub struct Session {
397    /// Used for deriving a shared secret between the tenant
398    /// and the HYGON SP.
399    pub body: SessionBody,
400    pub sig: SessionSig,
401}
402
403impl Signer<Session> for PrivateKey<Usage> {
404    type Output = ();
405
406    fn sign(&self, target: &mut Session, uid: String) -> Result<()> {
407        let slot = &mut target.sig;
408
409        let mut msg: Vec<u8> = Vec::new();
410        msg.save(&target.body)?;
411
412        let sig = sm::SM2::sign(self.key, uid.as_bytes(), &msg)?;
413
414        let ecdsa_sig = ecdsa::Signature::try_from(&sig[..])?;
415
416        *slot = SessionSig::from(ecdsa_sig);
417        Ok(())
418    }
419}
420
421impl TryFrom<&Session> for Signature {
422    type Error = std::io::Error;
423
424    #[inline]
425    fn try_from(value: &Session) -> Result<Self> {
426        let sig = ecdsa::Signature::from(value.sig);
427
428        let sig = Vec::try_from(&sig)?;
429        Ok(Self {
430            sig,
431            id: None,
432            usage: Usage::PDH,
433            algo: None,
434        })
435    }
436}
437
438impl codicon::Encoder<crate::Body> for Session {
439    type Error = std::io::Error;
440
441    fn encode(&self, mut writer: impl Write, _: crate::Body) -> Result<()> {
442        writer.save(&self.body)
443    }
444}
445
446impl Verifiable for (&Certificate, &Session) {
447    type Output = ();
448
449    fn verify(self) -> Result<()> {
450        let key: PublicKey = self.0.try_into()?;
451        let sig: Signature = self.1.try_into()?;
452        key.verify(
453            self.1,
454            &self.0.body.data.user_id[..self.0.body.data.uid_size as usize],
455            &sig,
456        )
457    }
458}
459
460/// Used to establish a secure session with the HYGON SP.
461#[repr(C)]
462#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
463pub struct Start {
464    /// The tenant's policy for this CSV guest.
465    pub policy: Policy,
466
467    /// The tenant's Diffie-Hellman certificate.
468    pub cert: Certificate,
469
470    /// A secure channel with the HYGON SP.
471    pub session: Session,
472}
473
474impl codicon::Decoder<()> for Start {
475    type Error = std::io::Error;
476
477    fn decode(mut reader: impl Read, _: ()) -> std::io::Result<Self> {
478        reader.load()
479    }
480}
481
482impl codicon::Encoder<()> for Start {
483    type Error = std::io::Error;
484
485    fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> {
486        writer.save(self)
487    }
488}
489
490bitflags! {
491    /// Additional descriptions of the secret header packet.
492    #[derive(Default, Deserialize, Serialize)]
493    pub struct HeaderFlags: u32 {
494        /// If set, the contents of the packet are compressed and
495        /// the HYGON SP must decompress them.
496        const COMPRESSED = 0b00000001u32.to_le();
497    }
498}
499
500/// The header for a data packet that contains secret information
501/// to be injected into the guest.
502#[repr(C)]
503#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
504pub struct Header {
505    /// Describes the secret packet (for example: if it is
506    /// compressed).
507    pub flags: HeaderFlags,
508
509    /// The initialization vector.
510    pub iv: [u8; 16],
511
512    /// Integrity protection MAC.
513    pub mac: [u8; 32],
514}
515
516/// A packet containing secret information to be injected
517/// into the guest.
518#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
519pub struct Secret {
520    /// The header for this packet.
521    pub header: Header,
522
523    /// The encrypted secret to inject.
524    pub ciphertext: Vec<u8>,
525}
526
527/// A measurement of the CSV guest.
528#[repr(C)]
529#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
530pub struct Measurement {
531    /// The measurement.
532    pub measure: [u8; 32],
533
534    /// A random nonce.
535    pub mnonce: [u8; 16],
536}
537
538#[repr(C)]
539#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
540pub struct AttestationReport {
541    /// The random nonce generated by user to protect struct TeeInfoSigner.
542    pub mnonce: [u8; 16],
543
544    pub digest: [u8; 32],
545
546    /// The guest policy
547    pub policy: Policy,
548
549    /// The usage of the signature.
550    pub sig_usage: [u8; 4],
551
552    /// The algorithm of the signature.
553    pub sig_algo: [u8; 4],
554
555    /// Reserved
556    reserved: [u8; 4],
557
558    pub sig1: [[u8; 16]; 9],
559}