use tss_esapi::{
Context, TctiNameConf,
attributes::ObjectAttributesBuilder,
interface_types::{
algorithm::{HashingAlgorithm, PublicAlgorithm, SymmetricMode},
key_bits::AesKeyBits,
reserved_handles::Hierarchy,
},
structures::{
CreatePrimaryKeyResult, Digest, InitialValue, MaxBuffer, PublicBuilder,
SymmetricCipherParameters, SymmetricDefinitionObject,
},
};
use std::convert::TryFrom;
fn main() {
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");
let primary = create_primary(&mut context);
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)
.with_sign_encrypt(true)
.with_decrypt(true)
.build()
.expect("Failed to build object attributes");
let aes_params = SymmetricCipherParameters::new(SymmetricDefinitionObject::Aes {
key_bits: AesKeyBits::Aes128,
mode: SymmetricMode::Cbc,
});
let key_pub = PublicBuilder::new()
.with_public_algorithm(PublicAlgorithm::SymCipher)
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
.with_object_attributes(object_attributes)
.with_symmetric_cipher_parameters(aes_params)
.with_symmetric_cipher_unique_identifier(Digest::default())
.build()
.unwrap();
let (enc_private, public) = context
.execute_with_nullauth_session(|ctx| {
ctx.create(primary.key_handle, key_pub, None, None, None, None)
.map(|key| (key.out_private, key.out_public))
})
.unwrap();
let data_to_encrypt = "TPMs are super cool, you should use them!"
.as_bytes()
.to_vec();
eprintln!("{:?}", data_to_encrypt.len());
const AES_BLOCK_SIZE: usize = 16;
let need_k_bytes = AES_BLOCK_SIZE - (data_to_encrypt.len() % AES_BLOCK_SIZE);
let need_k_bytes = if need_k_bytes == 0 {
AES_BLOCK_SIZE
} else {
need_k_bytes
};
let new_len = data_to_encrypt.len() + need_k_bytes;
let mut padded_data_to_encrypt = data_to_encrypt.to_vec();
padded_data_to_encrypt.resize(new_len, need_k_bytes as u8);
let padded_data_to_encrypt = MaxBuffer::try_from(padded_data_to_encrypt).unwrap();
assert_ne!(
data_to_encrypt.as_slice(),
padded_data_to_encrypt.as_slice()
);
let initial_value = context
.execute_with_nullauth_session(|ctx| {
ctx.get_random(InitialValue::MAX_SIZE)
.and_then(|random| InitialValue::try_from(random.to_vec()))
})
.unwrap();
let (encrypted_data, _initial_value) = context
.execute_with_nullauth_session(|ctx| {
let aes_key = ctx
.load(primary.key_handle, enc_private.clone(), public.clone())
.unwrap();
let decrypt = false;
ctx.encrypt_decrypt_2(
aes_key,
decrypt,
SymmetricMode::Cbc,
padded_data_to_encrypt.clone(),
initial_value.clone(),
)
})
.unwrap();
println!("encrypted_data = {:?}", encrypted_data);
assert_ne!(encrypted_data.as_slice(), padded_data_to_encrypt.as_slice());
let (decrypted_data, _initial_value) = context
.execute_with_nullauth_session(|ctx| {
let aes_key = ctx
.load(primary.key_handle, enc_private.clone(), public.clone())
.unwrap();
let decrypt = true;
ctx.encrypt_decrypt_2(
aes_key,
decrypt,
SymmetricMode::Cbc,
encrypted_data.clone(),
initial_value,
)
})
.unwrap();
if decrypted_data.is_empty() {
panic!("Should not be empty");
}
let last_byte = decrypted_data.len() - 1;
let k_byte = decrypted_data[last_byte];
if k_byte as usize > AES_BLOCK_SIZE {
panic!("Invalid pad byte, exceeds AES_BLOCK_SIZE");
}
for i in 0..k_byte {
if decrypted_data[last_byte - i as usize] != k_byte {
panic!("Invalid pad byte, is not equal to k_byte");
}
}
let truncate_to = decrypted_data.len().checked_sub(k_byte as usize).unwrap();
let mut decrypted_data = decrypted_data.to_vec();
decrypted_data.truncate(truncate_to);
println!("data_to_encrypt = {:?}", data_to_encrypt);
println!("decrypted_data = {:?}", decrypted_data);
assert_eq!(data_to_encrypt, decrypted_data);
}
fn create_primary(context: &mut Context) -> CreatePrimaryKeyResult {
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)
.with_decrypt(true)
.with_restricted(true)
.build()
.expect("Failed to build object attributes");
let primary_pub = PublicBuilder::new()
.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();
context
.execute_with_nullauth_session(|ctx| {
ctx.create_primary(Hierarchy::Owner, primary_pub, None, None, None, None)
})
.unwrap()
}