Skip to content

Commit 21dba1a

Browse files
author
Adrian Cruceru
committed
Async support
1 parent f8a03da commit 21dba1a

File tree

9 files changed

+1561
-11
lines changed

9 files changed

+1561
-11
lines changed

Cargo.lock

Lines changed: 512 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
[workspace]
22
members = ["mbedtls", "mbedtls-sys"]
3+
4+
[patch.crates-io]
5+
mio = { git = "https://github.com/mzohreva/mio", branch = "mz/sgx-port-0.7.6" }
6+
tokio = { git = "https://github.com/mzohreva/tokio", branch = "mz/sgx-port-0.3.4" }
7+

mbedtls/Cargo.toml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ bit-vec = { version = "0.5", optional = true }
3030
block-modes = { version = "0.3", optional = true }
3131
rc2 = { version = "0.3", optional = true }
3232
cfg-if = "1.0.0"
33+
tokio = { version = "0.3.4", optional = true }
3334

3435
[target.x86_64-fortanix-unknown-sgx.dependencies]
35-
rs-libc = "0.2.0"
36+
rs-libc = "0.1.0"
3637
chrono = "0.4"
3738

3839
[dependencies.mbedtls-sys-auto]
@@ -48,6 +49,11 @@ serde_cbor = "0.6"
4849
hex = "0.3"
4950
matches = "0.1.8"
5051
hyper = { version = "0.10.16", default-features = false }
52+
hyper13 = { package = "hyper", version = "0.13", default-features = false, features = ["stream"] }
53+
tokio-02 = { package = "tokio", version = "0.2", default-features = false }
54+
async-stream = "0.3.0"
55+
futures = "0.3"
56+
tracing = "0.1"
5157

5258
[build-dependencies]
5359
cc = "1.0"
@@ -67,6 +73,8 @@ padlock = ["mbedtls-sys-auto/padlock"]
6773
dsa = ["std", "yasna", "num-bigint", "bit-vec"]
6874
pkcs12 = ["std", "yasna"]
6975
pkcs12_rc2 = ["pkcs12", "rc2", "block-modes"]
76+
async = ["std", "tokio","tokio/net","tokio/io-util", "tokio/macros"]
77+
async-rt = ["async", "tokio/rt", "tokio/sync", "tokio/rt-multi-thread"]
7078
migration_mode=[]
7179

7280
[[example]]
@@ -92,3 +100,20 @@ required-features = ["std"]
92100
[[test]]
93101
name = "hyper"
94102
required-features = ["std"]
103+
104+
[[test]]
105+
name = "hyper13"
106+
required-features = ["std", "async-rt"]
107+
108+
[[test]]
109+
name = "async_session"
110+
path = "tests/async_session.rs"
111+
required-features = ["async-rt"]
112+
113+
114+
[package.metadata.fortanix-sgx]
115+
threads = 100
116+
heap-size = 0x40000000
117+
stack-size = 0x100000
118+
# The following are not processed by the EDP tools but are picked up by build-enclave.sh:
119+
#isvprodid = 66

