Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding functions and types to calculate AUTH and ID blocks #139

Merged
merged 3 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ rsa = { version = "0.9.2", optional = true }
sha2 = { version = "0.10.8", optional = true }
x509-cert = { version = "0.2.4", optional = true }
byteorder = "1.4.3"
base64 = "0.21.5"

[dev-dependencies]
kvm-ioctls = ">=0.12"
Expand Down
102 changes: 102 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,104 @@ impl std::fmt::Display for SevHashError {

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

/// Possible errors when working with the large array type
#[derive(Debug)]
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 {
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 {
#[cfg(all(feature = "snp", feature = "openssl"))]
/// TryFrom Slice Error handling
CryptoErrorStack(openssl::error::ErrorStack),

/// Large Array Error handling
LargeArrayError(LargeArrayError),

/// File Error Handling
FileError(std::io::Error),

/// Bincode Error Handling
BincodeError(bincode::ErrorKind),

/// Error from when handling SEV Curve algorithm
SevCurveError(),

/// Error when handling SEV ECDSA Signature
SevEcsdsaSigError(String),
}

impl std::fmt::Display for IdBlockError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
#[cfg(all(feature = "snp", feature = "openssl"))]
IdBlockError::CryptoErrorStack(e) => write!(f, "Error when with OPENSSL: {e}"),
IdBlockError::LargeArrayError(e) => write!(f, "{e}"),
IdBlockError::FileError(e) => write!(f, "Failed handling file: {e}"),
IdBlockError::BincodeError(e) => write!(f, "Bincode error encountered: {e}"),
IdBlockError::SevCurveError() => {
write!(f, "Wrong curve used in the provided private key")
}
IdBlockError::SevEcsdsaSigError(msg) => {
write!(f, "Error validation SEV signature: {msg}")
}
}
}
}

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

#[cfg(all(feature = "snp", feature = "openssl"))]
impl std::convert::From<openssl::error::ErrorStack> for IdBlockError {
fn from(value: openssl::error::ErrorStack) -> Self {
Self::CryptoErrorStack(value)
}
}

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

impl std::convert::From<std::io::Error> for IdBlockError {
fn from(value: std::io::Error) -> Self {
Self::FileError(value)
}
}
impl std::convert::From<bincode::ErrorKind> for IdBlockError {
fn from(value: bincode::ErrorKind) -> Self {
Self::BincodeError(value)
}
}
Comment on lines +817 to +839
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't these be converted by map_err()? Is the From implementation necessary?


/// Errors which may be encountered when calculating the guest measurement.
#[derive(Debug)]
pub enum MeasurementError {
Expand Down Expand Up @@ -767,6 +865,9 @@ pub enum MeasurementError {
/// SEV Hash Error Handling
SevHashError(SevHashError),

/// Id Block Error Handling
IdBlockError(IdBlockError),

/// Invalid VCPU provided
InvalidVcpuTypeError(String),

Expand Down Expand Up @@ -794,6 +895,7 @@ impl std::fmt::Display for MeasurementError {
MeasurementError::GCTXError(e) => write!(f, "GCTX Error Encountered: {e}"),
MeasurementError::OVMFError(e) => write!(f, "OVMF Error Encountered: {e}"),
MeasurementError::SevHashError(e) => write!(f, "Sev hash Error Encountered: {e}"),
MeasurementError::IdBlockError(e) => write!(f, "Id Block Error Encountered: {e}"),
MeasurementError::InvalidVcpuTypeError(value) => {
write!(f, "Invalid VCPU type value provided: {value}")
}
Expand Down
139 changes: 139 additions & 0 deletions src/measurement/idblock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: Apache-2.0

//! Functions to use to calculate the ID-BLOCK and the AUTH-BLOCK.

use bincode;
use openssl::{ec::EcKey, pkey::Private, sha::sha384};
use std::{
convert::{TryFrom, TryInto},
fs::File,
io::Read,
path::PathBuf,
};

use crate::{
error::IdBlockError,
measurement::idblock_types::{
FamilyId, IdAuth, IdBlock, IdBlockLaunchDigest, IdMeasurements, ImageId, SevEcdsaPubKey,
SevEcdsaSig, CURVE_P384_NID,
},
};

/// Generate an AUTH-BLOCK using 2 EC P-384 keys and an already calculated ID-BlOCK
pub fn gen_id_auth_block(
id_block: &IdBlock,
id_key_file: PathBuf,
author_key_file: PathBuf,
) -> Result<IdAuth, IdBlockError> {
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,
bincode::serialize(id_block)
.map_err(|e| IdBlockError::BincodeError(*e))?
.as_slice(),
))?;

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,
bincode::serialize(&id_ec_pub_key)
.map_err(|e| IdBlockError::BincodeError(*e))?
.as_slice(),
))?;

Ok(IdAuth::new(
None,
None,
id_sig,
id_ec_pub_key,
author_sig,
author_pub_key,
))
}

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(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 key_data)
.map_err(IdBlockError::FileError)?;

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)?;

if let Some(name) = pkey.group().curve_name() {
if name != CURVE_P384_NID {
return Err(IdBlockError::SevCurveError());
};
};

Ok(pkey)
}

/// 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(key_path)?;

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

Ok(IdBlockLaunchDigest::new(
sha384(
bincode::serialize(&pub_key)
.map_err(|e| IdBlockError::BincodeError(*e))?
.as_slice(),
)
.try_into()?,
))
}

/// Calculate the different pieces needed for a complete pre-attestation.
/// ID-BLOCK, AUTH-BLOCK, id-key digest and auth-key digest.
pub fn snp_calculate_id(
ld: Option<IdBlockLaunchDigest>,
family_id: Option<FamilyId>,
image_id: Option<ImageId>,
svn: Option<u32>,
policy: Option<u64>,
id_key_file: PathBuf,
auth_key_file: PathBuf,
) -> Result<IdMeasurements, IdBlockError> {
let id_block = IdBlock::new(ld, family_id, image_id, svn, policy)?;

Ok(IdMeasurements {
id_block,
id_auth: gen_id_auth_block(&id_block, id_key_file.clone(), auth_key_file.clone())?,

id_key_digest: generate_key_digest(id_key_file)?,

auth_key_digest: generate_key_digest(auth_key_file)?,
})
}
Loading
Loading