-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #284 from ionut-arm/tkc-activate-cred
Add ActivateCredential support for TKC
- Loading branch information
Showing
9 changed files
with
524 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
// Copyright 2021 Contributors to the Parsec project. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
use super::{ObjectWrapper, TransientKeyContext}; | ||
use crate::{ | ||
abstraction::ek, | ||
constants::SessionType, | ||
handles::{AuthHandle, KeyHandle, SessionHandle}, | ||
interface_types::{ | ||
algorithm::{AsymmetricAlgorithm, HashingAlgorithm}, | ||
session_handles::{AuthSession, PolicySession}, | ||
}, | ||
structures::{EncryptedSecret, IDObject, SymmetricDefinition}, | ||
tss2_esys::{Tss2_MU_TPMT_PUBLIC_Marshal, TPM2B_PUBLIC, TPMT_PUBLIC}, | ||
utils::PublicKey, | ||
Error, Result, WrapperErrorKind, | ||
}; | ||
use log::error; | ||
use std::convert::{TryFrom, TryInto}; | ||
|
||
#[derive(Debug)] | ||
/// Wrapper for the parameters needed by MakeCredential | ||
/// | ||
/// The 3rd party requesting proof that the key is indeed backed | ||
/// by a TPM would perform a MakeCredential and would thus require | ||
/// `name` and `attesting_key_pub` as inputs for that operation. | ||
/// | ||
/// `public` is not strictly needed, however it is returned as a | ||
/// convenience block of data. Since the MakeCredential operation | ||
/// bakes into the encrypted credential the identity of the key to | ||
/// be attested via its `name`, the correctness of the `name` must | ||
/// be verifiable by the said 3rd party. `public` bridges this gap: | ||
/// | ||
/// * it includes all the public parameters of the attested key | ||
/// * can be hashed (in its marshaled form) with the name hash | ||
/// (found by unmarshaling it) to obtain `name` | ||
pub struct MakeCredParams { | ||
/// TPM name of the object being attested | ||
pub name: Vec<u8>, | ||
/// Encoding of the public parameters of the object whose name | ||
/// will be included in the credential computations | ||
pub public: Vec<u8>, | ||
/// Public part of the key used to protect the credential | ||
pub attesting_key_pub: PublicKey, | ||
} | ||
|
||
impl TransientKeyContext { | ||
/// Get the data required to perform a MakeCredential | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `object` - the object whose TPM name will be included in | ||
/// the credential | ||
/// * `key` - the key to be used to encrypt the secret that wraps | ||
/// the credential | ||
/// | ||
/// **Note**: If no `key` is given, the default Endorsement Key | ||
/// will be used. | ||
pub fn get_make_cred_params( | ||
&mut self, | ||
object: ObjectWrapper, | ||
key: Option<ObjectWrapper>, | ||
) -> Result<MakeCredParams> { | ||
let object_handle = self.load_key(object.params, object.material, None)?; | ||
let (object_public, object_name, _) = | ||
self.context.read_public(object_handle).or_else(|e| { | ||
self.context.flush_context(object_handle.into())?; | ||
Err(e) | ||
})?; | ||
self.context.flush_context(object_handle.into())?; | ||
|
||
// Name of objects is derived from their publicArea, i.e. the marshaled TPMT_PUBLIC | ||
let public = TPM2B_PUBLIC::from(object_public); | ||
let mut pub_buf = [0u8; std::mem::size_of::<TPMT_PUBLIC>()]; | ||
let mut offset = 0; | ||
let result = unsafe { | ||
Tss2_MU_TPMT_PUBLIC_Marshal( | ||
&public.publicArea, | ||
&mut pub_buf as *mut u8, | ||
std::mem::size_of::<TPMT_PUBLIC>() | ||
.try_into() | ||
.map_err(|_| Error::local_error(WrapperErrorKind::InternalError))?, | ||
&mut offset, | ||
) | ||
}; | ||
let result = Error::from_tss_rc(result); | ||
if !result.is_success() { | ||
error!("Error in marshalling TPM2B"); | ||
return Err(result); | ||
} | ||
// `offset` will be small, so no risk in the conversion below | ||
let public = pub_buf[..offset as usize].to_vec(); | ||
|
||
let attesting_key_pub = match key { | ||
None => get_ek_object_public(&mut self.context)?, | ||
Some(key) => key.material.public, | ||
}; | ||
Ok(MakeCredParams { | ||
name: object_name.value().to_vec(), | ||
public, | ||
attesting_key_pub, | ||
}) | ||
} | ||
|
||
/// Perform an ActivateCredential operation for the given object | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `object` - the object whose TPM name is included in the credential | ||
/// * `key` - the key used to encrypt the secret that wraps the credential | ||
/// * `credential_blob` - encrypted credential that will be returned by the | ||
/// TPM | ||
/// * `secret` - encrypted secret that was used to encrypt the credential | ||
/// | ||
/// **Note**: if no `key` is given, the default Endorsement Key | ||
/// will be used. You can find more information about the default Endorsement | ||
/// Key in the [ek] module. | ||
pub fn activate_credential( | ||
&mut self, | ||
object: ObjectWrapper, | ||
key: Option<ObjectWrapper>, | ||
credential_blob: Vec<u8>, | ||
secret: Vec<u8>, | ||
) -> Result<Vec<u8>> { | ||
let credential_blob = IDObject::try_from(credential_blob)?; | ||
let secret = EncryptedSecret::try_from(secret)?; | ||
let object_handle = self.load_key(object.params, object.material, object.auth)?; | ||
let (key_handle, session_2) = match key { | ||
Some(key) => self.prepare_key_activate_cred(key), | ||
None => self.prepare_ek_activate_cred(), | ||
} | ||
.or_else(|e| { | ||
self.context.flush_context(object_handle.into())?; | ||
Err(e) | ||
})?; | ||
|
||
let (session_1, _, _) = self.context.sessions(); | ||
let credential = self | ||
.context | ||
.execute_with_sessions((session_1, session_2, None), |ctx| { | ||
ctx.activate_credential(object_handle, key_handle, credential_blob, secret) | ||
}) | ||
.or_else(|e| { | ||
self.context.flush_context(object_handle.into())?; | ||
self.context.flush_context(key_handle.into())?; | ||
self.context | ||
.flush_context(SessionHandle::from(session_2).into())?; | ||
Err(e) | ||
})?; | ||
|
||
self.context.flush_context(object_handle.into())?; | ||
self.context.flush_context(key_handle.into())?; | ||
self.context | ||
.flush_context(SessionHandle::from(session_2).into())?; | ||
Ok(credential.value().to_vec()) | ||
} | ||
|
||
// No key was given, use the EK. This requires using a Policy session | ||
fn prepare_ek_activate_cred(&mut self) -> Result<(KeyHandle, Option<AuthSession>)> { | ||
let session = self.context.start_auth_session( | ||
None, | ||
None, | ||
None, | ||
SessionType::Policy, | ||
SymmetricDefinition::AES_128_CFB, | ||
HashingAlgorithm::Sha256, | ||
)?; | ||
let _ = self.context.policy_secret( | ||
PolicySession::try_from(session.unwrap()) | ||
.expect("Failed to convert auth session to policy session"), | ||
AuthHandle::Endorsement, | ||
Default::default(), | ||
Default::default(), | ||
Default::default(), | ||
None, | ||
); | ||
Ok(( | ||
ek::create_ek_object(&mut self.context, AsymmetricAlgorithm::Rsa, None).or_else( | ||
|e| { | ||
self.context | ||
.flush_context(SessionHandle::from(session).into())?; | ||
Err(e) | ||
}, | ||
)?, | ||
session, | ||
)) | ||
} | ||
|
||
// Load key and create a HMAC session for it | ||
fn prepare_key_activate_cred( | ||
&mut self, | ||
key: ObjectWrapper, | ||
) -> Result<(KeyHandle, Option<AuthSession>)> { | ||
let session = self.context.start_auth_session( | ||
None, | ||
None, | ||
None, | ||
SessionType::Hmac, | ||
SymmetricDefinition::AES_128_CFB, | ||
HashingAlgorithm::Sha256, | ||
)?; | ||
Ok(( | ||
self.load_key(key.params, key.material, key.auth) | ||
.or_else(|e| { | ||
self.context | ||
.flush_context(SessionHandle::from(session).into())?; | ||
Err(e) | ||
})?, | ||
session, | ||
)) | ||
} | ||
} | ||
|
||
fn get_ek_object_public(context: &mut crate::Context) -> Result<PublicKey> { | ||
let key_handle = ek::create_ek_object(context, AsymmetricAlgorithm::Rsa, None)?; | ||
let (attesting_key_pub, _, _) = context.read_public(key_handle).or_else(|e| { | ||
context.flush_context(key_handle.into())?; | ||
Err(e) | ||
})?; | ||
context.flush_context(key_handle.into())?; | ||
|
||
PublicKey::try_from(attesting_key_pub) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.