Using Rust's std
Most of Rust's standard library is available: collections and data structures, synchronization primitives, threading, streaming network connections, etc. Some parts of the standard library work slightly differently than you're used to and other parts don't work at all. This page documents all the differences.
Different functionality
The following primitives work slightly differently than a “standard” operating environment, but should still work for most purposes.
std::env::vars
and friends
Environment variables can be read, written, and removed as normal. Note that,
for security, enclave applications start with an empty environment. There is no
way to inject environment variables from userspace. You can of course set
whatever variables you want at the start of your program in fn main
:
1
2
3
4
fn main() {
std::env::set_var("RUST_BACKTRACE", "1");
panic!("Expecting a backtrace");
}
std::is_x86_feature_detected
The is_x86_feature_detected!
macro is used to detect available CPU features.
Since the CPUID instruction is not supported in SGX, this returns false
for
any features not enabled at compile-time. Improvements are planned in GitHub
issue #26.
std::time
Timekeeping in secure enclaves is an unsolved problem. The following functions use the current time as reported by the Operating System, which may or may not be the actual current time:
Instant::now
Instant::elapsed
SystemTime::now
SystemTime::elapsed
Do not use the time obtained from these functions as the sole time source for making security decisions such as credential expiry.
std::os::fortanix_sgx
Just like other platforms, the Fortanix SGX target has its own module in std
with platform-specific functionality. View the fortanix_sgx
API
documentation.
Stream networking
Stream networking using TCP as exposed by the std::net
module is supported.
An enclave can't trust the OS or the rest of the infrastructure to do things in networking correctly. Enclave applications should use cryptographic protocols (such as TLS) with authentication of remote parties (such as remote attestation or TLS certificates) to ensure correct operation of the network infrastructure.
Hostname resolution is not done in the enclave, but by userspace. Therefore,
using std::net::ToSocketAddrs
for hostname resolution is not supported. You
must pass the hostname directly to TcpStream::connect
. For example, this
won't work:
1
2
3
4
fn connect_sdkms() -> io::Result<TcpStream> {
let addrs = "sdkms.fortanix.com:443".to_socket_addrs()?.collect();
TcpStream::connect(Vec::as_slice(&addrs))
}
But this will:
1
2
3
fn connect_sdkms() -> IoResult<TcpStream> {
TcpStream::connect("sdkms.fortanix.com:443")
}
Advanced socket options
Because the enclave can't rely on any particular behavior from the operating
system, advanced socket options are not supported. The following functions in
std::net
do nothing but don't return an error:
TcpStream::shutdown
TcpStream::set_nodelay
TcpStream::set_ttl
TcpStream::set_nonblocking
TcpListener::set_ttl
TcpListener::set_only_v6
TcpListener::set_nonblocking
The following functions in std::net
return a default value:
TcpStream::nodelay
TcpStream::ttl
TcpListener::ttl
TcpListener::only_v6
Restricted functionality
Some primitives don't work when compiling for x86_64-fortanix-unknown-sgx
.
The following sections describe which behavior you get when these
primitives are called and what alternatives exist.
Timeouts
As mentioned above, timekeeping in secure enclaves is problematic, and the OS can trivially deny service to an enclave. Therefore, the utility of timeout functions in enclave applications is unclear. Currently, timeouts are not supported at all. Improvements are planned in GitHub issue #31.
The following functions panic unconditionally:
thread::sleep
thread::sleep_ms
thread::park_timeout
thread::park_timeout_ms
sync::Condvar::wait_timeout
Instead of blocking if the channel is empty,
sync::mpsc::Receiver::recv_timeout
will either panic or return
prematurely indicating the timeout was reached.
The following functions don't configure a timeout at all and revert back to default behavior:
net::TcpStream::connect_timeout
net::TcpStream::set_read_timeout
net::TcpStream::set_write_timeout
The following functions always return a default value:
net::TcpStream::read_timeout
net::TcpStream::write_timeout
std::fs
No filesystem is available in the enclave for security reasons. All functions
in std::fs
return an error when called. Depending on your scenario, you can
use one of the following recommended alternatives.
Storing persistent data
Use a storage or database service to store a blob sealed with authenticated encryption.
When reading from an untrusted storage or database service, the service may perform a rollback attack by not returning the latest stored data. One possible defense against this attack is to use a trusted freshness tracking service.
Also, if your enclave stores more than one blob, the service may perform a partial rollback attack by returning different versions of different blobs. One possible defense against this attack is to track the integrity of all blobs as a whole, for example with a Merkle tree.
Configuration files
Configuration that affects security parameters should be compiled into the enclave so that it is included in the enclave measurement. See enclave identity for more information. Configuration data may also be appended after build but before signing, see appending enclave data.
Configuration data that isn't relevant to security may be provided as a command-line argument, read from stdin, or appended as unmeasured data.
Filesystem-related functionality in std::env
In addition to the fs
module being unavailable, the following functions in
std::env
always return an error:
current_dir
current_exe
home_dir
join_paths
The following function does nothing but does not return an error:
set_current_dir
The following functions panic unconditionally:
split_paths
temp_dir
Processes
Multi-processing is not supported. Instead, call out to network services to interact with out-of-enclave functionality.
The following methods in std::process::Command
always return an error:
spawn
output
status
The std::process::id
function panics unconditionally.
std::net::UdpSocket
Datagram networking is not supported. UdpSocket::bind
always returns an
error. Support is being considered in GitHub issue #75.