|
@@ -1,9 +1,12 @@
|
|
|
//! Code which enables sending messages between processes in the blocktree system.
|
|
|
use btlib::{
|
|
|
bterr,
|
|
|
- crypto::{rand_array, ConcreteCreds, CredsPriv, CredsPub},
|
|
|
+ crypto::{
|
|
|
+ rand_array, AsymKey, ConcreteCreds, CredsPriv, CredsPub, HashKind, Private, Scheme, Sign,
|
|
|
+ Signer, Verifier,
|
|
|
+ },
|
|
|
error::BoxInIoErr,
|
|
|
- BlockPath, Result,
|
|
|
+ BlockPath, Principal, Result, Writecap,
|
|
|
};
|
|
|
use btserde::{read_from, write_to};
|
|
|
use bytes::{BufMut, BytesMut};
|
|
@@ -23,7 +26,12 @@ use lazy_static::lazy_static;
|
|
|
use log::error;
|
|
|
use quinn::{ClientConfig, Endpoint, SendStream, ServerConfig};
|
|
|
use rustls::{
|
|
|
- Certificate, ConfigBuilder, ConfigSide, PrivateKey, WantsCipherSuites, WantsVerifier,
|
|
|
+ client::ResolvesClientCert,
|
|
|
+ internal::msgs::base::PayloadU16,
|
|
|
+ server::ClientCertVerified,
|
|
|
+ sign::{CertifiedKey, SigningKey},
|
|
|
+ Certificate, ConfigBuilder, ConfigSide, PrivateKey, SignatureAlgorithm, SignatureScheme,
|
|
|
+ WantsCipherSuites, WantsVerifier,
|
|
|
};
|
|
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
|
|
use std::{
|
|
@@ -55,7 +63,7 @@ mod private {
|
|
|
|
|
|
/// Returns a [Router] which can be used to make a [Receiver] for the given path and
|
|
|
/// [Sender] instances for any path.
|
|
|
- pub fn router(addr: Arc<BlockAddr>, creds: &ConcreteCreds) -> Result<impl Router> {
|
|
|
+ pub fn router(addr: Arc<BlockAddr>, creds: Arc<ConcreteCreds>) -> Result<impl Router> {
|
|
|
QuicRouter::new(addr, creds)
|
|
|
}
|
|
|
|
|
@@ -91,20 +99,196 @@ mod private {
|
|
|
for cert in chain {
|
|
|
cert_chain.push(Certificate(cert))
|
|
|
}
|
|
|
- let key = PrivateKey(creds.private_sign().to_der()?);
|
|
|
+ let key = PrivateKey(creds.sign_pair().private().to_der()?);
|
|
|
let server_config = common_config(rustls::ServerConfig::builder())?
|
|
|
- .with_no_client_auth()
|
|
|
+ .with_client_cert_verifier(Arc::new(ClientCertVerifier))
|
|
|
.with_single_cert(cert_chain, key)?;
|
|
|
Ok(ServerConfig::with_crypto(Arc::new(server_config)))
|
|
|
}
|
|
|
|
|
|
- fn client_config() -> Result<ClientConfig> {
|
|
|
+ fn client_config(
|
|
|
+ server_path: Arc<BlockPath>,
|
|
|
+ creds: Arc<ConcreteCreds>,
|
|
|
+ ) -> Result<ClientConfig> {
|
|
|
let client_config = common_config(rustls::ClientConfig::builder())?
|
|
|
- .with_custom_certificate_verifier(CertVerifier::new())
|
|
|
- .with_no_client_auth();
|
|
|
+ .with_custom_certificate_verifier(Arc::new(ServerCertVerifier::new(server_path)))
|
|
|
+ .with_client_cert_resolver(Arc::new(ClientCertResolver::new(creds)?));
|
|
|
Ok(ClientConfig::new(Arc::new(client_config)))
|
|
|
}
|
|
|
|
|
|
+ fn to_cert_err(err: btlib::Error) -> rustls::Error {
|
|
|
+ rustls::Error::InvalidCertificateData(err.to_string())
|
|
|
+ }
|
|
|
+
|
|
|
+ struct ServerCertVerifier {
|
|
|
+ peer_path: Arc<BlockPath>,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ServerCertVerifier {
|
|
|
+ fn new(path: Arc<BlockPath>) -> Self {
|
|
|
+ Self { peer_path: path }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl rustls::client::ServerCertVerifier for ServerCertVerifier {
|
|
|
+ fn verify_server_cert(
|
|
|
+ &self,
|
|
|
+ end_entity: &Certificate,
|
|
|
+ intermediates: &[Certificate],
|
|
|
+ _server_name: &rustls::ServerName,
|
|
|
+ _scts: &mut dyn Iterator<Item = &[u8]>,
|
|
|
+ _ocsp_response: &[u8],
|
|
|
+ _now: std::time::SystemTime,
|
|
|
+ ) -> std::result::Result<rustls::client::ServerCertVerified, rustls::Error> {
|
|
|
+ let (writecap, ..) =
|
|
|
+ Writecap::from_cert_chain(end_entity, intermediates).map_err(to_cert_err)?;
|
|
|
+ writecap
|
|
|
+ .assert_valid_for(&self.peer_path)
|
|
|
+ .map_err(to_cert_err)?;
|
|
|
+ Ok(rustls::client::ServerCertVerified::assertion())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ struct ClientCertVerifier;
|
|
|
+
|
|
|
+ impl rustls::server::ClientCertVerifier for ClientCertVerifier {
|
|
|
+ fn verify_client_cert(
|
|
|
+ &self,
|
|
|
+ end_entity: &Certificate,
|
|
|
+ intermediates: &[Certificate],
|
|
|
+ _now: std::time::SystemTime,
|
|
|
+ ) -> std::result::Result<rustls::server::ClientCertVerified, rustls::Error> {
|
|
|
+ let (writecap, ..) =
|
|
|
+ Writecap::from_cert_chain(end_entity, intermediates).map_err(to_cert_err)?;
|
|
|
+ writecap
|
|
|
+ .assert_valid_for(writecap.path())
|
|
|
+ .map_err(to_cert_err)?;
|
|
|
+ Ok(ClientCertVerified::assertion())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn client_auth_root_subjects(&self) -> Option<rustls::DistinguishedNames> {
|
|
|
+ let der = match Principal::default().to_name_der() {
|
|
|
+ Ok(der) => der,
|
|
|
+ Err(err) => {
|
|
|
+ error!("failed to create distinguished name from root principal: {err}");
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ Some(vec![PayloadU16(der)])
|
|
|
+ }
|
|
|
+
|
|
|
+ fn verify_tls13_signature(
|
|
|
+ &self,
|
|
|
+ message: &[u8],
|
|
|
+ cert: &Certificate,
|
|
|
+ dss: &rustls::DigitallySignedStruct,
|
|
|
+ ) -> std::result::Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
|
|
|
+ let (_, subject_key) = Writecap::from_cert_chain(cert, &[]).map_err(to_cert_err)?;
|
|
|
+ subject_key
|
|
|
+ .verify(std::iter::once(message), dss.signature())
|
|
|
+ .map_err(|_| rustls::Error::InvalidCertificateSignature)?;
|
|
|
+ Ok(rustls::client::HandshakeSignatureValid::assertion())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ struct ClientCertResolver {
|
|
|
+ cert_key: Arc<CertifiedKey>,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ClientCertResolver {
|
|
|
+ fn new(creds: Arc<ConcreteCreds>) -> Result<Self> {
|
|
|
+ let writecap = creds.writecap().ok_or(btlib::BlockError::MissingWritecap)?;
|
|
|
+ let sign_pair = creds.sign_pair();
|
|
|
+ let chain = writecap.to_cert_chain(sign_pair.public())?;
|
|
|
+ let mut certs = Vec::with_capacity(chain.len());
|
|
|
+ for cert in chain {
|
|
|
+ certs.push(Certificate(cert))
|
|
|
+ }
|
|
|
+ let key = Arc::new(SignKey(creds));
|
|
|
+ let cert_key = Arc::new(CertifiedKey {
|
|
|
+ cert: certs,
|
|
|
+ key,
|
|
|
+ ocsp: None,
|
|
|
+ sct_list: None,
|
|
|
+ });
|
|
|
+ Ok(Self { cert_key })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ResolvesClientCert for ClientCertResolver {
|
|
|
+ fn resolve(
|
|
|
+ &self,
|
|
|
+ _acceptable_issuers: &[&[u8]],
|
|
|
+ _sigschemes: &[rustls::SignatureScheme],
|
|
|
+ ) -> Option<Arc<rustls::sign::CertifiedKey>> {
|
|
|
+ Some(self.cert_key.clone())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn has_certs(&self) -> bool {
|
|
|
+ true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ trait SignExt {
|
|
|
+ fn as_signature_scheme(&self) -> rustls::SignatureScheme;
|
|
|
+ fn as_signature_algorithm(&self) -> rustls::SignatureAlgorithm;
|
|
|
+ }
|
|
|
+
|
|
|
+ impl SignExt for Sign {
|
|
|
+ fn as_signature_scheme(&self) -> SignatureScheme {
|
|
|
+ match self {
|
|
|
+ Self::RsaSsaPss(scheme) => match scheme.hash_kind() {
|
|
|
+ HashKind::Sha2_256 => SignatureScheme::RSA_PSS_SHA256,
|
|
|
+ HashKind::Sha2_512 => SignatureScheme::RSA_PSS_SHA512,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn as_signature_algorithm(&self) -> SignatureAlgorithm {
|
|
|
+ match self {
|
|
|
+ Self::RsaSsaPss(..) => SignatureAlgorithm::RSA,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ struct SignKey(Arc<ConcreteCreds>);
|
|
|
+
|
|
|
+ impl SignKey {
|
|
|
+ fn key(&self) -> &AsymKey<Private, Sign> {
|
|
|
+ self.0.sign_pair().private()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl SigningKey for SignKey {
|
|
|
+ fn choose_scheme(
|
|
|
+ &self,
|
|
|
+ offered: &[rustls::SignatureScheme],
|
|
|
+ ) -> Option<Box<dyn rustls::sign::Signer>> {
|
|
|
+ if offered.contains(&self.key().scheme().as_signature_scheme()) {
|
|
|
+ Some(Box::new(SignKey(self.0.clone())))
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn algorithm(&self) -> rustls::SignatureAlgorithm {
|
|
|
+ self.key().scheme().as_signature_algorithm()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl rustls::sign::Signer for SignKey {
|
|
|
+ fn sign(&self, message: &[u8]) -> std::result::Result<Vec<u8>, rustls::Error> {
|
|
|
+ self.0
|
|
|
+ .sign(std::iter::once(message))
|
|
|
+ .map(|sig| sig.take_data())
|
|
|
+ .map_err(|err| rustls::Error::General(err.to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn scheme(&self) -> rustls::SignatureScheme {
|
|
|
+ self.key().scheme().as_signature_scheme()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// An identifier for a block. Persistent blocks (files, directories, and servers) are
|
|
|
/// identified by the `Inode` variant and transient blocks (processes) are identified by the
|
|
|
/// PID variant.
|
|
@@ -470,15 +654,17 @@ mod private {
|
|
|
|
|
|
struct QuicRouter {
|
|
|
recv_addr: Arc<BlockAddr>,
|
|
|
+ creds: Arc<ConcreteCreds>,
|
|
|
endpoint: Endpoint,
|
|
|
}
|
|
|
|
|
|
impl QuicRouter {
|
|
|
- fn new(recv_addr: Arc<BlockAddr>, creds: &ConcreteCreds) -> Result<Self> {
|
|
|
+ fn new(recv_addr: Arc<BlockAddr>, creds: Arc<ConcreteCreds>) -> Result<Self> {
|
|
|
let socket_addr = recv_addr.socket_addr();
|
|
|
- let endpoint = Endpoint::server(server_config(creds)?, socket_addr)?;
|
|
|
+ let endpoint = Endpoint::server(server_config(creds.as_ref())?, socket_addr)?;
|
|
|
Ok(Self {
|
|
|
endpoint,
|
|
|
+ creds,
|
|
|
recv_addr,
|
|
|
})
|
|
|
}
|
|
@@ -502,7 +688,9 @@ mod private {
|
|
|
type SenderFut<'a> = Pin<Box<dyn 'a + Future<Output = Result<QuicSender>> + Send>>;
|
|
|
|
|
|
fn sender(&self, addr: Arc<BlockAddr>) -> Self::SenderFut<'_> {
|
|
|
- Box::pin(async { QuicSender::from_endpoint(self.endpoint.clone(), addr).await })
|
|
|
+ Box::pin(async {
|
|
|
+ QuicSender::from_endpoint(self.endpoint.clone(), addr, self.creds.clone()).await
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -587,38 +775,24 @@ mod private {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- struct CertVerifier;
|
|
|
-
|
|
|
- impl CertVerifier {
|
|
|
- fn new() -> Arc<Self> {
|
|
|
- Arc::new(Self)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- impl rustls::client::ServerCertVerifier for CertVerifier {
|
|
|
- fn verify_server_cert(
|
|
|
- &self,
|
|
|
- _end_entity: &Certificate,
|
|
|
- _intermediates: &[Certificate],
|
|
|
- _server_name: &rustls::ServerName,
|
|
|
- _scts: &mut dyn Iterator<Item = &[u8]>,
|
|
|
- _ocsp_response: &[u8],
|
|
|
- _now: std::time::SystemTime,
|
|
|
- ) -> std::result::Result<rustls::client::ServerCertVerified, rustls::Error> {
|
|
|
- // TODO: Implement certificate verification.
|
|
|
- Ok(rustls::client::ServerCertVerified::assertion())
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
struct QuicSender {
|
|
|
addr: Arc<BlockAddr>,
|
|
|
sink: FramedWrite<SendStream, MsgEncoder>,
|
|
|
}
|
|
|
|
|
|
impl QuicSender {
|
|
|
- async fn from_endpoint(endpoint: Endpoint, addr: Arc<BlockAddr>) -> Result<Self> {
|
|
|
+ async fn from_endpoint(
|
|
|
+ endpoint: Endpoint,
|
|
|
+ addr: Arc<BlockAddr>,
|
|
|
+ creds: Arc<ConcreteCreds>,
|
|
|
+ ) -> Result<Self> {
|
|
|
let socket_addr = addr.socket_addr();
|
|
|
- let connecting = endpoint.connect_with(client_config()?, socket_addr, "localhost")?;
|
|
|
+ let connecting = endpoint.connect_with(
|
|
|
+ client_config(addr.path.clone(), creds)?,
|
|
|
+ socket_addr,
|
|
|
+ // The ServerCertVerifier ensures we connect to the correct path.
|
|
|
+ "INDETERMINANT",
|
|
|
+ )?;
|
|
|
let connection = connecting.await?;
|
|
|
let send_stream = connection.open_uni().await?;
|
|
|
let sink = FramedWrite::new(send_stream, MsgEncoder::new());
|
|
@@ -738,7 +912,7 @@ mod tests {
|
|
|
|
|
|
async fn endpoint(&self, inode: u64) -> (impl Sender, impl Receiver<BodyOwned>) {
|
|
|
let addr = Arc::new(block_addr([self.instance_num, inode].iter()));
|
|
|
- let router = router(addr.clone(), &NODE_CREDS).unwrap();
|
|
|
+ let router = router(addr.clone(), Arc::new(NODE_CREDS.clone())).unwrap();
|
|
|
let receiver = router.receiver::<BodyOwned>().await.unwrap();
|
|
|
let sender = router.sender(addr).await.unwrap();
|
|
|
(sender, receiver)
|