-
Notifications
You must be signed in to change notification settings - Fork 715
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
example(bindings): Add session resumption example
- Loading branch information
Showing
8 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
[workspace] | ||
members = [ | ||
"client-hello-config-resolution", | ||
"session-resumption", | ||
] | ||
resolver = "2" | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "session-resumption" | ||
version.workspace = true | ||
authors.workspace = true | ||
publish.workspace = true | ||
license.workspace = true | ||
edition.workspace = true | ||
|
||
[dependencies] | ||
s2n-tls = { path = "../../rust/s2n-tls" } | ||
s2n-tls-tokio = { path = "../../rust/s2n-tls-tokio" } | ||
tokio = { version = "1", features = ["full"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
This example demonstrates the s2n-tls [session resumption](https://aws.github.io/s2n-tls/usage-guide/ch11-resumption.html) feature. First, the client connects to the server and retrieves a session ticket. Then, the client connects to the server again, but uses the retrieved session ticket to resume the previous session. | ||
|
||
This example also demonstrates how the application context can be used to associate arbitrary information, such as an IP address, with an s2n-tls connection. The IP address is used in this example to determine whether a particular session ticket is viable for use on a subsequent connection. | ||
|
||
To run this example, first start the server in one terminal: | ||
``` | ||
cargo run --bin server | ||
``` | ||
|
||
Then run the client in another terminal: | ||
``` | ||
cargo run --bin client | ||
``` |
20 changes: 20 additions & 0 deletions
20
bindings/rust-examples/session-resumption/certs/generate.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
# immediately bail if any command fails | ||
set -e | ||
|
||
echo "generating self-signed certificate" | ||
openssl req -new -noenc -x509 \ | ||
-newkey ec \ | ||
-pkeyopt ec_paramgen_curve:P-384 \ | ||
-keyout test-key.pem \ | ||
-out test-cert.pem \ | ||
-days 65536 \ | ||
-SHA384 \ | ||
-subj "/C=US/CN=s2n" \ | ||
-addext "basicConstraints = critical,CA:true" \ | ||
-addext "keyUsage = critical,keyCertSign" \ | ||
-addext "subjectAltName = DNS:127.0.0.1" |
13 changes: 13 additions & 0 deletions
13
bindings/rust-examples/session-resumption/certs/test-cert.pem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIB7zCCAXagAwIBAgIUfg8ui1w1uHtk3bsIWhWitT3cicAwCgYIKoZIzj0EAwMw | ||
GzELMAkGA1UEBhMCVVMxDDAKBgNVBAMMA3MybjAgFw0yNDA1MTUxNTQ4MTBaGA8y | ||
MjAzMTAyMTE1NDgxMFowGzELMAkGA1UEBhMCVVMxDDAKBgNVBAMMA3MybjB2MBAG | ||
ByqGSM49AgEGBSuBBAAiA2IABBaRabOi6hcEixRizyLHhezXXxeJzQmzl+8N21do | ||
4NwVW38mCop94fkRX2yYCPE/NB976zbdbf2NI4fg6TFjMyjiuUfHzbto/9xUCbxU | ||
Vhk/zQs/lQoWwZfmjMLVIEAjlaN5MHcwHQYDVR0OBBYEFEvgyZ9xCcmq66Jv4IWu | ||
J66psVP1MB8GA1UdIwQYMBaAFEvgyZ9xCcmq66Jv4IWuJ66psVP1MA8GA1UdEwEB | ||
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMBQGA1UdEQQNMAuCCTEyNy4wLjAuMTAK | ||
BggqhkjOPQQDAwNnADBkAjBec3GrlGlPF2Hg3EWP6iFQicbsZJtpZIC0OEpunn57 | ||
vAuvCAD+1PolgVdggV1wyp4CMCwxEse93GCdBXZJb11bA//BAamrmLAaoX/AmqvY | ||
vmUPPK9C3WbBT0IfTYkJG2XXQg== | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
-----BEGIN PRIVATE KEY----- | ||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCNXvpHT/NBaXX8KIHy | ||
5r62KOLHN9AMb7xCxZtfYIqTNJRdZSCf3hs6kdGMZWa/NoKhZANiAAQWkWmzouoX | ||
BIsUYs8ix4Xs118Xic0Js5fvDdtXaODcFVt/JgqKfeH5EV9smAjxPzQfe+s23W39 | ||
jSOH4OkxYzMo4rlHx827aP/cVAm8VFYZP80LP5UKFsGX5ozC1SBAI5U= | ||
-----END PRIVATE KEY----- |
117 changes: 117 additions & 0 deletions
117
bindings/rust-examples/session-resumption/src/bin/client.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use s2n_tls::{ | ||
callbacks::{SessionTicket, SessionTicketCallback}, | ||
connection::{Connection, ModifiedBuilder}, | ||
security::DEFAULT_TLS13, | ||
}; | ||
use s2n_tls_tokio::TlsConnector; | ||
use std::{ | ||
collections::HashMap, | ||
error::Error, | ||
net::IpAddr, | ||
sync::{Arc, Mutex}, | ||
}; | ||
use tokio::{ | ||
io::{AsyncReadExt, AsyncWriteExt}, | ||
net::TcpStream, | ||
}; | ||
|
||
struct ApplicationContext { | ||
ip_addr: IpAddr, | ||
tickets_received: u32, | ||
} | ||
|
||
#[derive(Default, Clone)] | ||
pub struct SessionTicketHandler { | ||
session_tickets: Arc<Mutex<HashMap<IpAddr, Vec<u8>>>>, | ||
} | ||
|
||
impl SessionTicketCallback for SessionTicketHandler { | ||
fn on_session_ticket(&self, connection: &mut Connection, session_ticket: &SessionTicket) { | ||
let app_context = connection | ||
.application_context_mut::<ApplicationContext>() | ||
.unwrap(); | ||
|
||
let size = session_ticket.len().unwrap(); | ||
let mut data = vec![0; size]; | ||
session_ticket.data(&mut data).unwrap(); | ||
|
||
// Associate the received session ticket with the connection's IP address. | ||
let mut session_tickets = self.session_tickets.lock().unwrap(); | ||
session_tickets.insert(app_context.ip_addr, data); | ||
|
||
// Indicate that the connection has received a session ticket. | ||
app_context.tickets_received += 1; | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn Error>> { | ||
let cert_path = format!("{}/certs/test-cert.pem", env!("CARGO_MANIFEST_DIR")); | ||
let cert = std::fs::read(cert_path).unwrap(); | ||
|
||
let session_ticket_handler = SessionTicketHandler::default(); | ||
|
||
let config = { | ||
let mut builder = s2n_tls::config::Builder::new(); | ||
builder.set_security_policy(&DEFAULT_TLS13).unwrap(); | ||
builder.trust_pem(&cert).unwrap(); | ||
builder | ||
.set_session_ticket_callback(session_ticket_handler.clone()) | ||
.unwrap(); | ||
builder.enable_session_tickets(true).unwrap(); | ||
builder.build()? | ||
}; | ||
|
||
for connection_idx in 0..3 { | ||
let stream = TcpStream::connect("127.0.0.1:9000").await?; | ||
let ip = stream.peer_addr().unwrap().ip(); | ||
|
||
let builder = ModifiedBuilder::new(config.clone(), |conn| { | ||
// Associate the IP address with the new connection. | ||
conn.set_application_context(ApplicationContext { | ||
ip_addr: ip, | ||
tickets_received: 0, | ||
}); | ||
|
||
// If a session ticket exists that corresponds with the IP address, resume the | ||
// connection. | ||
let session_tickets = session_ticket_handler.session_tickets.lock().unwrap(); | ||
if let Some(session_ticket) = session_tickets.get(&ip) { | ||
conn.set_session_ticket(session_ticket)?; | ||
} | ||
|
||
Ok(conn) | ||
}); | ||
let client = TlsConnector::new(builder); | ||
|
||
let handshake = client.connect("127.0.0.1", stream).await; | ||
let mut tls = match handshake { | ||
Ok(tls) => tls, | ||
Err(e) => { | ||
println!("error during handshake: {e}"); | ||
return Ok(()); | ||
} | ||
}; | ||
|
||
let mut response = String::new(); | ||
tls.read_to_string(&mut response).await?; | ||
println!("server response: {response}"); | ||
|
||
tls.shutdown().await?; | ||
|
||
let connection = tls.as_ref(); | ||
if connection_idx == 0 { | ||
assert!(!connection.resumed()); | ||
} else { | ||
assert!(connection.resumed()); | ||
println!("connection resumed!"); | ||
} | ||
|
||
let app_ctx = connection | ||
.application_context::<ApplicationContext>() | ||
.unwrap(); | ||
assert_eq!(app_ctx.tickets_received, 1); | ||
} | ||
|
||
Ok(()) | ||
} |
46 changes: 46 additions & 0 deletions
46
bindings/rust-examples/session-resumption/src/bin/server.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use s2n_tls::security::DEFAULT_TLS13; | ||
use s2n_tls_tokio::TlsAcceptor; | ||
use std::{error::Error, time::SystemTime}; | ||
use tokio::{io::AsyncWriteExt, net::TcpListener}; | ||
|
||
const KEY: [u8; 16] = [0; 16]; | ||
const KEY_NAME: [u8; 3] = [1, 3, 4]; | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn Error>> { | ||
let cert_path = format!("{}/certs/test-cert.pem", env!("CARGO_MANIFEST_DIR")); | ||
let key_path = format!("{}/certs/test-key.pem", env!("CARGO_MANIFEST_DIR")); | ||
let cert = std::fs::read(cert_path).unwrap(); | ||
let key = std::fs::read(key_path).unwrap(); | ||
|
||
let mut config = s2n_tls::config::Builder::new(); | ||
config.set_security_policy(&DEFAULT_TLS13).unwrap(); | ||
config | ||
.add_session_ticket_key(&KEY_NAME, &KEY, SystemTime::now()) | ||
.unwrap(); | ||
config.load_pem(&cert, &key).unwrap(); | ||
let config = config.build()?; | ||
let server = TlsAcceptor::new(config); | ||
|
||
let listener = TcpListener::bind("0.0.0.0:9000").await?; | ||
loop { | ||
let server = server.clone(); | ||
let (stream, _) = listener.accept().await?; | ||
|
||
tokio::spawn(async move { | ||
let handshake = server.accept(stream).await; | ||
let mut tls = match handshake { | ||
Ok(tls) => tls, | ||
Err(e) => { | ||
println!("error during handshake: {e}"); | ||
return Ok(()); | ||
} | ||
}; | ||
|
||
let _ = tls.write("hello from server.".as_bytes()).await?; | ||
tls.shutdown().await?; | ||
|
||
Ok::<(), Box<dyn Error + Send + Sync>>(()) | ||
}); | ||
} | ||
} |