diff options
| -rw-r--r-- | oxish-proto/src/key_exchange.rs | 24 | ||||
| -rw-r--r-- | oxish/src/lib.rs | 21 | ||||
| -rw-r--r-- | oxish/src/server.rs | 34 | ||||
| -rw-r--r-- | oxish/src/session/mod.rs | 21 |
4 files changed, 56 insertions, 44 deletions
diff --git a/oxish-proto/src/key_exchange.rs b/oxish-proto/src/key_exchange.rs index e2acb98..eaa75e4 100644 --- a/oxish-proto/src/key_exchange.rs +++ b/oxish-proto/src/key_exchange.rs @@ -433,11 +433,11 @@ impl EcdhKeyExchangeReply { host_key: &dyn SigningKey, provider: &dyn CryptoProvider, ) -> Result<(Self, Digest, KeySourceSet), CryptoError> { - let KeyExchangeOutput { + let KeyExchangeStarted { shared_secret, exchange_hash, reply, - } = KeyExchangeOutput::new( + } = KeyExchangeStarted::new( exchange, ecdh_key_exchange_init.client_ephemeral_public_key, negotiated, @@ -584,13 +584,13 @@ impl SessionHostKey { } } -struct KeyExchangeOutput { +struct KeyExchangeStarted { shared_secret: SharedSecret, exchange_hash: Digest, reply: EcdhKeyExchangeReply, } -impl KeyExchangeOutput { +impl KeyExchangeStarted { fn new( mut exchange: HandshakeHash, client_ephemeral_public_key: &[u8], @@ -933,6 +933,22 @@ impl Encode for ExtInfo<'_> { } } +/// Output of the initial key exchange +pub struct KeyExchangeOutput<'a> { + /// The identities exchanged after connection acceptance + pub identities: Identities, + /// The host key used for the connection + pub host_key: ServerHostKey<'a>, + /// The strict key exchange state, if negotiated + pub strict_kx: Option<StrictKeyExchange>, + /// The session ID for the connection + pub session_id: Digest, + /// The keys derived for the connection + pub keys: KeySourceSet, + /// Whether post-quantum key exchange was negotiated + pub post_quantum_kx: bool, +} + /// The raw hashes from which we will derive the crypto keys. /// /// See <https://www.rfc-editor.org/rfc/rfc4253#section-7.2>. diff --git a/oxish/src/lib.rs b/oxish/src/lib.rs index 7219e96..8e389a6 100644 --- a/oxish/src/lib.rs +++ b/oxish/src/lib.rs @@ -20,8 +20,8 @@ use proto::{ KeySourceSide, }, key_exchange::{ - EcdhKeyExchangeInit, HostKeys, Identities, KeyExchange, KeySourceSet, NewKeys, Rekey, - ServerHostKey, SessionHostKey, StrictKeyExchange, + EcdhKeyExchangeInit, HostKeys, Identities, KeyExchange, KeyExchangeOutput, KeySourceSet, + NewKeys, Rekey, ServerHostKey, SessionHostKey, StrictKeyExchange, }, named::{EncryptionAlgorithm, ExtensionId}, }; @@ -70,14 +70,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Connection<T> { &mut self, host_keys: &'h HostKeys, provider: &dyn CryptoProvider, - ) -> anyhow::Result<( - Identities, - ServerHostKey<'h>, - Option<StrictKeyExchange>, - Digest, - KeySourceSet, - bool, - )> { + ) -> anyhow::Result<KeyExchangeOutput<'h>> { let (exchange, identities) = self.identify().await.context("identification failed")?; // Receive and send key exchange init packets @@ -98,7 +91,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Connection<T> { let packet = receive(&mut self.stream, &mut self.read).await?; let ecdh_key_exchange_init = EcdhKeyExchangeInit::try_from(packet)?; - let post_quantum_kex = kx.negotiated.key_exchange.post_quantum_secure(); + let post_quantum_kx = kx.negotiated.key_exchange.post_quantum_secure(); let (host_key, key_exchange_reply, session_id, keys) = kx .complete(ecdh_key_exchange_init, host_keys, provider) .context("key exchange failed")?; @@ -112,14 +105,14 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Connection<T> { } self.send(&Ignore::default()).await?; - Ok(( + Ok(KeyExchangeOutput { identities, host_key, strict_kx, session_id, keys, - post_quantum_kex, - )) + post_quantum_kx, + }) } /// Complete a client-initiated rekey after its `SSH_MSG_KEXINIT` has been parsed diff --git a/oxish/src/server.rs b/oxish/src/server.rs index 1389640..30f62f2 100644 --- a/oxish/src/server.rs +++ b/oxish/src/server.rs @@ -21,7 +21,7 @@ use anyhow::Context as _; use proto::{ Encode, ReadState, WriteState, crypto::CryptoProvider, - key_exchange::{HostKeys, Rekey, ServerHostKey, SessionHostKey}, + key_exchange::{HostKeys, ServerHostKey}, }; use rustix::net::{ RecvAncillaryBuffer, RecvFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, @@ -126,24 +126,18 @@ impl Server { }; let future = conn.exchange_keys(&self.host_keys, self.provider); - let (identities, host_key, strict_kx, session_id, keys, post_quantum_kx) = - match timeout(Duration::from_secs(30), future).await { - Ok(result) => result.context("key exchange failed")?, - Err(_) => return Err(anyhow::anyhow!("key exchange timed out")), - }; + let kx = match timeout(Duration::from_secs(30), future).await { + Ok(result) => result.context("key exchange failed")?, + Err(_) => return Err(anyhow::anyhow!("key exchange timed out")), + }; - let user = authenticate(&session_id, &mut conn, &*self.store, self.provider) + let user = authenticate(&kx.session_id, &mut conn, &*self.store, self.provider) .await .context("authentication failed")?; drop(authenticating); if !self.config.spawn { - let host_key = SessionHostKey::from_server(host_key, self.provider)?; - let session = Session::new( - conn, - Rekey::new(session_id, strict_kx, identities, host_key), - post_quantum_kx, - ); + let session = Session::new(kx, conn, self.provider)?; return session.run(self.provider).await.context("session failed"); } @@ -168,18 +162,18 @@ impl Server { let state = SessionState { addr, - host_key, - identities, - post_quantum_kx, - strict_kx, - session_id, + host_key: kx.host_key, + identities: kx.identities, + post_quantum_kx: kx.post_quantum_kx, + strict_kx: kx.strict_kx, + session_id: kx.session_id, read: SideState { - source: keys.client_to_server, + source: kx.keys.client_to_server, counter: read.opener.as_ref().map_or(0, |opener| opener.counter()), sequence_number: read.sequence_number, }, write: SideState { - source: keys.server_to_client, + source: kx.keys.server_to_client, counter: write.sealer.as_ref().map_or(0, |sealer| sealer.counter()), sequence_number: write.sequence_number, }, diff --git a/oxish/src/session/mod.rs b/oxish/src/session/mod.rs index ae4a45c..26a1899 100644 --- a/oxish/src/session/mod.rs +++ b/oxish/src/session/mod.rs @@ -23,7 +23,7 @@ use tokio::{ use tracing::{debug, info, instrument, trace, warn}; use zeroize::Zeroizing; -use crate::{Connection, DEFAULT_PROVIDER, Error, SessionState, receive, send}; +use crate::{Connection, DEFAULT_PROVIDER, Error, KeyExchangeOutput, SessionState, receive, send}; mod connections; use connections::{Channels, IncomingChannelMessage, TerminalsFuture}; @@ -166,13 +166,22 @@ impl Session<TcpStream> { } impl<T: AsyncRead + AsyncWrite + Unpin> Session<T> { - pub(crate) fn new(conn: Connection<T>, rekey: Rekey, post_quantum_kx: bool) -> Self { - Self { + pub(crate) fn new( + kx: KeyExchangeOutput<'_>, + conn: Connection<T>, + provider: &dyn CryptoProvider, + ) -> Result<Self, Error> { + Ok(Self { conn, channels: Channels::default(), - rekey, - post_quantum_kx, - } + rekey: Rekey::new( + kx.session_id, + kx.strict_kx, + kx.identities, + SessionHostKey::from_server(kx.host_key, provider)?, + ), + post_quantum_kx: kx.post_quantum_kx, + }) } /// Run the session, driving the connection forward and handling channel messages |
