#![deny(clippy::all)]
#![deny(missing_docs)]
#![allow(unknown_lints)]
#![allow(clippy::identity_op)]
#![allow(clippy::unreadable_literal)]
#[cfg(all(feature = "openssl", feature = "crypto_nossl"))]
compile_error!(
"feature \"openssl\" and feature \"crypto_nossl\" cannot be enabled at the same time"
);
pub mod certs;
pub mod firmware;
#[cfg(target_os = "linux")]
pub mod launch;
#[cfg(all(
any(feature = "sev", feature = "snp"),
feature = "openssl",
target_os = "linux"
))]
pub mod measurement;
#[cfg(all(target_os = "linux", feature = "openssl", feature = "sev"))]
pub mod session;
mod util;
pub mod vmsa;
pub mod error;
pub mod parser;
use crate::parser::Decoder;
#[cfg(all(feature = "sev", feature = "dangerous_hw_tests"))]
pub use util::cached_chain;
#[cfg(all(feature = "openssl", feature = "sev"))]
use certs::sev::sev;
#[cfg(feature = "sev")]
use certs::sev::ca::{Certificate, Chain as CertSevCaChain};
#[cfg(all(
not(feature = "sev"),
feature = "snp",
any(feature = "openssl", feature = "crypto_nossl")
))]
use certs::snp::ca::Chain as CertSnpCaChain;
#[cfg(feature = "sev")]
use certs::sev::builtin as SevBuiltin;
#[cfg(all(
not(feature = "sev"),
feature = "snp",
any(feature = "openssl", feature = "crypto_nossl")
))]
use certs::snp::builtin as SnpBuiltin;
#[cfg(any(feature = "sev", feature = "snp"))]
use std::convert::TryFrom;
use std::io::{Read, Write};
#[derive(Copy, Clone)]
pub enum Generation {
#[cfg(feature = "sev")]
Naples,
#[cfg(feature = "sev")]
Rome,
#[cfg(any(feature = "sev", feature = "snp"))]
Milan,
#[cfg(any(feature = "sev", feature = "snp"))]
Genoa,
#[cfg(any(feature = "sev", feature = "snp"))]
Turin,
}
#[cfg(feature = "snp")]
impl TryFrom<&[u8]> for Generation {
type Error = std::io::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.len() != 4 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"invalid length of bytes representing cpuid",
));
}
let base_model = (bytes[0] & 0xF0) >> 4;
let base_family = bytes[1] & 0x0F;
let ext_model = bytes[2] & 0x0F;
let ext_family = {
let low = (bytes[2] & 0xF0) >> 4;
let high = (bytes[3] & 0x0F) << 4;
low | high
};
let family = base_family + ext_family;
let model = (ext_model << 4) | base_model;
Self::identify_cpu(family, model)
}
}
#[cfg(feature = "snp")]
pub type CpuFamily = u8;
#[cfg(feature = "snp")]
pub type CpuModel = u8;
#[cfg(feature = "snp")]
impl TryFrom<(CpuFamily, CpuModel)> for Generation {
type Error = std::io::Error;
fn try_from(val: (CpuFamily, CpuModel)) -> Result<Self, Self::Error> {
Self::identify_cpu(val.0, val.1)
}
}
#[cfg(feature = "snp")]
impl Generation {
pub fn identify_cpu(family: u8, model: u8) -> Result<Self, std::io::Error> {
match family {
0x19 => match model {
0x0..=0xF => Ok(Self::Milan),
0x10..=0x1F | 0xA0..=0xAF => Ok(Self::Genoa),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"processor is not of know SEV-SNP model.",
)),
},
0x1A => match model {
0x0..=0x11 => Ok(Self::Turin),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"processor is not of know SEV-SNP model.",
)),
},
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"processor is not of know SEV-SNP generation.",
)),
}
}
#[cfg(feature = "snp")]
pub fn identify_host_generation() -> Result<Self, std::io::Error> {
use std::convert::TryInto;
#[cfg(target_arch = "x86_64")]
return unsafe { std::arch::x86_64::__cpuid(0x8000_0001) }
.eax
.to_le_bytes()
.as_slice()
.try_into();
#[cfg(not(target_arch = "x86_64"))]
Err(std::io::Error::other(
"Cannot get EPYC generation on non-x86 platform",
))
}
}
#[cfg(feature = "sev")]
impl From<Generation> for CertSevCaChain {
fn from(generation: Generation) -> CertSevCaChain {
let (ark, ask) = match generation {
#[cfg(feature = "sev")]
Generation::Naples => (SevBuiltin::naples::ARK, SevBuiltin::naples::ASK),
#[cfg(feature = "sev")]
Generation::Rome => (SevBuiltin::rome::ARK, SevBuiltin::rome::ASK),
#[cfg(any(feature = "sev", feature = "snp"))]
Generation::Milan => (SevBuiltin::milan::ARK, SevBuiltin::milan::ASK),
#[cfg(any(feature = "sev", feature = "snp"))]
Generation::Genoa => (SevBuiltin::genoa::ARK, SevBuiltin::genoa::ASK),
#[cfg(any(feature = "sev", feature = "snp"))]
Generation::Turin => (SevBuiltin::turin::ARK, SevBuiltin::turin::ASK),
};
CertSevCaChain {
ask: Certificate::decode(&mut &*ask, ()).unwrap(),
ark: Certificate::decode(&mut &*ark, ()).unwrap(),
}
}
}
#[cfg(all(
not(feature = "sev"),
feature = "snp",
any(feature = "openssl", feature = "crypto_nossl")
))]
impl From<Generation> for CertSnpCaChain {
fn from(gen: Generation) -> CertSnpCaChain {
let (ark, ask) = match gen {
Generation::Milan => (
SnpBuiltin::milan::ark().unwrap(),
SnpBuiltin::milan::ask().unwrap(),
),
Generation::Genoa => (
SnpBuiltin::genoa::ark().unwrap(),
SnpBuiltin::genoa::ask().unwrap(),
),
Generation::Turin => (
SnpBuiltin::turin::ark().unwrap(),
SnpBuiltin::turin::ask().unwrap(),
),
};
CertSnpCaChain { ark, ask }
}
}
#[cfg(all(feature = "sev", feature = "openssl"))]
impl TryFrom<&sev::Chain> for Generation {
type Error = ();
fn try_from(schain: &sev::Chain) -> Result<Self, Self::Error> {
use crate::certs::sev::Verifiable;
let naples: CertSevCaChain = Generation::Naples.into();
let rome: CertSevCaChain = Generation::Rome.into();
let milan: CertSevCaChain = Generation::Milan.into();
let genoa: CertSevCaChain = Generation::Genoa.into();
let turin: CertSevCaChain = Generation::Turin.into();
Ok(if (&naples.ask, &schain.cek).verify().is_ok() {
Generation::Naples
} else if (&rome.ask, &schain.cek).verify().is_ok() {
Generation::Rome
} else if (&milan.ask, &schain.cek).verify().is_ok() {
Generation::Milan
} else if (&genoa.ask, &schain.cek).verify().is_ok() {
Generation::Genoa
} else if (&turin.ask, &schain.cek).verify().is_ok() {
Generation::Turin
} else {
return Err(());
})
}
}
#[cfg(any(feature = "sev", feature = "snp"))]
impl TryFrom<String> for Generation {
type Error = ();
fn try_from(val: String) -> Result<Self, Self::Error> {
match &val.to_lowercase()[..] {
#[cfg(feature = "sev")]
"naples" => Ok(Self::Naples),
#[cfg(feature = "sev")]
"rome" => Ok(Self::Rome),
#[cfg(any(feature = "sev", feature = "snp"))]
"milan" => Ok(Self::Milan),
#[cfg(any(feature = "sev", feature = "snp"))]
"genoa" => Ok(Self::Genoa),
#[cfg(any(feature = "sev", feature = "snp"))]
"bergamo" => Ok(Self::Genoa),
#[cfg(any(feature = "sev", feature = "snp"))]
"siena" => Ok(Self::Genoa),
#[cfg(any(feature = "sev", feature = "snp"))]
"turin" => Ok(Self::Turin),
_ => Err(()),
}
}
}
#[cfg(any(feature = "sev", feature = "snp"))]
impl Generation {
pub fn titlecase(&self) -> String {
match self {
#[cfg(feature = "sev")]
Self::Naples => "Naples".to_string(),
#[cfg(feature = "sev")]
Self::Rome => "Rome".to_string(),
#[cfg(any(feature = "sev", feature = "snp"))]
Self::Milan => "Milan".to_string(),
#[cfg(any(feature = "sev", feature = "snp"))]
Self::Genoa => "Genoa".to_string(),
#[cfg(any(feature = "sev", feature = "snp"))]
Self::Turin => "Turin".to_string(),
}
}
}