Skip to content

Commit aa0e1ea

Browse files
authored
Merge pull request #536 from nicolaspernoud/main
File encryption and decryption example ; dev container with c library build and tpm emulator
2 parents d68a13a + fa9914d commit aa0e1ea

File tree

6 files changed

+303
-0
lines changed

6 files changed

+303
-0
lines changed

.devcontainer/Dockerfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
FROM rust:latest
2+
3+
RUN apt -y update && apt -y install \
4+
autoconf-archive \
5+
libcmocka0 \
6+
libcmocka-dev \
7+
procps \
8+
iproute2 \
9+
build-essential \
10+
git \
11+
pkg-config \
12+
gcc \
13+
libtool \
14+
automake \
15+
libssl-dev \
16+
uthash-dev \
17+
autoconf \
18+
doxygen \
19+
libjson-c-dev \
20+
libini-config-dev \
21+
libcurl4-openssl-dev \
22+
uuid-dev \
23+
libltdl-dev \
24+
libusb-1.0-0-dev \
25+
libftdi-dev \
26+
clang
27+
28+
WORKDIR /build
29+
ADD . /build
30+
31+
# TPM Lib
32+
RUN git clone --depth 1 --branch 4.1.3 https://github.com/tpm2-software/tpm2-tss.git && \
33+
cd tpm2-tss && \
34+
./bootstrap && \
35+
./configure --prefix=/usr && \
36+
make -j5 && \
37+
make install

.devcontainer/devcontainer.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "Development environment",
3+
"dockerComposeFile": "docker-compose.yml",
4+
"service": "app",
5+
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
6+
"customizations": {
7+
"vscode": {
8+
"extensions": [
9+
"1YiB.rust-bundle",
10+
"fill-labs.dependi"
11+
],
12+
"settings": {
13+
"remote.autoForwardPorts": false,
14+
"editor.formatOnSave": true
15+
}
16+
}
17+
}
18+
}

.devcontainer/docker-compose.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
services:
2+
app:
3+
hostname: app
4+
build:
5+
context: .
6+
dockerfile: Dockerfile
7+
volumes:
8+
- ../..:/workspaces:cached
9+
command: sleep infinity
10+
network_mode: host
11+
environment:
12+
TPM2TOOLS_TCTI: mssim:host=localhost,port=2321
13+
deploy:
14+
resources:
15+
limits:
16+
cpus: '4.0'
17+
memory: 16G
18+
19+
tpm:
20+
image: tpmdev/tpm2-runtime
21+
network_mode: host
22+
restart: unless-stopped
23+
environment:
24+
TPM2TOOLS_TCTI: mssim:host=localhost,port=2321
25+
command: /bin/bash -c "tpm_server >/dev/null & sleep 1; tpm2_startup -c; sleep infinity"
26+

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ jobs:
1212
- uses: actions/checkout@v2
1313
- name: Check spelling
1414
uses: codespell-project/actions-codespell@v1
15+
with:
16+
exclude_file: tss-esapi/examples/symmetric_file_encrypt_decrypt_example.txt
1517

