Pārlūkot izejas kodu

Modified btmsg so it can work with any Creds.

Matthew Carr 2 gadi atpakaļ
vecāks
revīzija
fe13c99897

+ 78 - 32
crates/btlib/src/crypto.rs

@@ -968,7 +968,8 @@ impl Default for SymKeyKind {
 
 #[repr(u32)]
 #[derive(Debug, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
-pub enum KeyLen {
+pub enum BitLen {
+    Bits128 = 16,
     Bits256 = 32,
     Bits512 = 64,
     Bits2048 = 256,
@@ -976,13 +977,14 @@ pub enum KeyLen {
     Bits4096 = 512,
 }
 
-impl KeyLen {
+impl BitLen {
     const fn bits(self) -> u32 {
         8 * self as u32
     }
 
     fn try_from_u32(value: u32) -> Result<Self> {
         match value {
+            16 => Ok(Self::Bits128),
             32 => Ok(Self::Bits256),
             64 => Ok(Self::Bits512),
             256 => Ok(Self::Bits2048),
@@ -993,7 +995,7 @@ impl KeyLen {
     }
 }
 
-impl TryFrom<u32> for KeyLen {
+impl TryFrom<u32> for BitLen {
     type Error = crate::Error;
     fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
         Self::try_from_u32(value)
@@ -1012,7 +1014,7 @@ pub trait Scheme:
     fn public_from_der(self, der: &[u8]) -> Result<PKey<Public>>;
     fn private_from_der(self, der: &[u8]) -> Result<PKey<Private>>;
     fn generate(self) -> Result<AsymKeyPair<Self::Kind>>;
-    fn key_len(self) -> KeyLen;
+    fn key_len(self) -> BitLen;
 
     fn message_digest(&self) -> MessageDigest {
         self.hash_kind().into()
@@ -1066,7 +1068,7 @@ impl Scheme for Encrypt {
         }
     }
 
-    fn key_len(self) -> KeyLen {
+    fn key_len(self) -> BitLen {
         match self {
             Encrypt::RsaEsOaep(inner) => inner.key_len(),
         }
@@ -1075,12 +1077,12 @@ impl Scheme for Encrypt {
 
 impl Encrypt {
     pub const RSA_OAEP_2048_SHA_256: Encrypt = Encrypt::RsaEsOaep(RsaEsOaep {
-        key_len: KeyLen::Bits2048,
+        key_len: BitLen::Bits2048,
         hash_kind: HashKind::Sha2_256,
     });
 
     pub const RSA_OAEP_3072_SHA_256: Encrypt = Encrypt::RsaEsOaep(RsaEsOaep {
-        key_len: KeyLen::Bits3072,
+        key_len: BitLen::Bits3072,
         hash_kind: HashKind::Sha2_256,
     });
 }
@@ -1133,23 +1135,23 @@ impl Scheme for Sign {
         }
     }
 
-    fn key_len(self) -> KeyLen {
+    fn key_len(self) -> BitLen {
         self.key_len_const()
     }
 }
 
 impl Sign {
     pub const RSA_PSS_2048_SHA_256: Sign = Sign::RsaSsaPss(RsaSsaPss {
-        key_bits: KeyLen::Bits2048,
+        key_bits: BitLen::Bits2048,
         hash_kind: HashKind::Sha2_256,
     });
 
     pub const RSA_PSS_3072_SHA_256: Sign = Sign::RsaSsaPss(RsaSsaPss {
-        key_bits: KeyLen::Bits3072,
+        key_bits: BitLen::Bits3072,
         hash_kind: HashKind::Sha2_256,
     });
 
-    const fn key_len_const(self) -> KeyLen {
+    const fn key_len_const(self) -> BitLen {
         match self {
             Sign::RsaSsaPss(inner) => inner.key_bits,
         }
@@ -1175,7 +1177,7 @@ impl Rsa {
 
 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Copy)]
 pub struct RsaEsOaep {
-    key_len: KeyLen,
+    key_len: BitLen,
     hash_kind: HashKind,
 }
 
@@ -1206,7 +1208,7 @@ impl Scheme for RsaEsOaep {
         Rsa::generate(self.into())
     }
 
-    fn key_len(self) -> KeyLen {
+    fn key_len(self) -> BitLen {
         self.key_len
     }
 }
@@ -1219,7 +1221,7 @@ impl From<RsaEsOaep> for Encrypt {
 
 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Copy)]
 pub struct RsaSsaPss {
-    key_bits: KeyLen,
+    key_bits: BitLen,
     hash_kind: HashKind,
 }
 
@@ -1250,7 +1252,7 @@ impl Scheme for RsaSsaPss {
         Rsa::generate(self.into())
     }
 
-    fn key_len(self) -> KeyLen {
+    fn key_len(self) -> BitLen {
         self.key_bits
     }
 }
@@ -1454,6 +1456,10 @@ impl Signer for AsymKey<Private, Sign> {
         signer.sign(signature.as_mut_slice())?;
         Ok(signature)
     }
+
+    fn kind(&self) -> Sign {
+        self.scheme
+    }
 }
 
 impl Verifier for AsymKey<Public, Sign> {
@@ -1477,6 +1483,10 @@ impl Verifier for AsymKey<Public, Sign> {
             Err(bterr!(Error::InvalidSignature))
         }
     }
+
+    fn kind(&self) -> Sign {
+        self.scheme
+    }
 }
 
 #[derive(Clone)]
@@ -1528,6 +1538,9 @@ impl Signer for AsymKeyPair<Sign> {
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         self.private.sign(parts)
     }
+    fn kind(&self) -> Sign {
+        self.private.kind()
+    }
 }
 
 impl Verifier for AsymKeyPair<Sign> {
@@ -1540,27 +1553,31 @@ impl Verifier for AsymKeyPair<Sign> {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.public.verify(parts, signature)
     }
+
+    fn kind(&self) -> Sign {
+        self.public.kind()
+    }
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct PublicCreds {
+pub struct ConcretePub {
     sign: AsymKeyPub<Sign>,
     enc: AsymKeyPub<Encrypt>,
 }
 
-impl Principaled for PublicCreds {
+impl Principaled for ConcretePub {
     fn principal_of_kind(&self, kind: HashKind) -> Principal {
         self.sign.principal_of_kind(kind)
     }
 }
 
-impl Encrypter for PublicCreds {
+impl Encrypter for ConcretePub {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
         self.enc.encrypt(slice)
     }
 }
 
-impl Verifier for PublicCreds {
+impl Verifier for ConcretePub {
     type Op<'v> = OsslVerifyOp<'v>;
 
     fn init_verify(&self) -> Result<Self::Op<'_>> {
@@ -1570,15 +1587,19 @@ impl Verifier for PublicCreds {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
+
+    fn kind(&self) -> Sign {
+        self.sign.kind()
+    }
 }
 
-impl CredsPub for PublicCreds {
+impl CredsPub for ConcretePub {
     fn public_sign(&self) -> &AsymKey<Public, Sign> {
         &self.sign
     }
 }
 
-impl PartialEq for PublicCreds {
+impl PartialEq for ConcretePub {
     fn eq(&self, other: &Self) -> bool {
         self.principal() == other.principal()
     }
@@ -1633,6 +1654,10 @@ impl Verifier for ConcreteCreds {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
+
+    fn kind(&self) -> Sign {
+        Verifier::kind(&self.sign)
+    }
 }
 
 impl Encrypter for ConcreteCreds {
@@ -1663,6 +1688,10 @@ impl Signer for ConcreteCreds {
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         self.sign.sign(parts)
     }
+
+    fn kind(&self) -> Sign {
+        Signer::kind(&self.sign)
+    }
 }
 
 impl Decrypter for ConcreteCreds {
@@ -1677,8 +1706,6 @@ impl CredsPriv for ConcreteCreds {
     }
 }
 
-impl Creds for ConcreteCreds {}
-
 pub trait Encrypter {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>>;
 }
@@ -1825,6 +1852,8 @@ pub trait Signer {
         write_to(value, &mut *buf)?;
         self.sign(std::iter::once(buf.as_slice()))
     }
+
+    fn kind(&self) -> Sign;
 }
 
 impl<T: Signer> Signer for &T {
@@ -1837,6 +1866,10 @@ impl<T: Signer> Signer for &T {
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         (*self).sign(parts)
     }
+
+    fn kind(&self) -> Sign {
+        (*self).kind()
+    }
 }
 
 pub trait VerifyOp: Sized {
@@ -1943,6 +1976,8 @@ pub trait Verifier {
         let data = to_vec(value)?;
         self.verify(std::iter::once(data.as_slice()), signature)
     }
+
+    fn kind(&self) -> Sign;
 }
 
 impl<T: Verifier> Verifier for &T {
@@ -1955,12 +1990,20 @@ impl<T: Verifier> Verifier for &T {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         (*self).verify(parts, signature)
     }
+
+    fn kind(&self) -> Sign {
+        (*self).kind()
+    }
 }
 
 /// Trait for types which can be used as public credentials.
 pub trait CredsPub: Verifier + Encrypter + Principaled {
     /// Returns a reference to the public signing key which can be used to verify signatures.
     fn public_sign(&self) -> &AsymKey<Public, Sign>;
+
+    fn sign_kind(&self) -> Sign {
+        Verifier::kind(self)
+    }
 }
 
 impl<T: CredsPub> CredsPub for &T {
@@ -1974,6 +2017,10 @@ pub trait CredsPriv: Decrypter + Signer {
     /// Returns a reference to the writecap associated with these credentials, if one has been
     /// issued.
     fn writecap(&self) -> Option<&Writecap>;
+
+    fn sign_kind(&self) -> Sign {
+        Signer::kind(self)
+    }
 }
 
 impl<T: CredsPriv> CredsPriv for &T {
@@ -2004,19 +2051,18 @@ pub trait Creds: CredsPriv + CredsPub + Clone {
             next: self.writecap().map(|e| Box::new(e.to_owned())),
         })
     }
-}
 
-impl<C: Creds> Creds for &C {
-    fn issue_writecap(
-        &self,
-        issued_to: Principal,
-        path_components: Vec<String>,
-        expires: Epoch,
-    ) -> Result<Writecap> {
-        (*self).issue_writecap(issued_to, path_components, expires)
+    fn pub_sign_kind(&self) -> Sign {
+        CredsPub::sign_kind(self)
+    }
+
+    fn priv_sign_kind(&self) -> Sign {
+        CredsPriv::sign_kind(self)
     }
 }
 
+impl<C: CredsPriv + CredsPub + Clone> Creds for C {}
+
 /// A trait for types which store credentials.
 pub trait CredStore {
     type CredHandle: Creds;

+ 13 - 7
crates/btlib/src/crypto/tpm.rs

@@ -609,14 +609,14 @@ impl TryInto<RsaScheme> for SchemeKind {
     }
 }
 
-impl TryFrom<KeyLen> for RsaKeyBits {
+impl TryFrom<BitLen> for RsaKeyBits {
     type Error = crate::Error;
 
-    fn try_from(len: KeyLen) -> Result<RsaKeyBits> {
+    fn try_from(len: BitLen) -> Result<RsaKeyBits> {
         match len {
-            KeyLen::Bits2048 => Ok(RsaKeyBits::Rsa2048),
-            KeyLen::Bits3072 => Ok(RsaKeyBits::Rsa3072),
-            KeyLen::Bits4096 => Ok(RsaKeyBits::Rsa4096),
+            BitLen::Bits2048 => Ok(RsaKeyBits::Rsa2048),
+            BitLen::Bits3072 => Ok(RsaKeyBits::Rsa3072),
+            BitLen::Bits4096 => Ok(RsaKeyBits::Rsa4096),
             _ => Err(bterr!("unsupported key len: {len}")),
         }
     }
@@ -1359,6 +1359,10 @@ impl Verifier for TpmCreds {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.public.verify(parts, signature)
     }
+
+    fn kind(&self) -> Sign {
+        self.sign.public.kind()
+    }
 }
 
 impl Encrypter for TpmCreds {
@@ -1441,6 +1445,10 @@ impl Signer for TpmCreds {
         }
         op.finish()
     }
+
+    fn kind(&self) -> Sign {
+        self.sign.public.kind()
+    }
 }
 
 impl Decrypter for TpmCreds {
@@ -1466,8 +1474,6 @@ impl CredsPriv for TpmCreds {
     }
 }
 
-impl Creds for TpmCreds {}
-
 trait TctiNameConfExt {
     fn default() -> TctiNameConf;
 }

+ 2 - 2
crates/btlib/src/crypto/x509.rs

@@ -4,7 +4,7 @@
 
 use crate::{
     bterr,
-    crypto::{AsymKeyPub, HashKind, KeyLen, RsaSsaPss, Sha2_256, Sha2_512, Sign, Signature},
+    crypto::{AsymKeyPub, BitLen, HashKind, RsaSsaPss, Sha2_256, Sha2_512, Sign, Signature},
     BlockPath, Epoch, Principal, Principaled, Result, Writecap, WritecapBody,
 };
 use bcder::{
@@ -196,7 +196,7 @@ mod private {
         }
 
         fn from_params(sig_octet_len: u32, params: Option<&[u8]>) -> Result<Self> {
-            let key_bits = KeyLen::try_from(sig_octet_len)?;
+            let key_bits = BitLen::try_from(sig_octet_len)?;
             let hash_kind = match params {
                 Some(params) => {
                     let source = SliceSource::new(params);

+ 3 - 3
crates/btlib/src/lib.rs

@@ -41,8 +41,8 @@ use strum_macros::{Display, EnumDiscriminants, FromRepr};
 use accessor::Accessor;
 pub use block_path::{BlockPath, BlockPathError};
 use crypto::{
-    AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, HashKind,
-    MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
+    AsymKeyPub, Ciphertext, ConcretePub, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt,
+    HashKind, MerkleStream, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
 };
 use error::{BoxInIoErr, BtErr};
 pub use error::{Error, Result};
@@ -1164,7 +1164,7 @@ pub struct ServerRecord {
     /// The most up-to-date address for this server.
     addr: SocketAddr,
     /// The public credentials for this server.
-    pub_creds: PublicCreds,
+    pub_creds: ConcretePub,
 }
 
 #[derive(Debug, PartialEq, Serialize, Deserialize, EnumDiscriminants)]

+ 83 - 52
crates/btmsg/src/lib.rs

@@ -1,10 +1,7 @@
 //! Code which enables sending messages between processes in the blocktree system.
 use btlib::{
     bterr,
-    crypto::{
-        rand_array, AsymKey, ConcreteCreds, CredsPriv, CredsPub, HashKind, Private, Scheme, Sign,
-        Signer, Verifier,
-    },
+    crypto::{rand_array, Creds, CredsPriv, HashKind, Scheme, Sign, Verifier},
     error::BoxInIoErr,
     BlockPath, Principal, Result, Writecap,
 };
@@ -13,6 +10,7 @@ use bytes::{BufMut, BytesMut};
 use core::{
     future::Future,
     marker::Send,
+    ops::Deref,
     pin::Pin,
     task::{Context, Poll},
 };
@@ -26,12 +24,12 @@ use lazy_static::lazy_static;
 use log::error;
 use quinn::{ClientConfig, Endpoint, SendStream, ServerConfig};
 use rustls::{
-    client::ResolvesClientCert,
+    client::{HandshakeSignatureValid, ResolvesClientCert},
     internal::msgs::base::PayloadU16,
-    server::ClientCertVerified,
+    server::{ClientCertVerified, ResolvesServerCert},
     sign::{CertifiedKey, SigningKey},
-    Certificate, ConfigBuilder, ConfigSide, PrivateKey, SignatureAlgorithm, SignatureScheme,
-    WantsCipherSuites, WantsVerifier,
+    Certificate, ConfigBuilder, ConfigSide, SignatureAlgorithm, SignatureScheme, WantsCipherSuites,
+    WantsVerifier,
 };
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use std::{
@@ -63,7 +61,10 @@ 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: Arc<ConcreteCreds>) -> Result<impl Router> {
+    pub fn router<C: 'static + Creds + Send + Sync>(
+        addr: Arc<BlockAddr>,
+        creds: Arc<C>,
+    ) -> Result<impl Router> {
         QuicRouter::new(addr, creds)
     }
 
@@ -92,27 +93,20 @@ mod private {
             .map_err(|err| err.into())
     }
 
-    fn server_config(creds: &ConcreteCreds) -> Result<ServerConfig> {
-        let writecap = creds.writecap().ok_or(btlib::BlockError::MissingWritecap)?;
-        let chain = writecap.to_cert_chain(creds.public_sign())?;
-        let mut cert_chain = Vec::with_capacity(chain.len());
-        for cert in chain {
-            cert_chain.push(Certificate(cert))
-        }
-        let key = PrivateKey(creds.sign_pair().private().to_der()?);
+    fn server_config<C: 'static + Creds + Send + Sync>(creds: Arc<C>) -> Result<ServerConfig> {
         let server_config = common_config(rustls::ServerConfig::builder())?
             .with_client_cert_verifier(Arc::new(ClientCertVerifier))
-            .with_single_cert(cert_chain, key)?;
+            .with_cert_resolver(Arc::new(CertResolver::new(creds)?));
         Ok(ServerConfig::with_crypto(Arc::new(server_config)))
     }
 
-    fn client_config(
+    fn client_config<C: 'static + Creds + Send + Sync>(
         server_path: Arc<BlockPath>,
-        creds: Arc<ConcreteCreds>,
+        creds: Arc<C>,
     ) -> Result<ClientConfig> {
         let client_config = common_config(rustls::ClientConfig::builder())?
             .with_custom_certificate_verifier(Arc::new(ServerCertVerifier::new(server_path)))
-            .with_client_cert_resolver(Arc::new(ClientCertResolver::new(creds)?));
+            .with_client_cert_resolver(Arc::new(CertResolver::new(creds)?));
         Ok(ClientConfig::new(Arc::new(client_config)))
     }
 
@@ -120,6 +114,18 @@ mod private {
         rustls::Error::InvalidCertificateData(err.to_string())
     }
 
+    fn verify_tls13_signature(
+        message: &[u8],
+        cert: &Certificate,
+        dss: &rustls::DigitallySignedStruct,
+    ) -> std::result::Result<(), 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(())
+    }
+
     struct ServerCertVerifier {
         peer_path: Arc<BlockPath>,
     }
@@ -147,6 +153,16 @@ mod private {
                 .map_err(to_cert_err)?;
             Ok(rustls::client::ServerCertVerified::assertion())
         }
+
+        fn verify_tls13_signature(
+            &self,
+            message: &[u8],
+            cert: &Certificate,
+            dss: &rustls::DigitallySignedStruct,
+        ) -> std::result::Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
+            verify_tls13_signature(message, cert, dss)?;
+            Ok(HandshakeSignatureValid::assertion())
+        }
     }
 
     struct ClientCertVerifier;
@@ -183,28 +199,24 @@ mod private {
             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)?;
+            verify_tls13_signature(message, cert, dss)?;
             Ok(rustls::client::HandshakeSignatureValid::assertion())
         }
     }
 
-    struct ClientCertResolver {
+    struct CertResolver {
         cert_key: Arc<CertifiedKey>,
     }
 
-    impl ClientCertResolver {
-        fn new(creds: Arc<ConcreteCreds>) -> Result<Self> {
+    impl CertResolver {
+        fn new<C: Creds + Send + Sync + 'static>(creds: Arc<C>) -> 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 chain = writecap.to_cert_chain(creds.public_sign())?;
             let mut certs = Vec::with_capacity(chain.len());
             for cert in chain {
                 certs.push(Certificate(cert))
             }
-            let key = Arc::new(SignKey(creds));
+            let key = Arc::new(CredRef::new(creds));
             let cert_key = Arc::new(CertifiedKey {
                 cert: certs,
                 key,
@@ -215,7 +227,7 @@ mod private {
         }
     }
 
-    impl ResolvesClientCert for ClientCertResolver {
+    impl ResolvesClientCert for CertResolver {
         fn resolve(
             &self,
             _acceptable_issuers: &[&[u8]],
@@ -229,6 +241,15 @@ mod private {
         }
     }
 
+    impl ResolvesServerCert for CertResolver {
+        fn resolve(
+            &self,
+            _client_hello: rustls::server::ClientHello,
+        ) -> Option<Arc<rustls::sign::CertifiedKey>> {
+            Some(self.cert_key.clone())
+        }
+    }
+
     trait SignExt {
         fn as_signature_scheme(&self) -> rustls::SignatureScheme;
         fn as_signature_algorithm(&self) -> rustls::SignatureAlgorithm;
@@ -251,41 +272,51 @@ mod private {
         }
     }
 
-    struct SignKey(Arc<ConcreteCreds>);
+    /// A new type around `Arc<C>` which allows rustls' traits to be implemented.
+    struct CredRef<C> {
+        creds: Arc<C>,
+    }
+
+    impl<C> CredRef<C> {
+        fn new(creds: Arc<C>) -> Self {
+            Self { creds }
+        }
+    }
 
-    impl SignKey {
-        fn key(&self) -> &AsymKey<Private, Sign> {
-            self.0.sign_pair().private()
+    impl<C> Deref for CredRef<C> {
+        type Target = C;
+        fn deref(&self) -> &Self::Target {
+            &self.creds
         }
     }
 
-    impl SigningKey for SignKey {
+    impl<C: CredsPriv + Send + Sync + 'static> SigningKey for CredRef<C> {
         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())))
+            if offered.contains(&self.sign_kind().as_signature_scheme()) {
+                Some(Box::new(Self::new(self.creds.clone())))
             } else {
                 None
             }
         }
 
         fn algorithm(&self) -> rustls::SignatureAlgorithm {
-            self.key().scheme().as_signature_algorithm()
+            self.sign_kind().as_signature_algorithm()
         }
     }
 
-    impl rustls::sign::Signer for SignKey {
+    impl<C: CredsPriv + Send + Sync> rustls::sign::Signer for CredRef<C> {
         fn sign(&self, message: &[u8]) -> std::result::Result<Vec<u8>, rustls::Error> {
-            self.0
+            self.creds
                 .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()
+            self.sign_kind().as_signature_scheme()
         }
     }
 
@@ -652,16 +683,16 @@ mod private {
         };
     }
 
-    struct QuicRouter {
+    struct QuicRouter<C> {
         recv_addr: Arc<BlockAddr>,
-        creds: Arc<ConcreteCreds>,
+        creds: Arc<C>,
         endpoint: Endpoint,
     }
 
-    impl QuicRouter {
-        fn new(recv_addr: Arc<BlockAddr>, creds: Arc<ConcreteCreds>) -> Result<Self> {
+    impl<C: 'static + Creds + Send + Sync> QuicRouter<C> {
+        fn new(recv_addr: Arc<BlockAddr>, creds: Arc<C>) -> Result<Self> {
             let socket_addr = recv_addr.socket_addr();
-            let endpoint = Endpoint::server(server_config(creds.as_ref())?, socket_addr)?;
+            let endpoint = Endpoint::server(server_config(creds.clone())?, socket_addr)?;
             Ok(Self {
                 endpoint,
                 creds,
@@ -670,7 +701,7 @@ mod private {
         }
     }
 
-    impl Router for QuicRouter {
+    impl<C: 'static + Creds + Send + Sync> Router for QuicRouter<C> {
         type Receiver<T: 'static + DeserializeOwned + Send> = QuicReceiver<T>;
         type ReceiverFut<'a, T: 'static + DeserializeOwned + Send + Unpin> =
             Ready<Result<QuicReceiver<T>>>;
@@ -781,10 +812,10 @@ mod private {
     }
 
     impl QuicSender {
-        async fn from_endpoint(
+        async fn from_endpoint<C: 'static + Creds + Send + Sync>(
             endpoint: Endpoint,
             addr: Arc<BlockAddr>,
-            creds: Arc<ConcreteCreds>,
+            creds: Arc<C>,
         ) -> Result<Self> {
             let socket_addr = addr.socket_addr();
             let connecting = endpoint.connect_with(
@@ -838,7 +869,7 @@ mod private {
 mod tests {
     use super::*;
 
-    use btlib::{crypto::Creds, Epoch, Principal, Principaled};
+    use btlib::{crypto::ConcreteCreds, Epoch, Principal, Principaled};
     use ctor::ctor;
     use std::{
         net::Ipv6Addr,