1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
/*
* This example demonstrates how to create an HMAC key that can be reloaded.
*/
use tss_esapi::{
attributes::ObjectAttributesBuilder,
interface_types::{
algorithm::{HashingAlgorithm, PublicAlgorithm},
resource_handles::Hierarchy,
},
structures::{
Digest, KeyedHashScheme, MaxBuffer, PublicBuilder, PublicKeyedHashParameters,
SymmetricCipherParameters, SymmetricDefinitionObject,
},
Context, TctiNameConf,
};
use std::convert::TryFrom;
fn main() {
// Create a new TPM context. This reads from the environment variable `TPM2TOOLS_TCTI` or `TCTI`
//
// It's recommended you use `TCTI=device:/dev/tpmrm0` for the linux kernel
// tpm resource manager.
let mut context = Context::new(
TctiNameConf::from_environment_variable()
.expect("Failed to get TCTI / TPM2TOOLS_TCTI from environment. Try `export TCTI=device:/dev/tpmrm0`"),
)
.expect("Failed to create Context");
// Create the primary key. A primary key is the "root" of a collection of objects.
// These other objects are encrypted by the primary key allowing them to persist
// over a reboot and reloads.
//
// A primary key is derived from a seed, and provided that the same inputs are given
// the same primary key will be derived in the tpm. This means that you do not need
// to store or save the details of this key - only the parameters of how it was created.
let object_attributes = ObjectAttributesBuilder::new()
// Indicate the key can only exist within this tpm and can not be exported.
.with_fixed_tpm(true)
// The primary key and it's descendent keys can't be moved to other primary
// keys.
.with_fixed_parent(true)
// The primary key will persist over suspend and resume of the system.
.with_st_clear(false)
// The primary key was generated entirely inside the TPM - only this TPM
// knows it's content.
.with_sensitive_data_origin(true)
// This key requires "authentication" to the TPM to access - this can be
// an HMAC or password session. HMAC sessions are used by default with
// the "execute_with_nullauth_session" function.
.with_user_with_auth(true)
// This key has the ability to decrypt
.with_decrypt(true)
// This key may only be used to encrypt or sign objects that are within
// the TPM - it can not encrypt or sign external data.
.with_restricted(true)
.build()
.expect("Failed to build object attributes");
let primary_pub = PublicBuilder::new()
// This key is a symmetric key.
.with_public_algorithm(PublicAlgorithm::SymCipher)
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
.with_object_attributes(object_attributes)
.with_symmetric_cipher_parameters(SymmetricCipherParameters::new(
SymmetricDefinitionObject::AES_128_CFB,
))
.with_symmetric_cipher_unique_identifier(Digest::default())
.build()
.unwrap();
let primary = context
.execute_with_nullauth_session(|ctx| {
// Create the key under the "owner" hierarchy. Other hierarchies are platform
// which is for boot services, null which is ephemeral and resets after a reboot,
// and endorsement which allows key certification by the TPM manufacturer.
ctx.create_primary(Hierarchy::Owner, primary_pub, None, None, None, None)
})
.unwrap();
// Create the HMAC key. This key exists under the primary key in it's hierarchy
// and can only be used if the same primary key is recreated from the parameters
// defined above.
let object_attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_st_clear(false)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
// The key is used only for signing.
.with_sign_encrypt(true)
.build()
.expect("Failed to build object attributes");
let key_pub = PublicBuilder::new()
// This key is a HMAC key
.with_public_algorithm(PublicAlgorithm::KeyedHash)
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
.with_object_attributes(object_attributes)
.with_keyed_hash_parameters(PublicKeyedHashParameters::new(
KeyedHashScheme::HMAC_SHA_256,
))
.with_keyed_hash_unique_identifier(Digest::default())
.build()
.unwrap();
let (enc_private, public) = context
.execute_with_nullauth_session(|ctx| {
// Create the HMAC key given our primary key as it's parent. This returns the private
// and public portions of the key. It's *important* to note that the private component
// is *encrypted* by a key associated with the primary key. It is not plaintext or
// leaked in this step.
ctx.create(primary.key_handle, key_pub, None, None, None, None)
.map(|key| (key.out_private, key.out_public))
})
.unwrap();
// Once the key is created, we have it's parameters in the private and public values.
// We now need to load it into the tpm so that it can be used.
//
// The enc_private and public values can be serialised and persisted - that way they can
// be reloaded for future use.
let input_data = MaxBuffer::try_from("TPMs are cool.".as_bytes().to_vec())
.expect("Failed to create buffer for input data.");
let hmac1 = context
.execute_with_nullauth_session(|ctx| {
// Load the HMAC key into the tpm context.
let hmac_key = ctx
.load(primary.key_handle, enc_private.clone(), public.clone())
.unwrap();
// Perform the HMAC.
let r = ctx.hmac(
hmac_key.into(),
input_data.clone(),
HashingAlgorithm::Sha256,
);
// Unload the key from the context.
ctx.flush_context(hmac_key.into()).unwrap();
r
})
.unwrap();
// Reload the key and perform the same hmac.
let hmac2 = context
.execute_with_nullauth_session(|ctx| {
let hmac_key = ctx
.load(primary.key_handle, enc_private.clone(), public.clone())
.unwrap();
let r = ctx.hmac(
hmac_key.into(),
input_data.clone(),
HashingAlgorithm::Sha256,
);
ctx.flush_context(hmac_key.into()).unwrap();
r
})
.unwrap();
println!("hmac1 = {:?}", hmac1);
println!("hmac2 = {:?}", hmac2);
// They are the same!
assert_eq!(hmac1, hmac2);
}