pub(crate) mod ioctl;
use ioctl::*;
pub(crate) mod types;
use types::*;
use crate::{
certs::{csv::Certificate, Signer, Usage, Verifiable},
crypto::{sig::ecdsa, sm, PrivateKey, PublicKey, Signature},
util::*,
Version,
};
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use std::{
convert::*,
io::{Read, Result, Write},
mem::MaybeUninit,
os::unix::io::AsRawFd,
};
pub struct New;
pub struct Started(Handle);
pub struct Measured(Handle, Measurement);
pub struct Launcher<T, U: AsRawFd, V: AsRawFd> {
state: T,
vm_fd: U,
csv: V,
}
impl<T, U: AsRawFd, V: AsRawFd> Launcher<T, U, V> {
pub fn as_mut_vmfd(&mut self) -> &mut U {
&mut self.vm_fd
}
}
impl<U: AsRawFd, V: AsRawFd> Launcher<New, U, V> {
pub fn new(kvm: U, csv: V) -> Result<Self> {
let mut launcher = Launcher {
vm_fd: kvm,
csv,
state: New,
};
let mut cmd = Command::from(&mut launcher.csv, &Init);
INIT.ioctl(&mut launcher.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(launcher)
}
pub fn new_es(kvm: U, csv: V) -> Result<Self> {
let mut launcher = Launcher {
vm_fd: kvm,
csv,
state: New,
};
let mut cmd = Command::from(&mut launcher.csv, &EsInit);
ES_INIT
.ioctl(&mut launcher.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(launcher)
}
pub fn start(mut self, start: Start) -> Result<Launcher<Started, U, V>> {
let mut launch_start = LaunchStart::new(&start.policy, &start.cert, &start.session);
let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
LAUNCH_START
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
let next = Launcher {
state: Started(launch_start.into()),
vm_fd: self.vm_fd,
csv: self.csv,
};
Ok(next)
}
pub fn start_raw(
mut self,
policy: &Policy,
cert: &Certificate,
session: &Session,
) -> Result<Launcher<Started, U, V>> {
let mut launch_start = LaunchStart::new(policy, cert, session);
let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
LAUNCH_START
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
let next = Launcher {
state: Started(launch_start.into()),
vm_fd: self.vm_fd,
csv: self.csv,
};
Ok(next)
}
pub fn start_with_policy_only(mut self, policy: Policy) -> Result<Launcher<Started, U, V>> {
let mut launch_start = LaunchStart::with_policy_only(&policy);
let mut cmd = Command::from_mut(&mut self.csv, &mut launch_start);
LAUNCH_START
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
let next = Launcher {
state: Started(launch_start.into()),
vm_fd: self.vm_fd,
csv: self.csv,
};
Ok(next)
}
}
impl<U: AsRawFd, V: AsRawFd> Launcher<Started, U, V> {
pub fn update_data(&mut self, data: &[u8]) -> Result<()> {
let launch_update_data = LaunchUpdateData::new(data);
let mut cmd = Command::from(&mut self.csv, &launch_update_data);
KvmEncRegion::new(data).register(&mut self.vm_fd)?;
LAUNCH_UPDATE_DATA
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(())
}
pub fn register_kvm_enc_region(&mut self, data: &[u8]) -> Result<()> {
KvmEncRegion::new(data).register(&mut self.vm_fd)?;
Ok(())
}
pub fn update_data_without_registration(&mut self, data: &[u8]) -> Result<()> {
let launch_update_data = LaunchUpdateData::new(data);
let mut cmd = Command::from(&mut self.csv, &launch_update_data);
LAUNCH_UPDATE_DATA
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(())
}
pub fn update_vmsa(&mut self) -> Result<()> {
let launch_update_vmsa = LaunchUpdateVmsa::new();
let mut cmd = Command::from(&mut self.csv, &launch_update_vmsa);
LAUNCH_UPDATE_VMSA
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(())
}
pub fn measure(mut self) -> Result<Launcher<Measured, U, V>> {
let mut measurement = MaybeUninit::uninit();
let mut launch_measure = LaunchMeasure::new(&mut measurement);
let mut cmd = Command::from_mut(&mut self.csv, &mut launch_measure);
LAUNCH_MEASUREMENT
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
let next = Launcher {
state: Measured(self.state.0, unsafe { measurement.assume_init() }),
vm_fd: self.vm_fd,
csv: self.csv,
};
Ok(next)
}
}
impl<U: AsRawFd, V: AsRawFd> Launcher<Measured, U, V> {
pub fn measurement(&self) -> Measurement {
self.state.1
}
pub fn get_attestation_report(&mut self, mnonce: [u8; 16]) -> Result<Box<AttestationReport>> {
let mut ar = MaybeUninit::uninit();
let mut attestation = Attestation::new(&mut ar, mnonce);
let mut cmd = Command::from_mut(&mut self.csv, &mut attestation);
ATTESTATION
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(Box::new(unsafe { ar.assume_init() }))
}
pub fn inject(&mut self, secret: &Secret, guest: usize) -> Result<()> {
let launch_secret = LaunchSecret::new(&secret.header, guest, &secret.ciphertext[..]);
let mut cmd = Command::from(&mut self.csv, &launch_secret);
LAUNCH_SECRET
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(())
}
pub fn finish(mut self) -> Result<Handle> {
let mut cmd = Command::from(&mut self.csv, &LaunchFinish);
LAUNCH_FINISH
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|e| cmd.encapsulate(e))?;
Ok(self.state.0)
}
}
bitflags! {
#[derive(Default, Deserialize, Serialize)]
pub struct PolicyFlags: u16 {
const NO_DEBUG = 0b00000001u16.to_le();
const NO_KEY_SHARING = 0b00000010u16.to_le();
const ENCRYPTED_STATE = 0b00000100u16.to_le();
const NO_SEND = 0b00001000u16.to_le();
const DOMAIN = 0b00010000u16.to_le();
const CSV = 0b00100000u16.to_le();
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct Policy {
pub flags: PolicyFlags,
pub minfw: Version,
}
impl From<u32> for Policy {
fn from(p: u32) -> Self {
let flags = p as u16;
let flags = PolicyFlags::from_bits_truncate(flags);
let p = p >> 16;
let p = p as u16;
let minfw = Version::from(p);
Self { flags, minfw }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionBody {
pub nonce: [u8; 16],
pub wrap_tk: [u8; 32],
pub wrap_iv: [u8; 16],
pub wrap_mac: [u8; 32],
pub session_mac: [u8; 32],
pub key_id: [u8; 16],
#[serde(with = "BigArray")]
pub rnd_pub_key_data: [u8; 148],
#[serde(with = "BigArray")]
pub ms_enc: [u8; 256],
pub vm_digest: [u8; 32],
pub pubkey_digest: [u8; 32],
pub vm_id: [u8; 16],
pub vm_version: [u8; 16],
#[serde(with = "BigArray")]
pub user_data: [u8; 64],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionSig {
pub r: [u8; 32],
pub s: [u8; 32],
}
impl From<ecdsa::Signature> for SessionSig {
#[inline]
fn from(value: ecdsa::Signature) -> Self {
let mut r = [0u8; 32];
let mut s = [0u8; 32];
for (i, b) in value.r.iter().take(32).cloned().enumerate() {
r[i] = b;
}
for (i, b) in value.s.iter().take(32).cloned().enumerate() {
s[i] = b;
}
SessionSig { r, s }
}
}
impl From<SessionSig> for ecdsa::Signature {
#[inline]
fn from(value: SessionSig) -> Self {
let mut r = [0u8; 72];
let mut s = [0u8; 72];
for (i, b) in value.r.iter().cloned().enumerate() {
r[i] = b;
}
for (i, b) in value.s.iter().cloned().enumerate() {
s[i] = b;
}
ecdsa::Signature { r, s }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Session {
pub body: SessionBody,
pub sig: SessionSig,
}
impl Signer<Session> for PrivateKey<Usage> {
type Output = ();
fn sign(&self, target: &mut Session, uid: String) -> Result<()> {
let slot = &mut target.sig;
let mut msg: Vec<u8> = Vec::new();
msg.save(&target.body)?;
let sig = sm::SM2::sign(self.key, uid.as_bytes(), &msg)?;
let ecdsa_sig = ecdsa::Signature::try_from(&sig[..])?;
*slot = SessionSig::from(ecdsa_sig);
Ok(())
}
}
impl TryFrom<&Session> for Signature {
type Error = std::io::Error;
#[inline]
fn try_from(value: &Session) -> Result<Self> {
let sig = ecdsa::Signature::from(value.sig);
let sig = Vec::try_from(&sig)?;
Ok(Self {
sig,
id: None,
usage: Usage::PDH,
algo: None,
})
}
}
impl codicon::Encoder<crate::Body> for Session {
type Error = std::io::Error;
fn encode(&self, mut writer: impl Write, _: crate::Body) -> Result<()> {
writer.save(&self.body)
}
}
impl Verifiable for (&Certificate, &Session) {
type Output = ();
fn verify(self) -> Result<()> {
let key: PublicKey = self.0.try_into()?;
let sig: Signature = self.1.try_into()?;
key.verify(
self.1,
&self.0.body.data.user_id[..self.0.body.data.uid_size as usize],
&sig,
)
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Start {
pub policy: Policy,
pub cert: Certificate,
pub session: Session,
}
impl codicon::Decoder<()> for Start {
type Error = std::io::Error;
fn decode(mut reader: impl Read, _: ()) -> std::io::Result<Self> {
reader.load()
}
}
impl codicon::Encoder<()> for Start {
type Error = std::io::Error;
fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> {
writer.save(self)
}
}
bitflags! {
#[derive(Default, Deserialize, Serialize)]
pub struct HeaderFlags: u32 {
const COMPRESSED = 0b00000001u32.to_le();
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct Header {
pub flags: HeaderFlags,
pub iv: [u8; 16],
pub mac: [u8; 32],
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct Secret {
pub header: Header,
pub ciphertext: Vec<u8>,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct Measurement {
pub measure: [u8; 32],
pub mnonce: [u8; 16],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct AttestationReport {
pub mnonce: [u8; 16],
pub digest: [u8; 32],
pub policy: Policy,
pub sig_usage: [u8; 4],
pub sig_algo: [u8; 4],
reserved: [u8; 4],
pub sig1: [[u8; 16]; 9],
}