Skip to content
This repository was archived by the owner on Jul 16, 2020. It is now read-only.

Commit a38b88d

Browse files
committed
Established encrypted channel between tenant and enclave using DHKE.
1 parent 6b70407 commit a38b88d

File tree

10 files changed

+796
-162
lines changed

10 files changed

+796
-162
lines changed

intel-sgx/Cargo.lock

+405-98
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

intel-sgx/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ members = ['attestation-enclave', 'attestation-daemon', 'attestation-tenant']
44
[patch.crates-io]
55
dcap-ql = { git = "https://github.com/lkatalin/rust-sgx", branch = "serde-pck", rev = "aa81839e714ad7501e6ef44b2d4e61c8574548d4" }
66
sgx-isa = { git = "https://github.com/lkatalin/rust-sgx", branch = "serde-pck", rev = "aa81839e714ad7501e6ef44b2d4e61c8574548d4" }
7+
mbedtls = { git = "https://github.com/haraldh/rust-mbedtls", branch = "upstream_bindgen" }

intel-sgx/attestation-daemon/Cargo.toml

-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ authors = ["Lily Sturmann <[email protected]>"]
55
edition = "2018"
66

77
[dependencies]
8-
openssl = "0.10.23"
9-
hex = "0.3.1"
108
dcap-ql = "0.2.0"
119
serde_json = "1.0.40"
12-
bufstream = "0.1.4"
1310
serde = { version = "1.0", features = ["derive"] }
1411

1512
[dependencies.sgx-isa]

intel-sgx/attestation-daemon/src/main.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use serde_json::{from_reader, to_writer};
12
use std::error::Error;
2-
use std::io::Write;
3-
use std::net::{TcpListener, TcpStream};
3+
use std::net::{Shutdown, TcpListener, TcpStream};
44

55
const LISTENER_CONN: &'static str = "localhost:1034";
66
const ENCLAVE_CONN: &'static str = "localhost:1032";
@@ -19,24 +19,23 @@ fn main() -> Result<(), Box<dyn Error>> {
1919
// used as the target for the enclave's attestation Report.
2020
let qe_ti = dcap_ql::target_info().expect("Could not retrieve QE target info.");
2121

22-
// Serialize the Target Info onto the stream to the enclave
2322
let mut enclave_stream = TcpStream::connect(ENCLAVE_CONN)?;
24-
serde_json::to_writer(&mut enclave_stream, &qe_ti)?;
25-
enclave_stream.shutdown(std::net::Shutdown::Write)?;
23+
to_writer(&mut enclave_stream, &qe_ti)?;
24+
enclave_stream.shutdown(Shutdown::Write)?;
2625

2726
// The attestation daemon receives the Report back from the attesting enclave.
28-
let report: sgx_isa::Report = serde_json::from_reader(&mut enclave_stream)?;
27+
let report: sgx_isa::Report = from_reader(enclave_stream)?;
2928

3029
// The attestation daemon gets a Quote from the Quoting Enclave for the Report.
3130
// The Quoting Enclave verifies the Report's MAC as a prerequisite for generating
3231
// the Quote. The Quote is signed with the Quoting Enclave's Attestation Key.
3332
let quote = dcap_ql::quote(&report).expect("Could not generate quote.");
3433

35-
// The attestation daemon sends the Quote to the tenant.
3634
let mut tenant_stream = incoming_tenant_stream?;
37-
tenant_stream.write(&quote)?;
35+
to_writer(&mut tenant_stream, &quote)?;
36+
tenant_stream.shutdown(Shutdown::Write)?;
3837

39-
println!("\nQuote successfully generated and sent to tenant...");
38+
println!("\nQuote successfully generated and sent to tenant.");
4039
}
4140
Ok(())
4241
}

intel-sgx/attestation-enclave/Cargo.toml

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ edition = "2018"
66

77
[dependencies]
88
serde_json = "1.0"
9-
bufstream = "0.1.4"
9+
byteorder = "1"
1010

