Skip to content

Commit 3849337

Browse files
committed
Add ActivateCredential support for TKC
This commit adds support for two operations, enabling key attestation via the ActivateCredential call. A test has also been added to verify attestation using the Endorsement Key. Signed-off-by: Ionut Mihalcea <[email protected]>
1 parent 714fe81 commit 3849337

File tree

4 files changed

+301
-6
lines changed

4 files changed

+301
-6
lines changed

tss-esapi/src/abstraction/ek.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ use std::convert::TryFrom;
2424
const RSA_2048_EK_CERTIFICATE_NV_INDEX: u32 = 0x01c00002;
2525
const ECC_P256_EK_CERTIFICATE_NV_INDEX: u32 = 0x01c0000a;
2626

27-
// Source: TCG EK Credential Profile for TPM Family 2.0; Level 0 Version 2.3 Revision 2
28-
// Appendix B.3.3 and B.3.4
29-
fn create_ek_public_from_default_template<IKC: IntoKeyCustomization>(
27+
/// Get the [`Public`] representing a default Endorsement Key
28+
///
29+
/// Source: TCG EK Credential Profile for TPM Family 2.0; Level 0 Version 2.3 Revision 2
30+
/// Appendix B.3.3 and B.3.4
31+
pub fn create_ek_public_from_default_template<IKC: IntoKeyCustomization>(
3032
alg: AsymmetricAlgorithm,
3133
key_customization: IKC,
3234
) -> Result<Public> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
use super::{ObjectWrapper, TransientKeyContext};
4+
use crate::{
5+
abstraction::ek,
6+
constants::SessionType,
7+
handles::{AuthHandle, SessionHandle},
8+
interface_types::{
9+
algorithm::{AsymmetricAlgorithm, HashingAlgorithm},
10+
session_handles::PolicySession,
11+
},
12+
structures::{EncryptedSecret, IDObject, SymmetricDefinition},
13+
tss2_esys::{TPM2B_PUBLIC, TPMT_PUBLIC},
14+
utils::PublicKey,
15+
Result,
16+
};
17+
use std::convert::{TryFrom, TryInto};
18+
19+
#[derive(Debug)]
20+
/// Wrapper for the parameters needed by MakeCredential
21+
pub struct MakeCredParams {
22+
/// TPM name of the object
23+
name: Vec<u8>,
24+
/// Encoding of the public parameters of the object whose name
25+
/// will be included in the credential computations
26+
public: Vec<u8>,
27+
/// Public part of the key used to protect the credential
28+
attesting_key_pub: PublicKey,
29+
}
30+
31+
impl MakeCredParams {
32+
pub fn name(&self) -> &[u8] {
33+
&self.name
34+
}
35+
36+
pub fn public(&self) -> &[u8] {
37+
&self.public
38+
}
39+
40+
pub fn attesting_key_pub(&self) -> &PublicKey {
41+
&self.attesting_key_pub
42+
}
43+
}
44+
45+
impl TransientKeyContext {
46+
/// Get the data required to perform a MakeCredential
47+
///
48+
/// # Parameters
49+
///
50+
/// * `object` - the object whose TPM name will be included in
51+
/// the credential
52+
/// * `key` - the key to be used to encrypt the secret that wraps
53+
/// the credential
54+
///
55+
/// **Note**: If no `key` is given, the default Endorsement Key
56+
/// will be used.
57+
pub fn get_make_cred_params(
58+
&mut self,
59+
object: ObjectWrapper,
60+
key: Option<ObjectWrapper>,
61+
) -> Result<MakeCredParams> {
62+
let object_handle = self.load_key(object.params, object.material, None)?;
63+
let (object_public, object_name, _) =
64+
self.context.read_public(object_handle).or_else(|e| {
65+
self.context.flush_context(object_handle.into())?;
66+
Err(e)
67+
})?;
68+
self.context.flush_context(object_handle.into())?;
69+
70+
let public = TPM2B_PUBLIC::from(object_public);
71+
let public = unsafe {
72+
std::mem::transmute::<TPMT_PUBLIC, [u8; std::mem::size_of::<TPMT_PUBLIC>()]>(
73+
public.publicArea,
74+
)
75+
};
76+
let attesting_key_pub = match key {
77+
None => {
78+
let key_handle =
79+
ek::create_ek_object(&mut self.context, AsymmetricAlgorithm::Rsa, None)?;
80+
let (attesting_key_pub, _, _) =
81+
self.context.read_public(key_handle).or_else(|e| {
82+
self.context.flush_context(key_handle.into())?;
83+
Err(e)
84+
})?;
85+
self.context.flush_context(key_handle.into())?;
86+
87+
attesting_key_pub.try_into()?
88+
}
89+
Some(key) => key.material.public,
90+
};
91+
Ok(MakeCredParams {
92+
name: object_name.value().to_vec(),
93+
public: public.to_vec(),
94+
attesting_key_pub,
95+
})
96+
}
97+
98+
/// Perform an ActivateCredential operation for the given object
99+
///
100+
/// # Parameters
101+
///
102+
/// * `object` - the object whose TPM name is included in the credential
103+
/// * `key` - the key used to encrypt the secret that wraps the credential
104+
/// * `credential_blob` - encrypted credential that will be returned by the
105+
/// TPM
106+
/// * `secret` - encrypted secret that was used to encrypt the credential
107+
///
108+
/// **Note**: if no `key` is given, the default Endorsement Key
109+
/// will be used. You can find more information about the default Endorsement
110+
/// Key in the [ek] module.
111+
pub fn activate_credential(
112+
&mut self,
113+
object: ObjectWrapper,
114+
key: Option<ObjectWrapper>,
115+
credential_blob: Vec<u8>,
116+
secret: Vec<u8>,
117+
) -> Result<Vec<u8>> {
118+
let credential_blob = IDObject::try_from(credential_blob)?;
119+
let secret = EncryptedSecret::try_from(secret)?;
120+
let object_handle = self.load_key(object.params, object.material, object.auth)?;
121+
let session_2;
122+
let key_handle = match key {
123+
None => {
124+
// No key was given, use the EK. This requires using a Policy session
125+
session_2 = self
126+
.context
127+
.start_auth_session(
128+
None,
129+
None,
130+
None,
131+
SessionType::Policy,
132+
SymmetricDefinition::AES_128_CFB,
133+
HashingAlgorithm::Sha256,
134+
)
135+
.or_else(|e| {
136+
self.context.flush_context(object_handle.into())?;
137+
Err(e)
138+
})?;
139+
let _ = self.context.policy_secret(
140+
PolicySession::try_from(session_2.unwrap())
141+
.expect("Failed to convert auth session to policy session"),
142+
AuthHandle::Endorsement,
143+
Default::default(),
144+
Default::default(),
145+
Default::default(),
146+
None,
147+
);
148+
ek::create_ek_object(&mut self.context, AsymmetricAlgorithm::Rsa, None).or_else(
149+
|e| {
150+
self.context.flush_context(object_handle.into())?;
151+
self.context
152+
.flush_context(SessionHandle::from(session_2).into())?;
153+
Err(e)
154+
},
155+
)?
156+
}
157+
Some(key) => {
158+
// Load key and create a HMAC session for it
159+
session_2 = self
160+
.context
161+
.start_auth_session(
162+
None,
163+
None,
164+
None,
165+
SessionType::Hmac,
166+
SymmetricDefinition::AES_128_CFB,
167+
HashingAlgorithm::Sha256,
168+
)
169+
.or_else(|e| {
170+
self.context.flush_context(object_handle.into())?;
171+
Err(e)
172+
})?;
173+
self.load_key(key.params, key.material, key.auth)
174+
.or_else(|e| {
175+
self.context.flush_context(object_handle.into())?;
176+
self.context
177+
.flush_context(SessionHandle::from(session_2).into())?;
178+
Err(e)
179+
})?
180+
}
181+
};
182+
183+
let (session_1, _, _) = self.context.sessions();
184+
let credential = self
185+
.context
186+
.execute_with_sessions((session_1, session_2, None), |ctx| {
187+
ctx.activate_credential(object_handle, key_handle, credential_blob, secret)
188+
})
189+
.or_else(|e| {
190+
self.context.flush_context(object_handle.into())?;
191+
self.context.flush_context(key_handle.into())?;
192+
self.context
193+
.flush_context(SessionHandle::from(session_2).into())?;
194+
Err(e)
195+
})?;
196+
197+
self.context.flush_context(object_handle.into())?;
198+
self.context.flush_context(key_handle.into())?;
199+
self.context
200+
.flush_context(SessionHandle::from(session_2).into())?;
201+
Ok(credential.value().to_vec())
202+
}
203+
}

tss-esapi/src/abstraction/transient.rs tss-esapi/src/abstraction/transient/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ use serde::{Deserialize, Serialize};
3939
use std::convert::{TryFrom, TryInto};
4040
use zeroize::Zeroize;
4141

42+
mod key_attestation;
43+
4244
/// Parameters for the kinds of keys supported by the context
4345
#[derive(Debug, Clone, Copy)]
4446
pub enum KeyParams {
@@ -89,6 +91,13 @@ impl KeyMaterial {
8991
}
9092
}
9193

94+
#[derive(Debug, Clone)]
95+
pub struct ObjectWrapper {
96+
pub material: KeyMaterial,
97+
pub params: KeyParams,
98+
pub auth: Option<Auth>,
99+
}
100+
92101
/// Structure offering an abstracted programming experience.
93102
///
94103
/// The `TransientKeyContext` makes use of a root key from which the other, client-controlled

tss-esapi/tests/integration_tests/abstraction_tests/transient_key_context_tests.rs

+84-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// Copyright 2020 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
3-
use std::convert::TryFrom;
3+
use std::convert::{TryFrom, TryInto};
44
use tss_esapi::{
5-
abstraction::transient::{KeyParams, TransientKeyContextBuilder},
5+
abstraction::ek,
6+
abstraction::transient::{KeyParams, ObjectWrapper, TransientKeyContextBuilder},
67
constants::response_code::Tss2ResponseCodeKind,
78
interface_types::{
8-
algorithm::{EccSchemeAlgorithm, HashingAlgorithm, RsaSchemeAlgorithm},
9+
algorithm::{
10+
AsymmetricAlgorithm, EccSchemeAlgorithm, HashingAlgorithm, RsaSchemeAlgorithm,
11+
},
912
ecc::EccCurve,
1013
key_bits::RsaKeyBits,
1114
resource_handles::Hierarchy,
@@ -597,3 +600,81 @@ fn ctx_migration_test() {
597600
panic!("Got wrong type of key from TPM");
598601
}
599602
}
603+
604+
#[test]
605+
fn activate_credential() {
606+
// create a Transient key context, generate a key and
607+
// obtain the Make Credential parameters
608+
let mut ctx = create_ctx();
609+
let params = KeyParams::Ecc {
610+
curve: EccCurve::NistP256,
611+
scheme: EccScheme::create(
612+
EccSchemeAlgorithm::EcDsa,
613+
Some(HashingAlgorithm::Sha256),
614+
None,
615+
)
616+
.expect("Failed to create ecc scheme"),
617+
};
618+
let (material, auth) = ctx.create_key(params, 16).unwrap();
619+
let obj = ObjectWrapper {
620+
material,
621+
auth,
622+
params,
623+
};
624+
let make_cred_params = ctx.get_make_cred_params(obj.clone(), None).unwrap();
625+
626+
drop(ctx);
627+
628+
// create a normal Context and make the credential
629+
let mut basic_ctx = crate::common::create_ctx_with_session();
630+
631+
// the public part of the EK is used, so we retrieve the parameters
632+
let key_pub =
633+
ek::create_ek_public_from_default_template(AsymmetricAlgorithm::Rsa, None).unwrap();
634+
let key_pub = if let Public::Rsa {
635+
object_attributes,
636+
name_hashing_algorithm,
637+
auth_policy,
638+
parameters,
639+
..
640+
} = key_pub
641+
{
642+
Public::Rsa {
643+
object_attributes,
644+
name_hashing_algorithm,
645+
auth_policy,
646+
parameters,
647+
unique: if let PublicKey::Rsa(val) = make_cred_params.attesting_key_pub().clone() {
648+
PublicKeyRsa::try_from(val).unwrap()
649+
} else {
650+
panic!("Wrong public key type");
651+
},
652+
}
653+
} else {
654+
panic!("Wrong Public type");
655+
};
656+
let pub_handle = basic_ctx
657+
.load_external_public(&key_pub, Hierarchy::Owner)
658+
.unwrap();
659+
660+
// Credential to expect back as proof for attestation
661+
let credential = vec![0x53; 16];
662+
663+
let (cred, secret) = basic_ctx
664+
.make_credential(
665+
pub_handle,
666+
credential.clone().try_into().unwrap(),
667+
make_cred_params.name().to_vec().try_into().unwrap(),
668+
)
669+
.unwrap();
670+
671+
drop(basic_ctx);
672+
673+
// Create a new Transient key context and activate the credential
674+
let mut ctx = create_ctx();
675+
let cred_back = ctx
676+
.activate_credential(obj, None, cred.value().to_vec(), secret.value().to_vec())
677+
.unwrap();
678+
679+
assert_eq!(cred_back, credential);
680+
}

0 commit comments

Comments
 (0)