Initial implementation
This commit is contained in:
commit
c19c42ccd7
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
client_instance
|
||||||
|
server_instance
|
71
Cargo.toml
Normal file
71
Cargo.toml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
[package]
|
||||||
|
name = "occlum-ratls"
|
||||||
|
version = "0.4.5"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Ivan Chirkin <chirkin.ivan@gmail.com>"]
|
||||||
|
description = "Lib for remote attestation between occlum instances"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/aggregion/occlum-ratls"
|
||||||
|
keywords = ["occlum", "rustls", "ratls"]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
x509-parser = "0.15.0"
|
||||||
|
#rustls = { version = "0.21", features = [
|
||||||
|
rustls = { version = "0.20.9", features = [
|
||||||
|
"dangerous_configuration",
|
||||||
|
"logging"
|
||||||
|
] }
|
||||||
|
log = "0.4.17"
|
||||||
|
rcgen = "0.12.1"
|
||||||
|
occlum-sgx = "^0.1.12"
|
||||||
|
ring = "0.17.8"
|
||||||
|
hex = "0.4"
|
||||||
|
|
||||||
|
[dependencies.actix-web]
|
||||||
|
version = "4.3.1"
|
||||||
|
features = ["rustls-0_20"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.actix-http]
|
||||||
|
version = "3.3"
|
||||||
|
features = ["http2", "ws"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.actix-service]
|
||||||
|
version = "2"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.reqwest]
|
||||||
|
version = "=0.11.10"
|
||||||
|
default-features = false
|
||||||
|
features = ["__rustls"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dev-dependencies.env_logger]
|
||||||
|
version = "0.10.0"
|
||||||
|
|
||||||
|
[dev-dependencies.tokio]
|
||||||
|
version = "1"
|
||||||
|
features = ["full"]
|
||||||
|
|
||||||
|
[dev-dependencies.cargo-husky]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
occlum = []
|
||||||
|
reqwest = ["dep:reqwest"]
|
||||||
|
actix-web = ["dep:actix-web", "actix-service", "actix-http"]
|
||||||
|
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "server"
|
||||||
|
required-features = ["actix-web"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "client"
|
||||||
|
required-features = ["reqwest"]
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Occlum SGX Remote Attestation integrated in TLS connection
|
||||||
|
|
||||||
|
Steps to test the project:
|
||||||
|
```
|
||||||
|
occlum-cargo build --example server --features="occlum,actix-web"
|
||||||
|
strip -s target/x86_64-unknown-linux-musl/debug/examples/server
|
||||||
|
./build_server.sh
|
||||||
|
|
||||||
|
occlum-cargo build --example client --features="occlum,reqwest"
|
||||||
|
strip -s target/x86_64-unknown-linux-musl/debug/examples/client
|
||||||
|
./build_client.sh
|
||||||
|
```
|
11
build_client.sh
Executable file
11
build_client.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# initialize occlum workspace
|
||||||
|
rm -rf client_instance && mkdir client_instance && cd client_instance
|
||||||
|
|
||||||
|
occlum init && rm -rf image
|
||||||
|
copy_bom -f ../client.yaml --root image --include-dir /opt/occlum/etc/template
|
||||||
|
|
||||||
|
occlum build
|
||||||
|
occlum run /bin/client
|
11
build_server.sh
Executable file
11
build_server.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# initialize occlum workspace
|
||||||
|
rm -rf server_instance && mkdir server_instance && cd server_instance
|
||||||
|
|
||||||
|
occlum init && rm -rf image
|
||||||
|
copy_bom -f ../server.yaml --root image --include-dir /opt/occlum/etc/template
|
||||||
|
|
||||||
|
occlum build
|
||||||
|
occlum run /bin/server
|
7
client.yaml
Normal file
7
client.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
includes:
|
||||||
|
- base.yaml
|
||||||
|
targets:
|
||||||
|
- target: /bin
|
||||||
|
copy:
|
||||||
|
- files:
|
||||||
|
- ../target/x86_64-unknown-linux-musl/debug/examples/client
|
27
examples/client.rs
Normal file
27
examples/client.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use occlum_ratls::prelude::*;
|
||||||
|
use reqwest::ClientBuilder;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
|
||||||
|
|
||||||
|
let mrsigner_hex = "83D719E77DEACA1470F6BAF62A4D774303C899DB69020F9C70EE1DFC08C7CE9E";
|
||||||
|
let mut mrsigner = [0u8; 32];
|
||||||
|
hex::decode_to_slice(mrsigner_hex, &mut mrsigner)?;
|
||||||
|
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.use_ratls(
|
||||||
|
RaTlsConfig::new().allow_instance_measurement(
|
||||||
|
InstanceMeasurement::new()
|
||||||
|
.with_mrsigners(vec![SGXMeasurement::new(mrsigner)])
|
||||||
|
.with_product_ids(vec![0]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.build()?;
|
||||||
|
let res = client.get("https://127.0.0.1:8000").send().await?;
|
||||||
|
let data = res.text().await?;
|
||||||
|
|
||||||
|
println!("response: {}", data);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
37
examples/server.rs
Normal file
37
examples/server.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use actix_web::{get, App, HttpServer};
|
||||||
|
use occlum_ratls::prelude::*;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index() -> String {
|
||||||
|
format!("Hello world!")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
|
||||||
|
|
||||||
|
let mrsigner_hex = "83D719E77DEACA1470F6BAF62A4D774303C899DB69020F9C70EE1DFC08C7CE9E";
|
||||||
|
let mut mrsigner = [0u8; 32];
|
||||||
|
hex::decode_to_slice(mrsigner_hex, &mut mrsigner).expect("mrsigner decoding failed");
|
||||||
|
|
||||||
|
HttpServer::new(|| App::new().service(index))
|
||||||
|
.bind_ratls(
|
||||||
|
SocketAddr::from(([127, 0, 0, 1], 8000)),
|
||||||
|
RaTlsConfig::new()
|
||||||
|
.allow_instance_measurement(
|
||||||
|
InstanceMeasurement::new().with_mrsigners(vec![SGXMeasurement::new(mrsigner)]),
|
||||||
|
)
|
||||||
|
.allow_instance_measurement(
|
||||||
|
InstanceMeasurement::new()
|
||||||
|
.with_mrenclaves(vec![
|
||||||
|
SGXMeasurement::new([0u8; 32]),
|
||||||
|
SGXMeasurement::new([1u8; 32]),
|
||||||
|
])
|
||||||
|
.with_product_ids(vec![0, 2]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
7
server.yaml
Normal file
7
server.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
includes:
|
||||||
|
- base.yaml
|
||||||
|
targets:
|
||||||
|
- target: /bin
|
||||||
|
copy:
|
||||||
|
- files:
|
||||||
|
- ../target/x86_64-unknown-linux-musl/debug/examples/server
|
157
src/cert.rs
Normal file
157
src/cert.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
use crate::utils::hash_sha512;
|
||||||
|
use crate::{error::RaTlsError, RaTlsConfig};
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
use occlum_sgx::SGXQuote;
|
||||||
|
use rustls::{
|
||||||
|
sign::{any_supported_type, CertifiedKey},
|
||||||
|
Certificate, PrivateKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rcgen::{
|
||||||
|
Certificate as GenCertificate, CertificateParams, CustomExtension, DistinguishedName, KeyPair,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
use x509_parser::{nom::Parser, oid_registry::Oid, prelude::X509CertificateParser, public_key};
|
||||||
|
|
||||||
|
pub trait CertificateBuilder: Send + Sync {
|
||||||
|
fn build(&self) -> Result<CertifiedKey, RaTlsError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RaTlsCertifiedKey {
|
||||||
|
cert_der: Vec<u8>,
|
||||||
|
key_der: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RaTlsCertificateBuilder {
|
||||||
|
common_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RaTlsCertificateBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
common_name: "RATLS".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const REPORT_OID: [u64; 5] = [1, 2, 840, 113741, 1];
|
||||||
|
|
||||||
|
impl RaTlsCertificateBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_common_name(self, cn: String) -> Self {
|
||||||
|
Self { common_name: cn }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_internal(&self) -> Result<RaTlsCertifiedKey, Box<dyn Error>> {
|
||||||
|
let mut distinguished_name = DistinguishedName::new();
|
||||||
|
|
||||||
|
distinguished_name.push(rcgen::DnType::CommonName, self.common_name.clone());
|
||||||
|
distinguished_name.push(rcgen::DnType::CountryName, "US");
|
||||||
|
distinguished_name.push(rcgen::DnType::OrganizationName, "Aggregion");
|
||||||
|
|
||||||
|
let mut params = CertificateParams::default();
|
||||||
|
let key_pair = KeyPair::generate(params.alg)?;
|
||||||
|
|
||||||
|
let quote = self.get_quote(&key_pair)?;
|
||||||
|
|
||||||
|
params.key_pair = Some(key_pair);
|
||||||
|
params.distinguished_name = distinguished_name;
|
||||||
|
|
||||||
|
params.custom_extensions = vec![CustomExtension::from_oid_content(&REPORT_OID, quote)];
|
||||||
|
|
||||||
|
let crt = GenCertificate::from_params(params)?;
|
||||||
|
|
||||||
|
Ok(RaTlsCertifiedKey {
|
||||||
|
cert_der: crt.serialize_der()?,
|
||||||
|
key_der: crt.serialize_private_key_der(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "occlum"))]
|
||||||
|
fn get_quote(&self, _: &KeyPair) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
Ok([0u8; 32].to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
fn get_quote(&self, key_pair: &KeyPair) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let public_key = key_pair.public_key_raw().to_vec();
|
||||||
|
let report_data = hash_sha512(public_key);
|
||||||
|
let quote = SGXQuote::from_report_data(&report_data)?;
|
||||||
|
|
||||||
|
Ok(quote.as_slice().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CertificateBuilder for RaTlsCertificateBuilder {
|
||||||
|
fn build(&self) -> Result<CertifiedKey, RaTlsError> {
|
||||||
|
self.build_internal()
|
||||||
|
.map(|k| {
|
||||||
|
let sign_key = any_supported_type(&PrivateKey(k.key_der)).unwrap();
|
||||||
|
|
||||||
|
CertifiedKey::new(vec![Certificate(k.cert_der)], sign_key)
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
let err = RaTlsError::CertificateBuildError(e.to_string());
|
||||||
|
error!("{}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RaTlsCertificate {
|
||||||
|
fn verify_quote(&self, config: &RaTlsConfig) -> Result<(), Box<dyn Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsCertificate for rustls::Certificate {
|
||||||
|
#[cfg(not(feature = "occlum"))]
|
||||||
|
fn verify_quote(&self, _: &RaTlsConfig) -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
fn verify_quote(&self, config: &RaTlsConfig) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut parser = X509CertificateParser::new().with_deep_parse_extensions(true);
|
||||||
|
let (_, x509) = parser.parse(self.as_ref()).unwrap();
|
||||||
|
|
||||||
|
let report_oid = Oid::from(&REPORT_OID).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", x509);
|
||||||
|
|
||||||
|
if let Ok(Some(report)) = x509.get_extension_unique(&report_oid) {
|
||||||
|
let quote = SGXQuote::from_slice(report.value)?;
|
||||||
|
|
||||||
|
quote.verify()?;
|
||||||
|
|
||||||
|
let public_key = x509.public_key().parsed()?;
|
||||||
|
let public_key = match public_key {
|
||||||
|
public_key::PublicKey::EC(key) => key.data().to_vec(),
|
||||||
|
_ => return Err("Unexpected public key type".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let report_data = &*quote.report_data();
|
||||||
|
|
||||||
|
if hash_sha512(public_key) != report_data {
|
||||||
|
return Err("Invalid quote report data".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("x509 pk matches report data");
|
||||||
|
|
||||||
|
config.is_allowed_quote("e)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Not found quote extension".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/client.rs
Normal file
74
src/client.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cert::{CertificateBuilder, RaTlsCertificate, RaTlsCertificateBuilder},
|
||||||
|
RaTlsConfig, RaTlsConfigBuilder, RaTlsError,
|
||||||
|
};
|
||||||
|
use rustls::{
|
||||||
|
client::{ResolvesClientCert, ServerCertVerified, ServerCertVerifier},
|
||||||
|
sign::CertifiedKey,
|
||||||
|
ClientConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RaTlsServerCertVerifier {
|
||||||
|
config: RaTlsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsServerCertVerifier {
|
||||||
|
pub fn new(config: RaTlsConfig) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier for RaTlsServerCertVerifier {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
end_entity: &rustls::Certificate,
|
||||||
|
_intermediates: &[rustls::Certificate],
|
||||||
|
_server_name: &rustls::ServerName,
|
||||||
|
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||||
|
_ocsp_response: &[u8],
|
||||||
|
_now: std::time::SystemTime,
|
||||||
|
) -> Result<ServerCertVerified, rustls::Error> {
|
||||||
|
end_entity
|
||||||
|
.verify_quote(&self.config)
|
||||||
|
.map_err(|e| rustls::Error::General(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(ServerCertVerified::assertion())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RaTlsClientCertResolver {
|
||||||
|
cert: Arc<CertifiedKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsClientCertResolver {
|
||||||
|
pub fn new() -> Result<Self, RaTlsError> {
|
||||||
|
let builder = RaTlsCertificateBuilder::new().with_common_name("Client".to_string());
|
||||||
|
let cert = builder.build().map(Arc::new)?;
|
||||||
|
Ok(Self { cert })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvesClientCert for RaTlsClientCertResolver {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
_acceptable_issuers: &[&[u8]],
|
||||||
|
_sigschemes: &[rustls::SignatureScheme],
|
||||||
|
) -> Option<Arc<CertifiedKey>> {
|
||||||
|
Some(self.cert.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_certs(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsConfigBuilder<ClientConfig> for ClientConfig {
|
||||||
|
fn from_ratls_config(config: RaTlsConfig) -> Result<Self, RaTlsError> {
|
||||||
|
Ok(Self::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_custom_certificate_verifier(Arc::new(RaTlsServerCertVerifier::new(config)))
|
||||||
|
.with_client_cert_resolver(Arc::new(RaTlsClientCertResolver::new()?)))
|
||||||
|
}
|
||||||
|
}
|
138
src/config.rs
Normal file
138
src/config.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use crate::{RaTlsConfigBuilder, RaTlsError};
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
use occlum_sgx::SGXQuote;
|
||||||
|
|
||||||
|
pub use occlum_sgx::SGXMeasurement;
|
||||||
|
use rustls::{ClientConfig, ServerConfig};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct RaTlsConfig {
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
pub(crate) allowed_instances: Vec<InstanceMeasurement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct InstanceMeasurement {
|
||||||
|
pub(crate) mrsigners: Option<Vec<SGXMeasurement>>,
|
||||||
|
pub(crate) mrenclaves: Option<Vec<SGXMeasurement>>,
|
||||||
|
pub(crate) product_ids: Option<Vec<u16>>,
|
||||||
|
pub(crate) versions: Option<Vec<u16>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
impl InstanceMeasurement {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mrsigners(self, mrsigners: Vec<SGXMeasurement>) -> Self {
|
||||||
|
Self {
|
||||||
|
mrsigners: Some(mrsigners),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mrenclaves(self, mrenclaves: Vec<SGXMeasurement>) -> Self {
|
||||||
|
Self {
|
||||||
|
mrenclaves: Some(mrenclaves),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_product_ids(self, product_ids: Vec<u16>) -> Self {
|
||||||
|
Self {
|
||||||
|
product_ids: Some(product_ids),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_versions(self, versions: Vec<u16>) -> Self {
|
||||||
|
Self {
|
||||||
|
versions: Some(versions),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn check_quote_measurements(&self, quote: &SGXQuote) -> bool {
|
||||||
|
println!("mrsigner {}", quote.mrsigner());
|
||||||
|
println!("mrenclave {}", quote.mrenclave());
|
||||||
|
println!("productid {}", quote.product_id());
|
||||||
|
println!("version {}", quote.version());
|
||||||
|
println!("-----------------------------------------------------------------");
|
||||||
|
println!("mrsigners {:?}", self.mrsigners);
|
||||||
|
println!("mrenclaves {:?}", self.mrenclaves);
|
||||||
|
println!("product_ids {:?}", self.product_ids);
|
||||||
|
println!("versions {:?}", self.versions);
|
||||||
|
|
||||||
|
let mut result = false;
|
||||||
|
if let Some(mrsigners) = &self.mrsigners {
|
||||||
|
result = true;
|
||||||
|
let value = quote.mrsigner();
|
||||||
|
if !mrsigners.contains(&value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mrenclaves) = &self.mrenclaves {
|
||||||
|
result = true;
|
||||||
|
let value = quote.mrenclave();
|
||||||
|
if !mrenclaves.contains(&value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(product_ids) = &self.product_ids {
|
||||||
|
result = true;
|
||||||
|
let value = quote.product_id();
|
||||||
|
if !product_ids.contains(&value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(versions) = &self.versions {
|
||||||
|
result = true;
|
||||||
|
let value = quote.version();
|
||||||
|
if !versions.contains(&value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
pub fn allow_instance_measurement(mut self, instance_measurement: InstanceMeasurement) -> Self {
|
||||||
|
self.allowed_instances.push(instance_measurement);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
pub(crate) fn is_allowed_quote(&self, quote: &SGXQuote) -> Result<(), RaTlsError> {
|
||||||
|
match self
|
||||||
|
.allowed_instances
|
||||||
|
.iter()
|
||||||
|
.any(|im| im.check_quote_measurements(quote))
|
||||||
|
{
|
||||||
|
true => Ok(()),
|
||||||
|
false => Err(RaTlsError::QuoteVerifyError(format!(
|
||||||
|
"{:?} is not allowed",
|
||||||
|
quote
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_server_config(self) -> Result<ServerConfig, RaTlsError> {
|
||||||
|
ServerConfig::from_ratls_config(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_client_config(self) -> Result<ClientConfig, RaTlsError> {
|
||||||
|
ClientConfig::from_ratls_config(self)
|
||||||
|
}
|
||||||
|
}
|
20
src/error.rs
Normal file
20
src/error.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RaTlsError {
|
||||||
|
CertificateBuildError(String),
|
||||||
|
QuoteVerifyError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RaTlsError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
RaTlsError::CertificateBuildError(ref message) => {
|
||||||
|
write!(f, "CertificateBuildError: {}", message)
|
||||||
|
}
|
||||||
|
RaTlsError::QuoteVerifyError(ref message) => write!(f, "QuoteVerifyError: {}", message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for RaTlsError {}
|
48
src/http/actix_web.rs
Normal file
48
src/http/actix_web.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::net;
|
||||||
|
|
||||||
|
use actix_http::{body::MessageBody, Request};
|
||||||
|
use actix_service::{IntoServiceFactory, ServiceFactory};
|
||||||
|
use actix_web::dev::{AppConfig, Response};
|
||||||
|
use actix_web::*;
|
||||||
|
use rustls::ServerConfig;
|
||||||
|
|
||||||
|
use crate::{config::RaTlsConfig, RaTlsConfigBuilder};
|
||||||
|
|
||||||
|
pub trait ActixWebWithRatls<F, I, S, B>
|
||||||
|
where
|
||||||
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
S: ServiceFactory<Request, Config = AppConfig>,
|
||||||
|
S::Error: Into<Error>,
|
||||||
|
S::InitError: std::fmt::Debug,
|
||||||
|
S::Response: Into<Response<B>>,
|
||||||
|
B: MessageBody,
|
||||||
|
{
|
||||||
|
fn bind_ratls<A: net::ToSocketAddrs>(
|
||||||
|
self,
|
||||||
|
addr: A,
|
||||||
|
config: RaTlsConfig,
|
||||||
|
) -> Result<HttpServer<F, I, S, B>, std::io::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, I, S, B> ActixWebWithRatls<F, I, S, B> for HttpServer<F, I, S, B>
|
||||||
|
where
|
||||||
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
||||||
|
S::Error: Into<Error>,
|
||||||
|
S::InitError: std::fmt::Debug,
|
||||||
|
S::Response: Into<Response<B>>,
|
||||||
|
B: MessageBody + 'static,
|
||||||
|
{
|
||||||
|
fn bind_ratls<A: net::ToSocketAddrs>(
|
||||||
|
self,
|
||||||
|
addr: A,
|
||||||
|
config: RaTlsConfig,
|
||||||
|
) -> Result<Self, std::io::Error> {
|
||||||
|
let config = ServerConfig::from_ratls_config(config).map_err(|e|
|
||||||
|
std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e))
|
||||||
|
)?;
|
||||||
|
self.bind_rustls(addr, config)
|
||||||
|
}
|
||||||
|
}
|
4
src/http/mod.rs
Normal file
4
src/http/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[cfg(feature = "actix-web")]
|
||||||
|
pub mod actix_web;
|
||||||
|
#[cfg(feature = "reqwest")]
|
||||||
|
pub mod reqwest;
|
14
src/http/reqwest.rs
Normal file
14
src/http/reqwest.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use reqwest::ClientBuilder;
|
||||||
|
use rustls::ClientConfig;
|
||||||
|
|
||||||
|
use crate::{RaTlsConfig, RaTlsConfigBuilder};
|
||||||
|
|
||||||
|
pub trait ReqwestUseRatls {
|
||||||
|
fn use_ratls(self, config: RaTlsConfig) -> ClientBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReqwestUseRatls for ClientBuilder {
|
||||||
|
fn use_ratls(self, config: RaTlsConfig) -> ClientBuilder {
|
||||||
|
self.use_preconfigured_tls(ClientConfig::from_ratls_config(config).unwrap())
|
||||||
|
}
|
||||||
|
}
|
28
src/lib.rs
Normal file
28
src/lib.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
mod cert;
|
||||||
|
mod client;
|
||||||
|
mod config;
|
||||||
|
mod error;
|
||||||
|
mod http;
|
||||||
|
mod server;
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub use crate::config::RaTlsConfig;
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
pub use crate::config::InstanceMeasurement;
|
||||||
|
|
||||||
|
pub use crate::error::RaTlsError;
|
||||||
|
pub use occlum_sgx::SGXMeasurement;
|
||||||
|
|
||||||
|
#[cfg(feature = "actix-web")]
|
||||||
|
pub use crate::http::actix_web;
|
||||||
|
|
||||||
|
#[cfg(feature = "reqwest")]
|
||||||
|
pub use crate::http::reqwest;
|
||||||
|
|
||||||
|
pub trait RaTlsConfigBuilder<T> {
|
||||||
|
fn from_ratls_config(config: RaTlsConfig) -> Result<T, RaTlsError>;
|
||||||
|
}
|
11
src/prelude.rs
Normal file
11
src/prelude.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
pub use crate::RaTlsConfig;
|
||||||
|
pub use crate::SGXMeasurement;
|
||||||
|
|
||||||
|
#[cfg(feature = "occlum")]
|
||||||
|
pub use crate::InstanceMeasurement;
|
||||||
|
|
||||||
|
#[cfg(feature = "reqwest")]
|
||||||
|
pub use crate::reqwest::ReqwestUseRatls;
|
||||||
|
|
||||||
|
#[cfg(feature = "actix-web")]
|
||||||
|
pub use crate::actix_web::ActixWebWithRatls;
|
67
src/server.rs
Normal file
67
src/server.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use rustls::{server::{ClientCertVerified, ClientCertVerifier, ResolvesServerCert}, sign::CertifiedKey, Certificate, Error, ServerConfig, DistinguishedNames};
|
||||||
|
use std::{sync::Arc, time::SystemTime};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cert::{CertificateBuilder, RaTlsCertificate, RaTlsCertificateBuilder},
|
||||||
|
RaTlsConfig, RaTlsConfigBuilder, RaTlsError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RaTlsClientCertVerifier {
|
||||||
|
config: RaTlsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsClientCertVerifier {
|
||||||
|
pub fn new(config: RaTlsConfig) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientCertVerifier for RaTlsClientCertVerifier {
|
||||||
|
fn verify_client_cert(
|
||||||
|
&self,
|
||||||
|
end_entity: &Certificate,
|
||||||
|
_intermediates: &[Certificate],
|
||||||
|
_now: SystemTime,
|
||||||
|
) -> Result<ClientCertVerified, Error> {
|
||||||
|
end_entity.verify_quote(&self.config).map_err(|e| {
|
||||||
|
println!("{:?}", e);
|
||||||
|
rustls::Error::General(e.to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(ClientCertVerified::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_auth_root_subjects(&self) -> Option<DistinguishedNames> {
|
||||||
|
Some(DistinguishedNames::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RaTlsServerCertResolver {
|
||||||
|
cert: Arc<CertifiedKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsServerCertResolver {
|
||||||
|
pub fn new() -> Result<Self, RaTlsError> {
|
||||||
|
let builder = RaTlsCertificateBuilder::new().with_common_name("Client".to_string());
|
||||||
|
let cert = builder.build().map(Arc::new)?;
|
||||||
|
Ok(Self { cert })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvesServerCert for RaTlsServerCertResolver {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
_client_hello: rustls::server::ClientHello,
|
||||||
|
) -> Option<std::sync::Arc<CertifiedKey>> {
|
||||||
|
Some(self.cert.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaTlsConfigBuilder<ServerConfig> for ServerConfig {
|
||||||
|
fn from_ratls_config(config: RaTlsConfig) -> Result<Self, RaTlsError> {
|
||||||
|
Ok(Self::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_client_cert_verifier(Arc::new(RaTlsClientCertVerifier::new(config)))
|
||||||
|
.with_cert_resolver(Arc::new(RaTlsServerCertResolver::new()?)))
|
||||||
|
}
|
||||||
|
}
|
11
src/utils.rs
Normal file
11
src/utils.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use ring::digest::{Context, SHA512};
|
||||||
|
|
||||||
|
pub fn hash_sha512(input: Vec<u8>) -> [u8; 64] {
|
||||||
|
let mut context = Context::new(&SHA512);
|
||||||
|
context.update(input.as_ref());
|
||||||
|
let result = context.finish();
|
||||||
|
let digest = result.as_ref();
|
||||||
|
let mut output = [0u8; 64];
|
||||||
|
output.copy_from_slice(digest);
|
||||||
|
output
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user