11-
# The sgx-isa crate allows the use of Fortanix's data structures
11+
# The sgx-isa crate allows the use of Fortanix's data structures
1212
# relating to SGX, ex. Report, TargetInfo. The sgxstd feature
1313
# should be enabled when using std::os::fortanix_sgx functionality,
1414
# ex. ENCLU[EGETKEY] and ENCLU[EREPORT]. Serde_support allows for the
@@ -17,3 +17,10 @@ bufstream = "0.1.4"
1717
[dependencies.sgx-isa]
1818
version = "0.3.1"
1919
features = ["serde_support", "sgxstd"]
20+
21+
[dependencies.mbedtls]
22+
git = "https://github.com/haraldh/rust-mbedtls"
23+
branch = "upstream_bindgen"
24+
package = "mbedtls"
25+
default-features = false
26+
features = ["rdrand", "sgx"]

intel-sgx/attestation-enclave/src/main.rs

+161-13
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,182 @@
1+
use byteorder::{ByteOrder, NativeEndian, ReadBytesExt};
2+
use mbedtls::{
3+
cipher::*,
4+
ecp::{EcGroup, EcPoint},
5+
hash::{Md, Type::Sha256},
6+
pk::{EcGroupId, Pk},
7+
rng::{CtrDrbg, Random, Rdseed},
8+
};
9+
use serde_json::{from_reader, to_writer, Deserializer};
110
use sgx_isa::Report;
2-
use std::error::Error;
3-
use std::net::TcpListener;
11+
use std::{error::Error, io::Cursor, net::TcpListener};
412

5-
const LISTENER_ADDR: &'static str = "localhost:1032";
13+
const DAEMON_LISTENER_ADDR: &'static str = "localhost:1032";
14+
const TENANT_LISTENER_ADDR: &'static str = "localhost:1066";
15+
16+
// This copies the enclave key to the report data
17+
fn from_slice(bytes: &[u8]) -> [u8; 64] {
18+
let mut array = [0; 64];
19+
let bytes = &bytes[..array.len()]; // panics if not enough data
20+
array.copy_from_slice(bytes);
21+
array
22+
}
23+
24+
// Creates an AES 256 GCM cipher instance with the symmetric key and initialization vector
25+
// set for each decryption operation.
26+
fn new_aes256gcm_decrypt_cipher(
27+
symm_key: &[u8],
28+
iv: &[u8],
29+
) -> Result<Cipher<Decryption, Authenticated, AdditionalData>, Box<dyn Error>> {
30+
let c = Cipher::<_, Authenticated, _>::new(
31+
raw::CipherId::Aes,
32+
raw::CipherMode::GCM,
33+
(symm_key.len() * 8) as _,
34+
)
35+
.unwrap();
36+
37+
Ok(c.set_key_iv(&symm_key, &iv)?)
38+
}
39+
40+
// Creates an AES 256 GCM cipher instance with the symmetric key and initialization vector
41+
// set for each encryption operation.
42+
// TODO: This is redundant, but I can't return a Cipher<_, Authenticated, AdditionalData>, so I need two separate
43+
// functions. How to fix?
44+
fn new_aes256gcm_encrypt_cipher(
45+
symm_key: &[u8],
46+
iv: &[u8],
47+
) -> Result<Cipher<Encryption, Authenticated, AdditionalData>, Box<dyn Error>> {
48+
let c = Cipher::<_, Authenticated, _>::new(
49+
raw::CipherId::Aes,
50+
raw::CipherMode::GCM,
51+
(symm_key.len() * 8) as _,
52+
)
53+
.unwrap();
54+
55+
Ok(c.set_key_iv(&symm_key, &iv)?)
56+
}
657

