From a698c64fe1daaad88d447f5ac4378dbe3068bc84 Mon Sep 17 00:00:00 2001 From: Matt Johnston <matt@ucc.asn.au> Date: Sun, 21 Aug 2022 11:33:37 +0800 Subject: [PATCH] work in progress on make_kexdhreply --- async/src/async_door.rs | 3 +++ sshproto/src/conn.rs | 2 +- sshproto/src/kex.rs | 27 +++++++++++++++++---------- sshproto/src/sign.rs | 11 +++++++++-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/async/src/async_door.rs b/async/src/async_door.rs index df4419f..c3bc33f 100644 --- a/async/src/async_door.rs +++ b/async/src/async_door.rs @@ -59,6 +59,9 @@ impl<'a> AsyncDoor<'a> { AsyncDoorSocket::new(self) } + /// The `f` closure should return `Some` if the result should be returned + /// from `progress()`, or `None` to not do that. + /// XXX better docs, perhaps it won't take a closure anyway pub async fn progress<F, R>(&mut self, b: &mut Behaviour<'_>, f: F) diff --git a/sshproto/src/conn.rs b/sshproto/src/conn.rs index da464d5..556f8e9 100644 --- a/sshproto/src/conn.rs +++ b/sshproto/src/conn.rs @@ -242,7 +242,7 @@ impl<'a> Conn<'a> { let kex = core::mem::replace(&mut self.kex, kex::Kex::new()?); *output = Some(kex.handle_kexdhinit(&p, &self.sess_id)?); - let reply = output.as_ref().trap()?.make_kexdhreply(&b.server()?)?; + let reply = output.as_mut().trap()?.make_kexdhreply(&mut b.server()?).await?; resp.push(reply.into()).trap()?; } } diff --git a/sshproto/src/kex.rs b/sshproto/src/kex.rs index 59b9e18..cc6db09 100644 --- a/sshproto/src/kex.rs +++ b/sshproto/src/kex.rs @@ -445,7 +445,7 @@ impl SharedSecret { SharedSecret::KexCurve25519(ref k) => { let pubkey: salty::agreement::PublicKey = k.ours.as_ref().trap()?.into(); let mut kex_out = KexCurve25519::secret(&mut algos, p.q_c.0, kex_hash, sess_id)?; - kex_out.pubkey = Some(pubkey.to_bytes()); + kex_out.kex_pub = Some(pubkey.to_bytes()); kex_out } }; @@ -467,14 +467,17 @@ pub(crate) struct KexOutput { // storage for kex packet reply content that outlives Kex // in make_kexdhreply(). - // TODO: this should become generic for other kex methods - pubkey: Option<[u8; 32]>, + /// ephemeral public key octet string + kex_pub: Option<[u8; 32]>, + // the negotiated signature type + sig_type: SigType, + sig: Option<sign::OwnedSig>, } impl fmt::Debug for KexOutput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KexOutput") - .field("pubkey", &self.pubkey.is_some()) + .field("kex_pub", &self.kex_pub.is_some()) .finish_non_exhaustive() } } @@ -488,15 +491,19 @@ impl<'a> KexOutput { let sess_id = sess_id.as_ref().unwrap_or(&h); let keys = Keys::new_from(k, &h, &sess_id, algos)?; - Ok(KexOutput { h, keys, pubkey: None }) + Ok(KexOutput { h, keys, kex_pub: None, sig_type: algos.hostsig, sig: None }) } // server only - pub fn make_kexdhreply(&'a self, b: &ServBehaviour) -> Result<Packet<'a>> { - let q_s = BinString(self.pubkey.as_ref().trap()?); - // TODO real signature - let k_s = Blob(PubKey::Ed25519(packets::Ed25519PubKey{ key: BinString(&[]) })); - let sig = Blob(Signature::Ed25519(packets::Ed25519Sig{ sig: BinString(&[]) })); + pub async fn make_kexdhreply<'b>(&'a mut self, b: &'a mut ServBehaviour<'_>) -> Result<Packet<'a>> { + let q_s = BinString(self.kex_pub.as_ref().trap()?); + + // hostkeys list must contain the signature type + let key = b.hostkeys().await?.iter().find(|k| k.can_sign(&self.sig_type)).trap()?; + let k_s = Blob(key.pubkey()); + self.sig = Some(key.sign(&self.h.as_slice(), None)?); + let sig: Signature = self.sig.as_ref().unwrap().into(); + let sig = Blob(sig); Ok(packets::KexDHReply { k_s, q_s, sig }.into()) } } diff --git a/sshproto/src/sign.rs b/sshproto/src/sign.rs index 638499f..7ec846c 100644 --- a/sshproto/src/sign.rs +++ b/sshproto/src/sign.rs @@ -20,7 +20,7 @@ use core::mem::discriminant; // RSA requires alloc. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum SigType { Ed25519, RSA256, @@ -137,7 +137,14 @@ impl SignKey { k.try_into() } - pub(crate) fn sign_encode<'s>(&self, msg: &'s impl SSHEncode, parse_ctx: Option<&ParseContext>) -> Result<OwnedSig> { + /// Returns whether this `SignKey` can create a given signature type + pub(crate) fn can_sign(&self, sig_type: &SigType) -> bool { + match self { + SignKey::Ed25519(_) => matches!(sig_type, SigType::Ed25519), + } + } + + pub(crate) fn sign<'s>(&self, msg: &'s impl SSHEncode, parse_ctx: Option<&ParseContext>) -> Result<OwnedSig> { match self { SignKey::Ed25519(k) => { k.sign_parts(|h| { -- GitLab