1618
formatting:
1719
name: Check formatting
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use core::str;
2+
use std::convert::TryFrom;
3+
use std::fs;
4+
use tss_esapi::{
5+
attributes::ObjectAttributesBuilder,
6+
interface_types::{
7+
algorithm::{HashingAlgorithm, PublicAlgorithm, SymmetricMode},
8+
reserved_handles::Hierarchy,
9+
},
10+
structures::{
11+
CreatePrimaryKeyResult, Digest, InitialValue, MaxBuffer, PublicBuilder,
12+
SymmetricCipherParameters, SymmetricDefinitionObject,
13+
},
14+
Context, TctiNameConf,
15+
};
16+
17+
fn main() {
18+
// Create a new TPM context. This reads from the environment variable `TPM2TOOLS_TCTI` or `TCTI`
19+
//
20+
// It's recommended you use `TCTI=device:/dev/tpmrm0` for the linux kernel
21+
// tpm resource manager.
22+
let mut context = Context::new(
23+
TctiNameConf::from_environment_variable()
24+
.expect("Failed to get TCTI / TPM2TOOLS_TCTI from environment. Try `export TCTI=device:/dev/tpmrm0`"),
25+
)
26+
.expect("Failed to create Context");
27+
28+
// This example won't go over the process to create a new parent. For more detail see `examples/hmac.rs`.
29+
let primary = create_primary(&mut context);
30+
31+
// Create the AES key. This key exists under the primary key in it's hierarchy
32+
// and can only be used if the same primary key is recreated from the parameters
33+
// defined above.
34+
let object_attributes = ObjectAttributesBuilder::new()
35+
.with_fixed_tpm(true)
36+
.with_fixed_parent(true)
37+
.with_st_clear(false)
38+
.with_sensitive_data_origin(true)
39+
.with_user_with_auth(true)
40+
.with_sign_encrypt(true)
41+
.with_decrypt(true)
42+
.build()
43+
.expect("Failed to build object attributes");
44+
45+
let key_pub = PublicBuilder::new()
46+
// This key is an AES key
47+
.with_public_algorithm(PublicAlgorithm::SymCipher)
48+
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
49+
.with_symmetric_cipher_parameters(SymmetricCipherParameters::new(
50+
SymmetricDefinitionObject::AES_128_CFB,
51+
))
52+
.with_object_attributes(object_attributes)
53+
.with_symmetric_cipher_unique_identifier(Digest::default())
54+
.build()
55+
.unwrap();
56+
57+
let (private, public) = context
58+
.execute_with_nullauth_session(|ctx| {
59+
// Create the AES key given our primary key as it's parent. This returns the private
60+
// and public portions of the key. It's *important* to note that the private component
61+
// is *encrypted* by a key associated with the primary key. It is not plaintext or
62+
// leaked in this step.
63+
ctx.create(primary.key_handle, key_pub, None, None, None, None)
64+
.map(|key| (key.out_private, key.out_public))
65+
})
66+
.unwrap();
67+
68+
// Once the key is created, we have it's parameters in the private and public values.
69+
// We now need to load it into the tpm so that it can be used.
70+
//
71+
// The enc_private and public values can be serialised and persisted - that way they can
72+
// be reloaded for future use.
73+
74+
// We load the data from a file system file, it can be somewhat large (like a certificate), larger than MaxBuffer::MAX_SIZE
75+
let initial_data = fs::read("tss-esapi/examples/symmetric_file_encrypt_decrypt_example.txt")
76+
.expect("could not open data file");
77+
78+
// We create an initialisation vector, since it is needed for decryption, it should be persisted in a real world use case
79+
let iv = context
80+
.execute_with_nullauth_session(|ctx| {
81+
InitialValue::from_bytes(
82+
ctx.get_random(16)
83+
.expect("could not get random bytes for initialisation vector")
84+
.as_bytes(),
85+
)
86+
})
87+
.expect("could not create iv from random bytes");
88+
89+
// We encrypt the data
90+
let encrypted_data = context
91+
.execute_with_nullauth_session(|ctx| {
92+
let mut encrypted_data = Vec::new();
93+
let handle = ctx
94+
.load(primary.key_handle, private.clone(), public.clone())
95+
.expect("could not load child key");
96+
97+
let mut chunk_iv = iv.clone();
98+
99+
// This file is larger than the MaxBuffer::MAX_SIZE, so we need to chunk it
100+
// The iv must be different for every chunk, the encrypt_decrypt_2 function conveniently provide a new one at each iteration
101+
for chunk in initial_data.chunks(MaxBuffer::MAX_SIZE) {
102+
let data = MaxBuffer::try_from(Vec::from(chunk))
103+
.expect("failed to create data from file buffer chunk");
104+
let (enc_data, chunk_iv_out) = ctx.encrypt_decrypt_2(
105+
handle, // Handle to a symmetric key
106+
false, // false, indicates that the data should be encrypted
107+
SymmetricMode::Cfb, // The symmetric mode of the encryption
108+
data, // The data that is to be encrypted
109+
chunk_iv, // Initial value needed by the algorithm
110+
)?;
111+
chunk_iv = chunk_iv_out;
112+
encrypted_data.push(enc_data);
113+
}
114+
Ok::<Vec<MaxBuffer>, tss_esapi::Error>(encrypted_data)
115+
})
116+
.expect("Call to encrypt_decrypt_2 failed when encrypting data");
117+
let encrypted_data = encrypted_data
118+
.iter()
119+
.map(|e| e.as_bytes())
120+
.collect::<Vec<_>>()
121+
.concat();
122+
123+
// Decrypting is exactly the opposite, with the same first iv
124+
let decrypted_data = context
125+
.execute_with_nullauth_session(|ctx| {
126+
let mut decrypted_data = Vec::new();
127+
let handle = ctx
128+
.load(primary.key_handle, private.clone(), public.clone())
129+
.expect("could not load child key");
130+
131+
let mut chunk_iv = iv;
132+
133+
for chunk in encrypted_data.chunks(MaxBuffer::MAX_SIZE) {
134+
let data = MaxBuffer::try_from(Vec::from(chunk))
135+
.expect("failed to create data from encrypted data chunk");
136+
let (enc_data, chunk_iv_out) = ctx.encrypt_decrypt_2(
137+
handle, // Handle to a symmetric key
138+
true, // true, indicates that the data should be decrypted
139+
SymmetricMode::Cfb, // The symmetric mode of the encryption
140+
data, // The data that is to be encrypted
141+
chunk_iv, // Initial value needed by the algorithm
142+
)?;
143+
chunk_iv = chunk_iv_out;
144+
decrypted_data.push(enc_data);
145+
} //
146+
Ok::<Vec<MaxBuffer>, tss_esapi::Error>(decrypted_data)
147+
})
148+
.expect("Call to encrypt_decrypt_2 failed when encrypting data");
149+
let decrypted_data = decrypted_data
150+
.iter()
151+
.map(|e| e.as_bytes())
152+
.collect::<Vec<_>>()
153+
.concat();
154+
155+
println!(
156+
"=== Initial data ===\n\n{}\n\n\n\n",
157+
str::from_utf8(&initial_data).unwrap()
158+
);
159+
print!("");
160+
println!(
161+
"=== Decrypted data ===\n\n{}",
162+
str::from_utf8(&decrypted_data).unwrap()
163+
);
164+
// They are the same!
165+
assert_eq!(initial_data, decrypted_data);
166+
}
167+
168+
fn create_primary(context: &mut Context) -> CreatePrimaryKeyResult {
169+
// Create the primary key. A primary key is the "root" of a collection of objects.
170+
// These other objects are encrypted by the primary key allowing them to persist
171+
// over a reboot and reloads.
172+
//
173+
// A primary key is derived from a seed, and provided that the same inputs are given
174+
// the same primary key will be derived in the tpm. This means that you do not need
175+
// to store or save the details of this key - only the parameters of how it was created.
176+
let object_attributes = ObjectAttributesBuilder::new()
177+
// Indicate the key can only exist within this tpm and can not be exported.
178+
.with_fixed_tpm(true)
179+
// The primary key and it's descendent keys can't be moved to other primary
180+
// keys.
181+
.with_fixed_parent(true)
182+
// The primary key will persist over suspend and resume of the system.
183+
.with_st_clear(false)
184+
// The primary key was generated entirely inside the TPM - only this TPM
185+
// knows it's content.
186+
.with_sensitive_data_origin(true)
187+
// This key requires "authentication" to the TPM to access - this can be
188+
// an HMAC or password session. HMAC sessions are used by default with
189+
// the "execute_with_nullauth_session" function.
190+
.with_user_with_auth(true)
191+
// This key has the ability to decrypt
192+
.with_decrypt(true)
193+
// This key may only be used to encrypt or sign objects that are within
194+
// the TPM - it can not encrypt or sign external data.
195+
.with_restricted(true)
196+
.build()
197+
.expect("Failed to build object attributes");
198+
199+
let primary_pub = PublicBuilder::new()
200+
// This key is a symmetric key.
201+
.with_public_algorithm(PublicAlgorithm::SymCipher)
202+
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
203+
.with_object_attributes(object_attributes)
204+
.with_symmetric_cipher_parameters(SymmetricCipherParameters::new(
205+
SymmetricDefinitionObject::AES_128_CFB,
206+
))
207+
.with_symmetric_cipher_unique_identifier(Digest::default())
208+
.build()
209+
.unwrap();
210+
211+
context
212+
.execute_with_nullauth_session(|ctx| {
213+
// Create the key under the "owner" hierarchy. Other hierarchies are platform
214+
// which is for boot services, null which is ephemeral and resets after a reboot,
215+
// and endorsement which allows key certification by the TPM manufacturer.
216+
ctx.create_primary(Hierarchy::Owner, primary_pub, None, None, None, None)
217+
})
218+
.unwrap()
219+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lorem ipsum dolor sit amet consectetur adipiscing elit in lacinia egestas, eget cubilia laoreet vel iaculis urna gravida elementum himenaeos. Mattis mus nullam fames malesuada scelerisque quisque nec enim nisl inceptos, vehicula dapibus cursus nunc convallis diam lacinia proin. Accumsan mattis felis varius ligula, sagittis dignissim potenti rhoncus lectus, purus sed arcu. Arcu augue eleifend litora ultrices senectus laoreet fusce, suspendisse pharetra pulvinar risus penatibus odio eu, nam nulla platea interdum erat aptent. Dis euismod tincidunt tortor luctus morbi netus sagittis consequat, sollicitudin aptent volutpat fermentum condimentum ridiculus risus ullamcorper turpis, class penatibus in vel diam pretium est. Varius tempus fames habitasse et nibh turpis nam lobortis faucibus, eget dapibus odio nunc praesent nullam interdum. Rutrum mollis iaculis netus elementum potenti tristique phasellus, porta sociis integer tortor sodales neque, quis vestibulum varius morbi viverra himenaeos. Habitasse inceptos cursus elementum mi dictumst blandit nam, habitant montes quis praesent fames potenti sem, consequat tellus sollicitudin vivamus mollis dui. Nostra felis ullamcorper sociosqu integer penatibus luctus lacus bibendum suspendisse dis, eu a ridiculus sapien litora hendrerit blandit fusce fringilla viverra, suscipit habitasse odio aliquet mus congue quam conubia himenaeos. Sem porttitor vehicula consequat pretium fames ullamcorper posuere rutrum, vulputate cursus phasellus magnis accumsan diam. Iaculis hendrerit sodales sagittis in maecenas vel tristique, ultrices augue malesuada accumsan etiam pulvinar, ligula mauris convallis turpis natoque imperdiet pellentesque, diam torquent tempus commodo sapien. Consequat habitasse porta ut nascetur augue urna, eu tortor placerat eleifend proin, egestas natoque tincidunt feugiat morbi. Lacus faucibus viverra cubilia nascetur dictum a potenti commodo facilisi, fames mauris posuere erat mus senectus est placerat ornare mollis, vitae sociosqu suscipit luctus montes sem maecenas vel. Lacinia sollicitudin lobortis duis nisl posuere gravida tristique tempus volutpat netus class parturient arcu, velit torquent in cursus etiam ultricies eu sem lacus habitasse rutrum morbi. Molestie facilisis sodales lobortis et ultrices cum nam curabitur, odio bibendum nec platea placerat natoque tempor, habitant morbi montes tristique nullam massa cursus. Natoque quam pharetra fames iaculis rutrum leo metus dis dictum, sollicitudin per tempus senectus cum magna nisl scelerisque accumsan, justo ligula massa mattis primis est nullam congue. Pulvinar arcu tempor fermentum diam nisl ridiculus molestie accumsan nostra volutpat fringilla placerat, venenatis dapibus penatibus aliquam lacinia quam risus sagittis neque velit ac. Turpis inceptos est morbi primis vulputate euismod in vivamus iaculis, suscipit platea curae rhoncus montes aliquet sapien nostra, quisque vitae dignissim rutrum id gravida feugiat pretium. Integer arcu litora facilisis hac accumsan nisi suspendisse, aenean suscipit vel curabitur nostra. Molestie conubia venenatis nullam placerat mollis vulputate egestas, sodales tellus justo commodo metus suspendisse turpis, volutpat dignissim laoreet ante est sociis. Mauris rutrum suspendisse lacinia massa lectus natoque arcu nostra est sem mollis rhoncus per, fermentum curae tristique eros suscipit justo fames nec ligula himenaeos velit. Euismod felis platea velit cum rhoncus aptent, mollis aliquam penatibus non nisl per pellentesque, eget tortor magnis at varius. Ut magnis suspendisse turpis montes sociis aptent gravida arcu, mus vehicula eu facilisi molestie hendrerit hac dictumst, quam id ultrices in parturient scelerisque dignissim. Elementum primis porta nibh ullamcorper placerat ad dapibus mauris velit, nascetur cursus senectus consequat platea sagittis inceptos orci, taciti fusce parturient feugiat sapien imperdiet leo vitae. Nam tellus suscipit viverra lacus ac maecenas molestie quisque, ut natoque vel feugiat magna nulla bibendum, mi purus euismod hac phasellus ad arcu. Scelerisque facilisi a eros enim et purus, malesuada pretium mauris interdum natoque, ut dignissim magnis senectus ac. Facilisis tempus viverra penatibus mi rhoncus ut montes semper duis, posuere laoreet ultrices luctus molestie cras ac neque aptent, metus eu ridiculus urna nisl arcu vehicula fermentum. Aenean class eget sociis ridiculus accumsan, porta fringilla facilisi netus cum, eu sed imperdiet vel. Parturient odio proin integer lobortis primis lectus magnis sapien dictumst per sociis, condimentum euismod eros cras nascetur vel facilisi leo urna. Natoque fusce eleifend integer porttitor felis fermentum lacus iaculis dignissim, erat sapien malesuada primis pharetra etiam potenti neque, facilisi luctus tempor accumsan faucibus rutrum tristique dis. Nulla egestas nisl risus condimentum scelerisque diam sed mollis cursus, netus libero facilisi proin semper phasellus varius nullam nam parturient, donec est sociosqu acc.

0 commit comments

Comments
 (0)