Skip to content

Commit

Permalink
Merge pull request #284 from ionut-arm/tkc-activate-cred
Browse files Browse the repository at this point in the history
Add ActivateCredential support for TKC
  • Loading branch information
ionut-arm authored Nov 18, 2021
2 parents e8cd513 + d523ce2 commit 62fb9b7
Show file tree
Hide file tree
Showing 9 changed files with 524 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
run: docker build -t ubuntucontainer tss-esapi/tests/ --file tss-esapi/tests/Dockerfile-ubuntu
- name: Run the container
run: docker run -v $(pwd):/tmp/rust-tss-esapi -w /tmp/rust-tss-esapi/tss-esapi ubuntucontainer /tmp/rust-tss-esapi/tss-esapi/tests/all-ubuntu.sh
- name: Run the cross-compilation script
run: docker run -v $(pwd):/tmp/rust-tss-esapi -w /tmp/rust-tss-esapi/tss-esapi ubuntucontainer /tmp/rust-tss-esapi/tss-esapi/tests/cross-compile.sh

tests-ubuntu-v3:
name: Ubuntu tests on v3.x.y of tpm2-tss
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,3 @@ jobs:
run: docker run -v $(pwd):/tmp/rust-tss-esapi -w /tmp/rust-tss-esapi/tss-esapi --security-opt seccomp=unconfined ubuntucontainer /tmp/rust-tss-esapi/tss-esapi/tests/coverage.sh
- name: Collect coverage results
run: bash <(curl -s https://codecov.io/bash)
- name: Run the cross-compilation script
run: docker run -v $(pwd):/tmp/rust-tss-esapi -w /tmp/rust-tss-esapi/tss-esapi ubuntucontainer /tmp/rust-tss-esapi/tss-esapi/tests/cross-compile.sh
1 change: 1 addition & 0 deletions tss-esapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ primal = "0.3.0"

[dev-dependencies]
env_logger = "0.7.1"
sha2 = "0.9.8"

[features]
generate-bindings = ["tss-esapi-sys/generate-bindings"]
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
222 changes: 222 additions & 0 deletions tss-esapi/src/abstraction/transient/key_attestation.rs
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)
}
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
Loading

0 comments on commit 62fb9b7

Please sign in to comment.