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

Add ActivateCredential support for TKC #284

Merged
merged 9 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 5 additions & 3 deletions tss-esapi/src/abstraction/ek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ use std::convert::TryFrom;
const RSA_2048_EK_CERTIFICATE_NV_INDEX: u32 = 0x01c00002;
const ECC_P256_EK_CERTIFICATE_NV_INDEX: u32 = 0x01c0000a;

// Source: TCG EK Credential Profile for TPM Family 2.0; Level 0 Version 2.3 Revision 2
// Appendix B.3.3 and B.3.4
fn create_ek_public_from_default_template<IKC: IntoKeyCustomization>(
/// Get the [`Public`] representing a default Endorsement Key
///
/// Source: TCG EK Credential Profile for TPM Family 2.0; Level 0 Version 2.3 Revision 2
/// Appendix B.3.3 and B.3.4
pub fn create_ek_public_from_default_template<IKC: IntoKeyCustomization>(
alg: AsymmetricAlgorithm,
key_customization: IKC,
) -> Result<Public> {
Expand Down
203 changes: 203 additions & 0 deletions tss-esapi/src/abstraction/transient/key_attestation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// 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_TPM2B_PUBLIC_Marshal, TPM2B_PUBLIC},
utils::PublicKey,
Error, Result,
};
use log::error;
use std::convert::TryFrom;

#[derive(Debug)]
/// Wrapper for the parameters needed by MakeCredential
pub struct MakeCredParams {
/// TPM name of the object
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>,
ionut-arm marked this conversation as resolved.
Show resolved Hide resolved
/// 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())?;

let public = TPM2B_PUBLIC::from(object_public);
let mut pub_buf = [0u8; std::mem::size_of::<TPM2B_PUBLIC>()];
let mut offset = 0;
let result = unsafe {
Tss2_MU_TPM2B_PUBLIC_Marshal(
&public,
&mut pub_buf as *mut u8,
pub_buf.len() as u64,
&mut offset,
)
};
let result = Error::from_tss_rc(result);
if !result.is_success() {
error!("Error in marshalling TPM2B");
return Err(result);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

I wonder if we should implement methods on the types that can be marshalled, to convert them to Vec<u8> using these functions from the library

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just recently I've been reading the TPM book and in the chapter on auditing there is a passage about the need for marshalled command bytes:

For a command, the hash calculation, which requires marshaled parameters, should be straightforward. A TSS would naturally expose command-parameter marshaling to assist in the command-parameter authorization operation. Responses are trickier, because a TSS naturally unmarshals responses but doesn’t marshal them. One approach is for the audit log to hold the marshaled response as well as the response parameters. The auditor can use the TSS to unmarshal and then validate those response parameters. If the TSS doesn’t expose the unmarshal function, or if the audit log doesn’t hold the marshaled response, the auditor has no choice but to write or obtain a marshaling function. Because a TPM naturally has this function, it’s possible that it can be copied from a future open source TPM implementation.

(A Practical Guide to TPM 2.0 chapter 16 "Auditing TPM Commands" section "Audit Log")

By the way usually the error! calls also print the inner result as in:

error!("Error in loading external object: {}", ret);

(in object_commands.rs)


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: pub_buf.to_vec(),
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(
ionut-arm marked this conversation as resolved.
Show resolved Hide resolved
&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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ use crate::{

use log::error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use zeroize::Zeroize;

mod key_attestation;

pub use key_attestation::MakeCredParams;

/// Parameters for the kinds of keys supported by the context
#[derive(Debug, Clone, Copy)]
pub enum KeyParams {
Expand Down Expand Up @@ -89,6 +94,13 @@ impl KeyMaterial {
}
}

#[derive(Debug, Clone)]
pub struct ObjectWrapper {
pub material: KeyMaterial,
pub params: KeyParams,
pub auth: Option<Auth>,
}

/// Structure offering an abstracted programming experience.
///
/// The `TransientKeyContext` makes use of a root key from which the other, client-controlled
Expand Down Expand Up @@ -523,10 +535,10 @@ impl TransientKeyContext {
#[derive(Debug)]
pub struct TransientKeyContextBuilder {
tcti_name_conf: TctiNameConf,
hierarchy: Hierarchy,
root_key_size: u16, // TODO: replace with root key PUBLIC definition
root_key_auth_size: usize,
hierarchy_auth: Vec<u8>,
root_hierarchy: Hierarchy,
hierarchy_auth: HashMap<Hierarchy, Vec<u8>>,
default_context_cipher: SymmetricDefinitionObject,
session_hash_alg: HashingAlgorithm,
}
Expand All @@ -536,10 +548,10 @@ impl TransientKeyContextBuilder {
pub fn new() -> Self {
TransientKeyContextBuilder {
tcti_name_conf: TctiNameConf::Device(Default::default()),
hierarchy: Hierarchy::Owner,
root_hierarchy: Hierarchy::Owner,
root_key_size: 2048,
root_key_auth_size: 32,
hierarchy_auth: Vec::new(),
hierarchy_auth: HashMap::new(),
default_context_cipher: SymmetricDefinitionObject::AES_256_CFB,
session_hash_alg: HashingAlgorithm::Sha256,
}
Expand All @@ -551,9 +563,15 @@ impl TransientKeyContextBuilder {
self
}

/// Set the auth values for any hierarchies that will be used
pub fn with_hierarchy_auth(mut self, hierarchy: Hierarchy, auth: Vec<u8>) -> Self {
let _ = self.hierarchy_auth.insert(hierarchy, auth);
self
}

/// Define which hierarchy will be used for the keys being managed.
pub fn with_hierarchy(mut self, hierarchy: Hierarchy) -> Self {
self.hierarchy = hierarchy;
pub fn with_root_hierarchy(mut self, hierarchy: Hierarchy) -> Self {
self.root_hierarchy = hierarchy;
self
}

Expand All @@ -569,12 +587,6 @@ impl TransientKeyContextBuilder {
self
}

/// Input the authentication value of the working hierarchy.
pub fn with_hierarchy_auth(mut self, hierarchy_auth: Vec<u8>) -> Self {
self.hierarchy_auth = hierarchy_auth;
self
}

/// Define the cipher to be used within this context as a default.
///
/// Currently this default is used for:
Expand Down Expand Up @@ -615,7 +627,7 @@ impl TransientKeyContextBuilder {
/// `Context::set_handle_auth`
/// * if the root key authentication size is given greater than 32 or if the root key size is
/// not 1024, 2048, 3072 or 4096, a `InvalidParam` wrapper error is returned
pub fn build(self) -> Result<TransientKeyContext> {
pub fn build(mut self) -> Result<TransientKeyContext> {
if self.root_key_auth_size > 32 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
Expand All @@ -631,9 +643,9 @@ impl TransientKeyContextBuilder {
None
};

if !self.hierarchy_auth.is_empty() {
let auth_hierarchy = Auth::try_from(self.hierarchy_auth)?;
context.tr_set_auth(self.hierarchy.into(), &auth_hierarchy)?;
for (hierarchy, auth) in self.hierarchy_auth.drain() {
let auth_hierarchy = Auth::try_from(auth)?;
context.tr_set_auth(hierarchy.into(), &auth_hierarchy)?;
}

let session = context
Expand All @@ -660,7 +672,7 @@ impl TransientKeyContextBuilder {

let root_key_handle = context
.create_primary(
self.hierarchy,
self.root_hierarchy,
&create_restricted_decryption_rsa_public(
self.default_context_cipher,
root_key_rsa_key_bits,
Expand Down
2 changes: 1 addition & 1 deletion tss-esapi/src/interface_types/resource_handles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::convert::TryFrom;
///
/// Enum describing the object hierarchies in a TPM 2.0.
//////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Hierarchy {
Owner,
Platform,
Expand Down
Loading