758
fn main() -> Result<(), Box<dyn Error>> {
8-
println!("\nListening on {}....\n", LISTENER_ADDR);
59+
println!(
60+
"\nListening on {} and {}....\n",
61+
DAEMON_LISTENER_ADDR, TENANT_LISTENER_ADDR
62+
);
63+
64+
let daemon_streams = TcpListener::bind(DAEMON_LISTENER_ADDR)?;
65+
let tenant_streams = TcpListener::bind(TENANT_LISTENER_ADDR)?;
66+
67+
let curve = EcGroup::new(EcGroupId::SecP256R1)?;
68+
69+
// The enclave generates an EC key pair. The public key will be inserted into the ReportData
70+
// field of the enclave's attestation Report, which will be transmitted to the tenant.
71+
let mut entropy = Rdseed;
72+
let mut rng = CtrDrbg::new(&mut entropy, None)?;
73+
let mut ec_key = Pk::generate_ec(&mut rng, curve.clone())?;
74+
let ec_pub = ec_key.ec_public()?;
975

1076
// The enclave handles each incoming connection from attestation daemon.
11-
for stream in TcpListener::bind(LISTENER_ADDR).unwrap().incoming() {
77+
for stream in daemon_streams.incoming() {
1278
let mut stream = stream?;
1379

1480
// The enclave receives the identity of the Quoting Enclave from the
15-
// attestation daemon, in the form of a (serialized) TargetInfo
81+
// attestation daemon, in the form of a serialized TargetInfo
1682
// structure. The TargetInfo contains the measurement and attribute flags
1783
// of the Quoting Enclave.
18-
let qe_id: sgx_isa::Targetinfo = serde_json::from_reader(&mut stream)?;
84+
let qe_id: sgx_isa::Targetinfo = from_reader(&mut stream)?;
85+
86+
// The enclave's public key will be transmitted to the tenant in the ReportData field
87+
// of the enclave's attesation Report. It must be a &[u8; 64].
88+
// The compressed public key is 33 bytes long and must be extended by 31 bytes.
89+
let mut report_data = ec_pub.to_binary(&curve, true)?;
90+
report_data.extend(&[0u8; 31]);
91+
let report_data = from_slice(&report_data);
1992

2093
// The enclave creates a Report attesting its identity, with the Quoting
2194
// Enclave (whose identity was just received) as the Report's target. The
22-
// blank ReportData field must be passed in as a &[u8; 64].
23-
let report = {
24-
let report_data = [0u8; 64];
25-
Report::for_target(&qe_id, &report_data)
26-
};
95+
// ReportData field contains the enclave's public key.
96+
let report = Report::for_target(&qe_id, &report_data);
2797

2898
// The enclave sends its attestation Report back to the attestation daemon.
29-
serde_json::to_writer(&mut stream, &report)?;
99+
to_writer(&mut stream, &report)?;
30100

31101
println!("Successfully sent report to daemon.");
102+
103+
break;
104+
}
105+
106+
// The enclave handles each incoming connection from the tenant. These channels between the tenant
107+
// and enclave are established after attestation is verified and all data exchanged between the tenant
108+
// and enclave after public keys are exchanged is encrypted with a shared symmetric key.
109+
for stream in tenant_streams.incoming() {
110+
let mut stream = stream?;
111+
112+
// The enclave receives and deserializes tenant pub key, ivs and tags for ciphertext values, ciphertext.
113+
let deserializer = Deserializer::from_reader(stream.try_clone().unwrap());
114+
let mut iterator = deserializer.into_iter::<Vec<u8>>();
115+
116+
let tenant_key = iterator.next().unwrap()?;
117+
let ad = iterator.next().unwrap()?;
118+
let iv1 = iterator.next().unwrap()?;
119+
let iv2 = iterator.next().unwrap()?;
120+
let tag1 = iterator.next().unwrap()?;
121+
let tag2 = iterator.next().unwrap()?;
122+
let ciphertext1 = iterator.next().unwrap()?;
123+
let ciphertext2 = iterator.next().unwrap()?;
124+
125+
// The enclave generates a shared secret with the tenant. A SHA256 hash of this shared secret
126+
// is used as the symmetric key for encryption and decryption of data.
127+
let tenant_pubkey_ecpoint = EcPoint::from_binary(&curve, &tenant_key)?;
128+
let tenant_pubkey = Pk::public_from_ec_components(curve.clone(), tenant_pubkey_ecpoint)?;
129+
130+
// TODO: Should this use the same rng as before or create a new one?
131+
let mut shared_secret = [0u8; 32]; // 256 / 8
132+
ec_key.agree(&tenant_pubkey, &mut shared_secret, &mut rng)?;
133+
let mut symm_key = [0u8; 32];
134+
Md::hash(Sha256, &shared_secret, &mut symm_key)?;
135+
136+
// These cipher instances are used for decryption operations and one encryption operation.
137+
// TODO: Can the same cipher instance be used for these? Cipher doesn't implement clone().
138+
let decrypt_cipher_1 = new_aes256gcm_decrypt_cipher(&symm_key, &iv1)?;
139+
let decrypt_cipher_2 = new_aes256gcm_decrypt_cipher(&symm_key, &iv2)?;
140+
141+
let mut entropy = Rdseed;
142+
let mut rng = CtrDrbg::new(&mut entropy, None)?;
143+
let mut iv = [0u8; 16];
144+
rng.random(&mut iv)?;
145+
let encrypt_cipher = new_aes256gcm_encrypt_cipher(&symm_key, &iv)?;
146+
147+
// The values received from the tenant are decrypted.
148+
let mut plaintext1 = [0u8; 32];
149+
let mut plaintext2 = [0u8; 32];
150+
let _ = decrypt_cipher_1.decrypt_auth(&ad, &ciphertext1, &mut plaintext1, &tag1)?;
151+
let _ = decrypt_cipher_2.decrypt_auth(&ad, &ciphertext2, &mut plaintext2, &tag2)?;
152+
153+
// The values received from the tenant are converted back to 32-bit unsigned ints.
154+
let num1 = Cursor::new(plaintext1).read_u32::<NativeEndian>()?;
155+
let num2 = Cursor::new(plaintext2).read_u32::<NativeEndian>()?;
156+
157+
// The sum of the two plaintext values is calculated.
158+
let sum: u32 = num1 + num2;
159+
println!("\n{} + {} = {}", num1, num2, sum);
160+
161+
// The sum is converted from u32 to bytes to serve as input for the encryption function.
162+
// The extra 5th byte is in case of overflow.
163+
let mut sum_as_bytes = [0u8; 5];
164+
NativeEndian::write_u32(&mut sum_as_bytes, sum);
165+
166+
// The sum is encrypted.
167+
let mut ciphersum = [0u8; 5];
168+
let mut tag = [0u8; 16];
169+
let _ = encrypt_cipher.encrypt_auth(&ad, &sum_as_bytes, &mut ciphersum, &mut tag)?;
170+
171+
// The tag, iv, additional data, and encrypted sum are sent back to the tenant.
172+
to_writer(&mut stream, &tag)?;
173+
to_writer(&mut stream, &iv)?;
174+
to_writer(&mut stream, &ad)?;
175+
to_writer(&mut stream, &ciphersum)?;
176+
177+
// TODO: This line exits the program after one run. Otherwise, it appears as though the tenant can be run
178+
// again, but instead the program just hangs the second time. Why?
179+
break;
32180
}
33181

34182
Ok(())

intel-sgx/attestation-tenant/Cargo.toml

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ edition = "2018"
77
[dependencies]
88
sgx-isa = { version = "0.3.0" }
99
dcap-ql = "0.2.0"
10+
failure = "0.1"
1011
openssl = "0.10.23"
11-
hex = "0.3.1"
12-
failure = "0.1.5"
1312
bufstream = "0.1.4"
13+
serde_json = "1.0"
14+
byteorder = "1"
15+
16+
[dependencies.mbedtls]
17+
git = "https://github.com/haraldh/rust-mbedtls"
18+
branch = "upstream_bindgen"
19+
package = "mbedtls"
20+
default-features = false
21+
features = ["rdrand", "sgx"]

intel-sgx/attestation-tenant/src/cert_chain.rs

-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ impl CertChain {
5454
panic!("Invalid issuer relationship in certificate chain.");
5555
}
5656
}
57-
println!("Issuer relationships in PCK cert chain are valid...");
5857
Ok(())
5958
}
6059

@@ -85,7 +84,6 @@ impl CertChain {
8584
// verified.
8685
context.init(&store, &self.leaf, &chain, |c| c.verify_cert())?;
8786

88-
println!("Signatures on certificate chain are valid...");
8987
Ok(())
9088
}
9189
}

0 commit comments

Comments
 (0)