Skip to content

Commit

Permalink
moved large arrays out of vmsa, DER support for private keys
Browse files Browse the repository at this point in the history
Signed-off-by: DGonzalezVillal <[email protected]>
  • Loading branch information
DGonzalezVillal committed Feb 16, 2024
1 parent 869486a commit 823fec8
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 99 deletions.
23 changes: 20 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,18 +740,35 @@ impl std::error::Error for SevHashError {}

/// Possible errors when working with the large array type
#[derive(Debug)]
pub struct LargeArrayError {
pub(crate) error_msg: String,
pub enum LargeArrayError {
/// Error when trying from slice
SliceError(TryFromSliceError),

/// Error when converting from vector
VectorError(String),
}

impl std::fmt::Display for LargeArrayError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Error when handling large array: {}", self.error_msg)
match self {
LargeArrayError::SliceError(error) => {
write!(f, "Error when trying from slice: {error}")
}
LargeArrayError::VectorError(error) => {
write!(f, "Error when trying from vector: {error}")
}
}
}
}

impl std::error::Error for LargeArrayError {}

impl std::convert::From<TryFromSliceError> for LargeArrayError {
fn from(value: TryFromSliceError) -> Self {
Self::SliceError(value)
}
}

/// Errors when calculating the ID BLOCK
#[derive(Debug)]
pub enum IdBlockError {
Expand Down
37 changes: 29 additions & 8 deletions src/measurement/idblock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn gen_id_auth_block(
id_key_file: PathBuf,
author_key_file: PathBuf,
) -> Result<IdAuth, IdBlockError> {
let id_ec_priv_key = load_priv_key_from_pem(id_key_file)?;
let id_ec_priv_key = load_priv_key(id_key_file)?;
let id_ec_pub_key = SevEcdsaPubKey::try_from(&id_ec_priv_key)?;
let id_sig = SevEcdsaSig::try_from((
id_ec_priv_key,
Expand All @@ -34,7 +34,7 @@ pub fn gen_id_auth_block(
.as_slice(),
))?;

let author_ec_priv_key = load_priv_key_from_pem(author_key_file)?;
let author_ec_priv_key = load_priv_key(author_key_file)?;
let author_pub_key = SevEcdsaPubKey::try_from(&author_ec_priv_key)?;
let author_sig = SevEcdsaSig::try_from((
author_ec_priv_key,
Expand All @@ -53,19 +53,40 @@ pub fn gen_id_auth_block(
))
}

///Read a pem key file and return a private EcKey.
enum KeyFormat {
Pem,
Der,
}

/// Identifies the format of a key based upon the first twenty-seven
/// bytes of a byte stream. A non-PEM format assumes DER format.
fn identify_priv_key_format(bytes: &[u8]) -> KeyFormat {
const PEM_START: &[u8] = b"-----BEGIN PRIVATE KEY-----";
match &bytes[0..27] {
PEM_START => KeyFormat::Pem,
_ => KeyFormat::Der,
}
}
///Read a key file and return a private EcKey.
/// Key has to be an EC P-384 key.
pub fn load_priv_key_from_pem(path: PathBuf) -> Result<EcKey<Private>, IdBlockError> {
let mut pem_data = Vec::new();
pub fn load_priv_key(path: PathBuf) -> Result<EcKey<Private>, IdBlockError> {
let mut key_data = Vec::new();
let mut file = match File::open(path) {
Ok(file) => file,
Err(e) => return Err(IdBlockError::FileError(e)),
};

file.read_to_end(&mut pem_data)
file.read_to_end(&mut key_data)
.map_err(IdBlockError::FileError)?;

let pkey = EcKey::private_key_from_pem(&pem_data).map_err(IdBlockError::CryptoErrorStack)?;
let pkey = match identify_priv_key_format(&key_data) {
KeyFormat::Pem => {
EcKey::private_key_from_pem(&key_data).map_err(IdBlockError::CryptoErrorStack)?
}
KeyFormat::Der => {
EcKey::private_key_from_der(&key_data).map_err(IdBlockError::CryptoErrorStack)?
}
};

pkey.check_key().map_err(IdBlockError::CryptoErrorStack)?;

Expand All @@ -80,7 +101,7 @@ pub fn load_priv_key_from_pem(path: PathBuf) -> Result<EcKey<Private>, IdBlockEr

/// Generate the sha384 digest of the provided pem key
pub fn generate_key_digest(key_path: PathBuf) -> Result<IdBlockLaunchDigest, IdBlockError> {
let ec_key = load_priv_key_from_pem(key_path)?;
let ec_key = load_priv_key(key_path)?;

let pub_key = SevEcdsaPubKey::try_from(&ec_key)?;

Expand Down
2 changes: 1 addition & 1 deletion src/measurement/idblock_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use openssl::{
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};

use crate::{error::IdBlockError, measurement::vmsa::LargeArray};
use crate::{error::IdBlockError, measurement::large_array::LargeArray};

pub(crate) const DEFAULT_ID_VERSION: u32 = 1;
pub(crate) const DEFAULT_ID_POLICY: u64 = 0x300000;
Expand Down
79 changes: 79 additions & 0 deletions src/measurement/large_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: Apache-2.0

//! Helpful structure to deal with arrays with a size larger than 32 bytes
use crate::error::LargeArrayError;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use std::convert::{TryFrom, TryInto};

/// Large array structure to serialize and default arrays larger than 32 bytes.
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[repr(C)]
pub struct LargeArray<T, const N: usize>(#[serde(with = "BigArray")] [T; N])
where
T: for<'a> Deserialize<'a> + Serialize;

impl<T, const N: usize> Default for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
fn default() -> Self {
Self([T::default(); N])
}
}

impl<T, const N: usize> TryFrom<Vec<T>> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
Ok(LargeArray(vec.try_into().map_err(|_| {
LargeArrayError::VectorError("Vector is the wrong size".to_string())
})?))
}
}

impl<T, const N: usize> TryFrom<[T; N]> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(array: [T; N]) -> Result<Self, Self::Error> {
Ok(LargeArray(array))
}
}

impl<T, const N: usize> TryFrom<&[T]> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
Ok(LargeArray(slice.try_into()?))
}
}

impl<T, const N: usize> LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
/// Get the large array as a regular array format
pub fn as_array(&self) -> [T; N] {
self.0
}

/// Get the large array as a slice
pub fn as_slice(&self) -> &[T; N] {
&self.0
}

/// Get the large array as a mutable slice
pub fn as_mut_slice(&mut self) -> &mut [T; N] {
&mut self.0
}
}
2 changes: 2 additions & 0 deletions src/measurement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ pub mod idblock;

