Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(bindings): add example for kms pkey offload #4980

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bindings/rust-examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[workspace]
members = [
"client-hello-config-resolution", "tokio-server-client",
"async-pkey-offload",
"client-hello-config-resolution",
"tokio-server-client",
]
resolver = "2"

Expand Down
19 changes: 19 additions & 0 deletions bindings/rust-examples/async-pkey-offload/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "async-pkey-offload"
version.workspace = true
authors.workspace = true
publish.workspace = true
license.workspace = true
edition.workspace = true

[dependencies]
aws-config = "1.5.8"
aws-sdk-kms = "1.47.0"
clap = { version = "4", features = ["derive"] }
pin-project = "1.1.6"
rcgen = "0.13.1"
s2n-tls = { path = "../../rust/s2n-tls" }
s2n-tls-tokio = { path = "../../rust/s2n-tls-tokio" }
tokio = { version = "1", features = ["full"] }
tracing = "0.1.41"
yasna = "0.5.2"
90 changes: 90 additions & 0 deletions bindings/rust-examples/async-pkey-offload/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# PKey Offload with KMS

This example shows how to use s2n-tls pkey offload functionality to create TLS connections with a private key that is stored in KMS
jmayclin marked this conversation as resolved.
Show resolved Hide resolved

It will
1. generate an asymmetric key in KMS
2. create a public (self-signed) x509 certificate corresponding to the private key in KMS
3. handle TLS connections for that certificate, offloading all private key operations to KMS

If you are looking for a simpler example, you should start with the basic [tokio server & client](../tokio-server-client/Readme.md) instead.
jmayclin marked this conversation as resolved.
Show resolved Hide resolved

```
server (s2n-tls) KMS
┌───────────────┐ ┌─────────────┐
│ │ │ │
Client──────────────►│ Public Key ┼───────────►│ Private Key │
▲ │ (certificate) │ ▲ │ │
│ │ │ │ └─────────────┘
│ └───────────────┘ │
TLS Connection pkey offload
through AWS SDK
```

The client will talk to an s2n-tls server. This server only contains the public key in the form of an x509 certificate. The server does _not_ hold a copy of a private key. The only copy of the key is stored in KMS, and it can not be removed from KMS. The advantage of this is that if an attacker were able to compromise the server, they could not steal the private key.

Because the server does not have a copy of the private key, it must delegate cryptographic operations to KMS, and return those results to the clients. s2n-tls offers a "pkey offload" feature to accomplish this behavior. This example will use s2n-tls pkey offload functionality along with the AWS SDK to successfully complete a TLS handshake with the client, while never actually holding the private key.

### Running the demo
You will need to have access to IAM credentials with KMS permissions to create, list, describe, sign, and delete keys.

Once those are available in the environment, you can run the demo - which is structured as a test - with `cargo test -- --nocapture`.

```
creating new key
Using KMS Key: "b6a9ff77-f672-46a1-8d59-8fa0eb1136ed"
client successfully connected
TlsStream {
connection: Connection {
handshake_type: "NEGOTIATED|FULL_HANDSHAKE|MIDDLEBOX_COMPAT",
cipher_suite: "TLS_AES_128_GCM_SHA256",
actual_protocol_version: TLS13,
selected_curve: "secp256r1",
..
},
}
test handshake ... ok
```

You can clean up the test resources by running `cargo run --bin delete_demo_keys`.

### Self Signed Cert Generation
The example will use a self signed cert with an asymmetric key that is stored in KMS. First we generate a private key in KMS. This will be the private key of the certificate. We use `rcgen` and it's associated `KeyPair::from_remote` functionality to actually generate the cert. Below you can see what the certificate looked like when I ran it on my own machine.
jmayclin marked this conversation as resolved.
Show resolved Hide resolved

```
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
16:5c:dd:a4:d0:01:34:1a:82:16:03:2f:3b:d6:08:95:94:a0:6e:c3
Signature Algorithm: ecdsa-with-SHA384
Issuer: CN = rcgen self signed cert
Validity
Not Before: Jan 1 00:00:00 1975 GMT
Not After : Jan 1 00:00:00 4096 GMT
Subject: CN = rcgen self signed cert
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
04:d4:40:4c:1a:77:c2:2a:d2:04:f6:11:17:e2:e5:
7b:d7:14:9b:47:4a:fb:58:0e:09:a8:7e:c0:45:00:
51:55:22:52:1e:51:46:98:e5:57:08:7c:31:36:d5:
03:81:21:67:cf:88:75:43:21:c2:91:ec:bb:8f:67:
12:76:67:df:44:a0:2f:55:57:af:89:57:66:38:ad:
0d:0f:55:bb:2f:70:24:f8:46:67:5e:5b:d0:b5:ba:
79:6e:48:a7:f3:c7:9c
ASN1 OID: secp384r1
NIST CURVE: P-384
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:async-pkey.demo.s2n
Signature Algorithm: ecdsa-with-SHA384
Signature Value:
30:64:02:30:5f:8d:89:d2:ee:f1:2c:fc:88:43:3b:b4:31:6a:
7c:61:8e:6a:bb:b3:97:15:68:2d:77:c3:3e:08:c6:48:71:2f:
2d:ba:96:14:40:f0:66:7d:05:ba:47:27:12:83:d9:78:02:30:
27:df:5a:73:f6:3a:42:25:e2:7e:e4:e4:65:88:bc:56:98:7a:
47:92:bd:56:b7:1e:12:44:3a:e4:a1:63:32:f4:35:75:ac:e9:
94:d6:5d:2b:c5:c4:6d:3b:43:23:a4:b8
```
1 change: 1 addition & 0 deletions bindings/rust-examples/async-pkey-offload/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stable
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use async_pkey_offload::{DEMO_REGION, KEY_DESCRIPTION};
use aws_config::{BehaviorVersion, Region};
use aws_sdk_kms::Client;

/// This is a small helper script used to delete any keys that might have been
/// created by the demo.
///
/// It will iterate over all the KMS keys and schedule the deletion of any keys
/// where the key description is [crate::KEY_DESCRIPTION]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let shared_config = aws_config::defaults(BehaviorVersion::v2024_03_28())
.region(Region::from_static(DEMO_REGION))
.load()
.await;

let client = Client::new(&shared_config);

// list all KMS keys
let key_list = client.list_keys().send().await?;
if key_list.truncated {
// assumption: key list should be small enough to not require pagination
return Err("key list should not be truncated".into());
}

let keys = match key_list.keys {
Some(keys) => keys,
// no keys to delete, can immediately return
None => return Ok(()),
};

for k in keys {
let describe_output = client
.describe_key()
.key_id(k.key_id().unwrap())
.send()
.await?;

let metadata = match describe_output.key_metadata {
Some(metadata) => metadata,
None => continue,
};

// this key is already scheduled for deletion
if metadata.deletion_date.is_some() {
continue;
}

if metadata.description() == Some(KEY_DESCRIPTION) {
println!("scheduling {:?} for deletion", k.key_id().unwrap());
client
.schedule_key_deletion()
.key_id(k.key_id().unwrap())
.send()
.await?;
}
}

Ok(())
}
Loading
Loading