mbedtls/src/ssl/async_utils.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* Copyright (c) Fortanix, Inc.
2+
*
3+
* Licensed under the GNU General Public License, version 2 <LICENSE-GPL or
4+
* https://www.gnu.org/licenses/gpl-2.0.html> or the Apache License, Version
5+
* 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>, at your
6+
* option. This file may not be copied, modified, or distributed except
7+
* according to those terms. */
8+
9+
#![cfg(all(feature = "std", feature = "async"))]
10+
11+
use std::cell::Cell;
12+
use std::ptr::null_mut;
13+
use std::rc::Rc;
14+
use std::task::{Context as TaskContext, Poll};
15+
16+
17+
#[cfg(not(feature = "std"))]
18+
use core_io::{Error as IoError, Result as IoResult, ErrorKind as IoErrorKind};
19+
#[cfg(feature = "std")]
20+
use std::io::{Error as IoError, Result as IoResult, ErrorKind as IoErrorKind};
21+
22+
23+
#[derive(Clone)]
24+
pub struct ErasedContext(Rc<Cell<*mut ()>>);
25+
26+
unsafe impl Send for ErasedContext {}
27+
28+
impl ErasedContext {
29+
pub fn new() -> Self {
30+
Self(Rc::new(Cell::new(null_mut())))
31+
}
32+
33+
pub unsafe fn get(&self) -> Option<&mut TaskContext<'_>> {
34+
let ptr = self.0.get();
35+
if ptr.is_null() {
36+
None
37+
} else {
38+
Some(&mut *(ptr as *mut _))
39+
}
40+
}
41+
42+
pub fn set(&self, cx: &mut TaskContext<'_>) {
43+
self.0.set(cx as *mut _ as *mut ());
44+
}
45+
46+
pub fn clear(&self) {
47+
self.0.set(null_mut());
48+
}
49+
}
50+
51+
// mbedtls_ssl_write() has some weird semantics w.r.t non-blocking I/O:
52+
//
53+
// > When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be
54+
// > called later **with the same arguments**, until it returns a value greater
55+
// > than or equal to 0. When the function returns MBEDTLS_ERR_SSL_WANT_WRITE
56+
// > there may be some partial data in the output buffer, however this is not
57+
// > yet sent.
58+
//
59+
// WriteTracker is used to ensure we pass the same data in that scenario.
60+
//
61+
// Reference:
62+
// https://tls.mbed.org/api/ssl_8h.html#a5bbda87d484de82df730758b475f32e5
63+
pub struct WriteTracker {
64+
pending: Option<Box<DigestAndLen>>,
65+
}
66+
67+
struct DigestAndLen {
68+
#[cfg(debug_assertions)]
69+
digest: [u8; 20], // SHA-1
70+
len: usize,
71+
}
72+
73+
impl WriteTracker {
74+
fn new() -> Self {
75+
WriteTracker {
76+
pending: None,
77+
}
78+
}
79+
80+
#[cfg(debug_assertions)]
81+
fn digest(buf: &[u8]) -> [u8; 20] {
82+
use crate::hash::{Md, Type};
83+
let mut out = [0u8; 20];
84+
let res = Md::hash(Type::Sha1, buf, &mut out[..]);
85+
assert_eq!(res, Ok(out.len()));
86+
out
87+
}
88+
89+
pub fn adjust_buf<'a>(&self, buf: &'a [u8]) -> IoResult<&'a [u8]> {
90+
match self.pending.as_ref() {
91+
None => Ok(buf),
92+
Some(pending) => {
93+
if pending.len <= buf.len() {
94+
let buf = &buf[..pending.len];
95+
96+
// We only do this check in debug mode since it's an expensive check.
97+
#[cfg(debug_assertions)]
98+
if Self::digest(buf) == pending.digest {
99+
return Ok(buf);
100+
}
101+
102+
#[cfg(not(debug_assertions))]
103+
return Ok(buf);
104+
}
105+
Err(IoError::new(
106+
IoErrorKind::Other,
107+
"mbedtls expects the same data if the previous call to poll_write() returned Poll::Pending"
108+
))
109+
},
110+
}
111+
}
112+
113+
pub fn post_write(&mut self, buf: &[u8], res: &Poll<IoResult<usize>>) {
114+
match res {
115+
&Poll::Pending => {
116+
if self.pending.is_none() {
117+
self.pending = Some(Box::new(DigestAndLen {
118+
#[cfg(debug_assertions)]
119+
digest: Self::digest(buf),
120+
len: buf.len(),
121+
}));
122+
}
123+
},
124+
_ => {
125+
self.pending = None;
126+
}
127+
}
128+
}
129+
}
130+
131+
pub struct IoAdapter<S> {
132+
pub inner: S,
133+
pub ecx: ErasedContext,
134+
pub write_tracker: WriteTracker,
135+
}
136+
137+
impl<S> IoAdapter<S> {
138+
pub fn new(stream: S) -> Self {
139+
Self {
140+
inner: stream,
141+
ecx: ErasedContext::new(),
142+
write_tracker: WriteTracker::new(),
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)