#[cfg(all(feature = "snp", feature = "openssl"))]
pub mod idblock_types;

pub mod large_array;
88 changes: 4 additions & 84 deletions src/measurement/vmsa.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: Apache-2.0

//! Operations to build and interact with an SEV-ES VMSA
use crate::error::{LargeArrayError, MeasurementError};
use crate::measurement::vcpu_types::CpuType;
use crate::{
error::MeasurementError,
measurement::{large_array::LargeArray, vcpu_types::CpuType},
};
use bitfield::bitfield;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use std::{convert::TryFrom, fmt, str::FromStr};

/// Different Possible SEV modes
Expand Down Expand Up @@ -98,87 +99,6 @@ impl VmcbSeg {
}
}

/// Large array structure to serialize and default arrays larger than 32 bytes.
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[repr(C)]
pub struct LargeArray<T, const N: usize>(#[serde(with = "BigArray")] [T; N])
where
T: for<'a> Deserialize<'a> + Serialize;

impl<T, const N: usize> Default for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
fn default() -> Self {
Self([T::default(); N])
}
}

impl<T, const N: usize> TryFrom<Vec<T>> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
if vec.len() == N {
let mut array = [Default::default(); N];
array.copy_from_slice(&vec[..]);
Ok(LargeArray(array))
} else {
Err(LargeArrayError {
error_msg: "Incorrect vecotr size for conversion to LargeArray".to_string(),
})
}
}
}

impl<T, const N: usize> TryFrom<[T; N]> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(array: [T; N]) -> Result<Self, Self::Error> {
Ok(LargeArray(array))
}
}

impl<T, const N: usize> TryFrom<&[u8]> for LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
type Error = LargeArrayError;

fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() == N {
let mut array = [Default::default(); N];
for (size, i) in array.iter_mut().enumerate().take(N) {
let offset = size * std::mem::size_of::<T>();
*i = unsafe {
// Safe because we checked that the slice length matches the array size
*(slice.get_unchecked(offset) as *const u8 as *const T)
};
}
Ok(LargeArray(array))
} else {
Err(LargeArrayError {
error_msg: "Incorrect vecotr size for conversion to LargeArray".to_string(),
})
}
}
}

impl<T, const N: usize> LargeArray<T, N>
where
T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize,
{
/// Get the large array as a regular array format
pub fn as_array(&self) -> [T; N] {
self.0
}
}

bitfield! {
/// Kernel features that when enabled could affect the VMSA.
///
Expand Down
6 changes: 3 additions & 3 deletions tests/id-block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use base64::{engine::general_purpose, Engine as _};
use hex::{self, FromHex};

use sev::measurement::{
idblock::{load_priv_key_from_pem, snp_calculate_id},
idblock::{load_priv_key, snp_calculate_id},
idblock_types::{IdAuth, IdBlockLaunchDigest, SevEcdsaPubKey, SevEcdsaSig},
};

Expand Down Expand Up @@ -127,7 +127,7 @@ fn test_auth_block_generation() {
let auth_path: PathBuf = "./tests/measurement/test_auth_key.pem".into();

// Get id private test key from pem
let id_ec_priv_key = load_priv_key_from_pem(id_path).unwrap();
let id_ec_priv_key = load_priv_key(id_path).unwrap();

// Generate id public key, should always be the same
let id_ec_pub_key = SevEcdsaPubKey::try_from(&id_ec_priv_key).unwrap();
Expand All @@ -139,7 +139,7 @@ fn test_auth_block_generation() {
let id_block_sig: SevEcdsaSig = bincode::deserialize(&id_block_bytes).unwrap();

// Get author private test key from pem
let author_ec_priv_key = load_priv_key_from_pem(auth_path).unwrap();
let author_ec_priv_key = load_priv_key(auth_path).unwrap();

// Generate author public key, should always be the same
let author_pub_key = SevEcdsaPubKey::try_from(&author_ec_priv_key).unwrap();
Expand Down

0 comments on commit 823fec8

Please sign in to comment.