From c19c42ccd7abe63a746f2ec688ba568511428285 Mon Sep 17 00:00:00 2001 From: Valentyn Faychuk Date: Tue, 13 Aug 2024 02:22:34 +0000 Subject: [PATCH] Initial implementation --- .gitignore | 4 ++ Cargo.toml | 71 +++++++++++++++++++ README.md | 12 ++++ build_client.sh | 11 +++ build_server.sh | 11 +++ client.yaml | 7 ++ examples/client.rs | 27 ++++++++ examples/server.rs | 37 ++++++++++ server.yaml | 7 ++ src/cert.rs | 157 ++++++++++++++++++++++++++++++++++++++++++ src/client.rs | 74 ++++++++++++++++++++ src/config.rs | 138 +++++++++++++++++++++++++++++++++++++ src/error.rs | 20 ++++++ src/http/actix_web.rs | 48 +++++++++++++ src/http/mod.rs | 4 ++ src/http/reqwest.rs | 14 ++++ src/lib.rs | 28 ++++++++ src/prelude.rs | 11 +++ src/server.rs | 67 ++++++++++++++++++ src/utils.rs | 11 +++ 20 files changed, 759 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100755 build_client.sh create mode 100755 build_server.sh create mode 100644 client.yaml create mode 100644 examples/client.rs create mode 100644 examples/server.rs create mode 100644 server.yaml create mode 100644 src/cert.rs create mode 100644 src/client.rs create mode 100644 src/config.rs create mode 100644 src/error.rs create mode 100644 src/http/actix_web.rs create mode 100644 src/http/mod.rs create mode 100644 src/http/reqwest.rs create mode 100644 src/lib.rs create mode 100644 src/prelude.rs create mode 100644 src/server.rs create mode 100644 src/utils.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53a7646 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +target +Cargo.lock +client_instance +server_instance diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..03a4c76 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "occlum-ratls" +version = "0.4.5" +edition = "2021" +authors = ["Ivan Chirkin "] +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"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc49c51 --- /dev/null +++ b/README.md @@ -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 +``` diff --git a/build_client.sh b/build_client.sh new file mode 100755 index 0000000..b25e08f --- /dev/null +++ b/build_client.sh @@ -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 diff --git a/build_server.sh b/build_server.sh new file mode 100755 index 0000000..6ed3db6 --- /dev/null +++ b/build_server.sh @@ -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 diff --git a/client.yaml b/client.yaml new file mode 100644 index 0000000..4e5467f --- /dev/null +++ b/client.yaml @@ -0,0 +1,7 @@ +includes: + - base.yaml +targets: + - target: /bin + copy: + - files: + - ../target/x86_64-unknown-linux-musl/debug/examples/client diff --git a/examples/client.rs b/examples/client.rs new file mode 100644 index 0000000..e318b77 --- /dev/null +++ b/examples/client.rs @@ -0,0 +1,27 @@ +use occlum_ratls::prelude::*; +use reqwest::ClientBuilder; + +#[tokio::main] +async fn main() -> Result<(), Box> { + 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(()) +} diff --git a/examples/server.rs b/examples/server.rs new file mode 100644 index 0000000..28130df --- /dev/null +++ b/examples/server.rs @@ -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 +} diff --git a/server.yaml b/server.yaml new file mode 100644 index 0000000..7a81892 --- /dev/null +++ b/server.yaml @@ -0,0 +1,7 @@ +includes: + - base.yaml +targets: + - target: /bin + copy: + - files: + - ../target/x86_64-unknown-linux-musl/debug/examples/server diff --git a/src/cert.rs b/src/cert.rs new file mode 100644 index 0000000..db92ef7 --- /dev/null +++ b/src/cert.rs @@ -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; +} + +struct RaTlsCertifiedKey { + cert_der: Vec, + key_der: Vec, +} + +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> { + 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, Box> { + Ok([0u8; 32].to_vec()) + } + + #[cfg(feature = "occlum")] + fn get_quote(&self, key_pair: &KeyPair) -> Result, Box> { + 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 { + 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>; +} + +impl RaTlsCertificate for rustls::Certificate { + #[cfg(not(feature = "occlum"))] + fn verify_quote(&self, _: &RaTlsConfig) -> Result<(), Box> { + Ok(()) + } + + #[cfg(feature = "occlum")] + fn verify_quote(&self, config: &RaTlsConfig) -> Result<(), Box> { + 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()) + } + } +} diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..db16013 --- /dev/null +++ b/src/client.rs @@ -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, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result { + end_entity + .verify_quote(&self.config) + .map_err(|e| rustls::Error::General(e.to_string()))?; + + Ok(ServerCertVerified::assertion()) + } +} + +pub struct RaTlsClientCertResolver { + cert: Arc, +} + +impl RaTlsClientCertResolver { + pub fn new() -> Result { + 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> { + Some(self.cert.clone()) + } + + fn has_certs(&self) -> bool { + true + } +} + +impl RaTlsConfigBuilder for ClientConfig { + fn from_ratls_config(config: RaTlsConfig) -> Result { + Ok(Self::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(RaTlsServerCertVerifier::new(config))) + .with_client_cert_resolver(Arc::new(RaTlsClientCertResolver::new()?))) + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..c1741fd --- /dev/null +++ b/src/config.rs @@ -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, +} + +#[cfg(feature = "occlum")] +#[derive(Default, Clone)] +pub struct InstanceMeasurement { + pub(crate) mrsigners: Option>, + pub(crate) mrenclaves: Option>, + pub(crate) product_ids: Option>, + pub(crate) versions: Option>, +} + +#[cfg(feature = "occlum")] +impl InstanceMeasurement { + pub fn new() -> Self { + Self::default() + } + + pub fn with_mrsigners(self, mrsigners: Vec) -> Self { + Self { + mrsigners: Some(mrsigners), + ..self + } + } + + pub fn with_mrenclaves(self, mrenclaves: Vec) -> Self { + Self { + mrenclaves: Some(mrenclaves), + ..self + } + } + + pub fn with_product_ids(self, product_ids: Vec) -> Self { + Self { + product_ids: Some(product_ids), + ..self + } + } + + pub fn with_versions(self, versions: Vec) -> 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::from_ratls_config(self) + } + + pub fn into_client_config(self) -> Result { + ClientConfig::from_ratls_config(self) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..4d1811f --- /dev/null +++ b/src/error.rs @@ -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 {} diff --git a/src/http/actix_web.rs b/src/http/actix_web.rs new file mode 100644 index 0000000..9401b11 --- /dev/null +++ b/src/http/actix_web.rs @@ -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 +where + F: Fn() -> I + Send + Clone + 'static, + I: IntoServiceFactory, + S: ServiceFactory, + S::Error: Into, + S::InitError: std::fmt::Debug, + S::Response: Into>, + B: MessageBody, +{ + fn bind_ratls( + self, + addr: A, + config: RaTlsConfig, + ) -> Result, std::io::Error>; +} + +impl ActixWebWithRatls for HttpServer +where + F: Fn() -> I + Send + Clone + 'static, + I: IntoServiceFactory, + S: ServiceFactory + 'static, + S::Error: Into, + S::InitError: std::fmt::Debug, + S::Response: Into>, + B: MessageBody + 'static, +{ + fn bind_ratls( + self, + addr: A, + config: RaTlsConfig, + ) -> Result { + 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) + } +} diff --git a/src/http/mod.rs b/src/http/mod.rs new file mode 100644 index 0000000..e9551a7 --- /dev/null +++ b/src/http/mod.rs @@ -0,0 +1,4 @@ +#[cfg(feature = "actix-web")] +pub mod actix_web; +#[cfg(feature = "reqwest")] +pub mod reqwest; diff --git a/src/http/reqwest.rs b/src/http/reqwest.rs new file mode 100644 index 0000000..f6443ea --- /dev/null +++ b/src/http/reqwest.rs @@ -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()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b869f32 --- /dev/null +++ b/src/lib.rs @@ -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 { + fn from_ratls_config(config: RaTlsConfig) -> Result; +} diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..cd0c335 --- /dev/null +++ b/src/prelude.rs @@ -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; diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..f0f07a4 --- /dev/null +++ b/src/server.rs @@ -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 { + 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 { + Some(DistinguishedNames::new()) + } +} + +pub struct RaTlsServerCertResolver { + cert: Arc, +} + +impl RaTlsServerCertResolver { + pub fn new() -> Result { + 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> { + Some(self.cert.clone()) + } +} + +impl RaTlsConfigBuilder for ServerConfig { + fn from_ratls_config(config: RaTlsConfig) -> Result { + Ok(Self::builder() + .with_safe_defaults() + .with_client_cert_verifier(Arc::new(RaTlsClientCertVerifier::new(config))) + .with_cert_resolver(Arc::new(RaTlsServerCertResolver::new()?))) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..241e8a9 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,11 @@ +use ring::digest::{Context, SHA512}; + +pub fn hash_sha512(input: Vec) -> [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 +}