1#![deny(clippy::all)]
83#![deny(missing_docs)]
84#![allow(unknown_lints)]
85#![allow(clippy::identity_op)]
86#![allow(clippy::unreadable_literal)]
87
88#[cfg(all(feature = "openssl", feature = "crypto_nossl"))]
89compile_error!(
90 "feature \"openssl\" and feature \"crypto_nossl\" cannot be enabled at the same time"
91);
92
93pub mod certs;
95
96pub mod firmware;
97#[cfg(target_os = "linux")]
98pub mod launch;
99#[cfg(all(
100 any(feature = "sev", feature = "snp"),
101 feature = "openssl",
102 target_os = "linux"
103))]
104pub mod measurement;
105#[cfg(all(target_os = "linux", feature = "openssl", feature = "sev"))]
106pub mod session;
107mod util;
108pub mod vmsa;
109
110pub mod error;
112
113pub mod parser;
115
116use crate::parser::Decoder;
117
118#[cfg(all(feature = "sev", feature = "dangerous_hw_tests"))]
119pub use util::cached_chain;
120
121#[cfg(all(feature = "openssl", feature = "sev"))]
122use certs::sev::sev;
123
124#[cfg(feature = "sev")]
125use certs::sev::ca::{Certificate, Chain as CertSevCaChain};
126
127#[cfg(all(
128 not(feature = "sev"),
129 feature = "snp",
130 any(feature = "openssl", feature = "crypto_nossl")
131))]
132use certs::snp::ca::Chain as CertSnpCaChain;
133
134#[cfg(feature = "sev")]
135use certs::sev::builtin as SevBuiltin;
136
137#[cfg(all(
138 not(feature = "sev"),
139 feature = "snp",
140 any(feature = "openssl", feature = "crypto_nossl")
141))]
142use certs::snp::builtin as SnpBuiltin;
143
144#[cfg(any(feature = "sev", feature = "snp"))]
145use std::convert::TryFrom;
146
147use std::io::{Read, Write};
148
149#[derive(Copy, Clone)]
190pub enum Generation {
191 #[cfg(feature = "sev")]
193 Naples,
194
195 #[cfg(feature = "sev")]
197 Rome,
198
199 #[cfg(any(feature = "sev", feature = "snp"))]
201 Milan,
202
203 #[cfg(any(feature = "sev", feature = "snp"))]
205 Genoa,
206
207 #[cfg(any(feature = "sev", feature = "snp"))]
209 Turin,
210}
211
212#[cfg(feature = "snp")]
213impl TryFrom<&[u8]> for Generation {
214 type Error = std::io::Error;
215
216 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
217 if bytes.len() != 4 {
218 return Err(std::io::Error::new(
219 std::io::ErrorKind::InvalidData,
220 "invalid length of bytes representing cpuid",
221 ));
222 }
223
224 let base_model = (bytes[0] & 0xF0) >> 4;
225 let base_family = bytes[1] & 0x0F;
226
227 let ext_model = bytes[2] & 0x0F;
228
229 let ext_family = {
230 let low = (bytes[2] & 0xF0) >> 4;
231 let high = (bytes[3] & 0x0F) << 4;
232
233 low | high
234 };
235
236 let family = base_family + ext_family;
237 let model = (ext_model << 4) | base_model;
238
239 Self::identify_cpu(family, model)
240 }
241}
242
243#[cfg(feature = "snp")]
245pub type CpuFamily = u8;
246
247#[cfg(feature = "snp")]
249pub type CpuModel = u8;
250
251#[cfg(feature = "snp")]
252impl TryFrom<(CpuFamily, CpuModel)> for Generation {
253 type Error = std::io::Error;
254
255 fn try_from(val: (CpuFamily, CpuModel)) -> Result<Self, Self::Error> {
256 Self::identify_cpu(val.0, val.1)
257 }
258}
259
260#[cfg(feature = "snp")]
261impl Generation {
262 pub fn identify_cpu(family: u8, model: u8) -> Result<Self, std::io::Error> {
264 match family {
265 0x19 => match model {
266 0x0..=0xF => Ok(Self::Milan),
267 0x10..=0x1F | 0xA0..=0xAF => Ok(Self::Genoa),
268 _ => Err(std::io::Error::new(
269 std::io::ErrorKind::InvalidData,
270 "processor is not of know SEV-SNP model.",
271 )),
272 },
273 0x1A => match model {
274 0x0..=0x11 => Ok(Self::Turin),
275 _ => Err(std::io::Error::new(
276 std::io::ErrorKind::InvalidData,
277 "processor is not of know SEV-SNP model.",
278 )),
279 },
280 _ => Err(std::io::Error::new(
281 std::io::ErrorKind::InvalidData,
282 "processor is not of know SEV-SNP generation.",
283 )),
284 }
285 }
286
287 #[cfg(feature = "snp")]
289 pub fn identify_host_generation() -> Result<Self, std::io::Error> {
290 use std::convert::TryInto;
291
292 #[cfg(target_arch = "x86_64")]
293 return unsafe { std::arch::x86_64::__cpuid(0x8000_0001) }
294 .eax
295 .to_le_bytes()
296 .as_slice()
297 .try_into();
298
299 #[cfg(not(target_arch = "x86_64"))]
300 Err(std::io::Error::other(
301 "Cannot get EPYC generation on non-x86 platform",
302 ))
303 }
304}
305
306#[cfg(feature = "sev")]
307impl From<Generation> for CertSevCaChain {
308 fn from(generation: Generation) -> CertSevCaChain {
309 let (ark, ask) = match generation {
310 #[cfg(feature = "sev")]
311 Generation::Naples => (SevBuiltin::naples::ARK, SevBuiltin::naples::ASK),
312 #[cfg(feature = "sev")]
313 Generation::Rome => (SevBuiltin::rome::ARK, SevBuiltin::rome::ASK),
314 #[cfg(any(feature = "sev", feature = "snp"))]
315 Generation::Milan => (SevBuiltin::milan::ARK, SevBuiltin::milan::ASK),
316 #[cfg(any(feature = "sev", feature = "snp"))]
317 Generation::Genoa => (SevBuiltin::genoa::ARK, SevBuiltin::genoa::ASK),
318 #[cfg(any(feature = "sev", feature = "snp"))]
319 Generation::Turin => (SevBuiltin::turin::ARK, SevBuiltin::turin::ASK),
320 };
321
322 CertSevCaChain {
323 ask: Certificate::decode(&mut &*ask, ()).unwrap(),
324 ark: Certificate::decode(&mut &*ark, ()).unwrap(),
325 }
326 }
327}
328
329#[cfg(all(
330 not(feature = "sev"),
331 feature = "snp",
332 any(feature = "openssl", feature = "crypto_nossl")
333))]
334impl From<Generation> for CertSnpCaChain {
335 fn from(gen: Generation) -> CertSnpCaChain {
336 let (ark, ask) = match gen {
337 Generation::Milan => (
338 SnpBuiltin::milan::ark().unwrap(),
339 SnpBuiltin::milan::ask().unwrap(),
340 ),
341 Generation::Genoa => (
342 SnpBuiltin::genoa::ark().unwrap(),
343 SnpBuiltin::genoa::ask().unwrap(),
344 ),
345 Generation::Turin => (
346 SnpBuiltin::turin::ark().unwrap(),
347 SnpBuiltin::turin::ask().unwrap(),
348 ),
349 };
350
351 CertSnpCaChain { ark, ask }
352 }
353}
354
355#[cfg(all(feature = "sev", feature = "openssl"))]
356impl TryFrom<&sev::Chain> for Generation {
357 type Error = ();
358
359 fn try_from(schain: &sev::Chain) -> Result<Self, Self::Error> {
360 use crate::certs::sev::Verifiable;
361
362 let naples: CertSevCaChain = Generation::Naples.into();
363 let rome: CertSevCaChain = Generation::Rome.into();
364 let milan: CertSevCaChain = Generation::Milan.into();
365 let genoa: CertSevCaChain = Generation::Genoa.into();
366 let turin: CertSevCaChain = Generation::Turin.into();
367
368 Ok(if (&naples.ask, &schain.cek).verify().is_ok() {
369 Generation::Naples
370 } else if (&rome.ask, &schain.cek).verify().is_ok() {
371 Generation::Rome
372 } else if (&milan.ask, &schain.cek).verify().is_ok() {
373 Generation::Milan
374 } else if (&genoa.ask, &schain.cek).verify().is_ok() {
375 Generation::Genoa
376 } else if (&turin.ask, &schain.cek).verify().is_ok() {
377 Generation::Turin
378 } else {
379 return Err(());
380 })
381 }
382}
383
384#[cfg(any(feature = "sev", feature = "snp"))]
385impl TryFrom<String> for Generation {
386 type Error = ();
387
388 fn try_from(val: String) -> Result<Self, Self::Error> {
389 match &val.to_lowercase()[..] {
390 #[cfg(feature = "sev")]
391 "naples" => Ok(Self::Naples),
392
393 #[cfg(feature = "sev")]
394 "rome" => Ok(Self::Rome),
395
396 #[cfg(any(feature = "sev", feature = "snp"))]
397 "milan" => Ok(Self::Milan),
398
399 #[cfg(any(feature = "sev", feature = "snp"))]
400 "genoa" => Ok(Self::Genoa),
401
402 #[cfg(any(feature = "sev", feature = "snp"))]
403 "bergamo" => Ok(Self::Genoa),
404
405 #[cfg(any(feature = "sev", feature = "snp"))]
406 "siena" => Ok(Self::Genoa),
407
408 #[cfg(any(feature = "sev", feature = "snp"))]
409 "turin" => Ok(Self::Turin),
410
411 _ => Err(()),
412 }
413 }
414}
415
416#[cfg(any(feature = "sev", feature = "snp"))]
417impl Generation {
418 pub fn titlecase(&self) -> String {
420 match self {
421 #[cfg(feature = "sev")]
422 Self::Naples => "Naples".to_string(),
423
424 #[cfg(feature = "sev")]
425 Self::Rome => "Rome".to_string(),
426
427 #[cfg(any(feature = "sev", feature = "snp"))]
428 Self::Milan => "Milan".to_string(),
429
430 #[cfg(any(feature = "sev", feature = "snp"))]
431 Self::Genoa => "Genoa".to_string(),
432
433 #[cfg(any(feature = "sev", feature = "snp"))]
434 Self::Turin => "Turin".to_string(),
435 }
436 }
437}