small fixes

This commit is contained in:
nora 2024-08-30 01:38:58 +02:00
parent 185d77e94f
commit 026965bda5
19 changed files with 124 additions and 106 deletions

2
Cargo.lock generated
View file

@ -449,11 +449,13 @@ dependencies = [
"crypto-bigint",
"ctr",
"ed25519-dalek",
"hex",
"hex-literal",
"p256",
"poly1305",
"rand_core",
"secrecy",
"serde",
"sha2",
"subtle",
"tracing",

View file

@ -102,6 +102,8 @@ async fn main() -> eyre::Result<()> {
let transport_config = cluelessh_protocol::transport::server::ServerConfig {
host_keys: pub_host_keys,
// This is definitely who we are.
server_identification: b"SSH-2.0-OpenSSH_9.7\r\n".to_vec(),
};
let mut listener =

View file

@ -69,7 +69,7 @@ async fn main() -> eyre::Result<()> {
result.wrap_err("failed to prompt password")
})
}),
sign_pubkey: Arc::new(move |session_identifier| {
sign_pubkey: Arc::new(move |session_id| {
let mut attempted_public_keys = HashSet::new();
let username = username.clone();
Box::pin(async move {
@ -93,11 +93,8 @@ async fn main() -> eyre::Result<()> {
}
let pubkey = PublicKey::from_wire_encoding(&identity.key_blob)?;
let sign_data = cluelessh_keys::signature::signature_data(
session_identifier,
&username,
&pubkey,
);
let sign_data =
cluelessh_keys::signature::signature_data(session_id.0, &username, &pubkey);
let signature = agent
.sign(&identity.key_blob, &sign_data, 0)
.await

View file

@ -74,7 +74,7 @@ pub async fn verify_signature(auth: VerifySignature) -> eyre::Result<Option<User
// Verify signature...
let sign_data = cluelessh_keys::signature::signature_data(
auth.session_identifier,
auth.session_id.0,
&auth.user,
&auth.public_key,
);

View file

@ -80,7 +80,6 @@ fn default_false() -> bool {
false
}
fn addr_default() -> IpAddr {
IpAddr::V4(Ipv4Addr::UNSPECIFIED)
}

View file

@ -52,7 +52,10 @@ async fn connection_inner(state: SerializedConnectionState) -> Result<()> {
let stream = TcpStream::from_std(stream)?;
let host_keys = state.pub_host_keys;
let transport_config = cluelessh_transport::server::ServerConfig { host_keys };
let transport_config = cluelessh_transport::server::ServerConfig {
host_keys,
server_identification: b"SSH-2.0-ClueleSSH_0.1\r\n".to_vec(),
};
let rpc_client = unsafe { OwnedFd::from_raw_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD) };
let rpc_client1 = Arc::new(rpc::Client::from_fd(rpc_client)?);
@ -66,25 +69,13 @@ async fn connection_inner(state: SerializedConnectionState) -> Result<()> {
let rpc_client = rpc_client1.clone();
Box::pin(async move {
rpc_client
.verify_signature(
msg.user,
msg.session_identifier,
msg.public_key,
msg.signature,
)
.verify_signature(msg.user, msg.session_id, msg.public_key, msg.signature)
.await
})
})),
check_pubkey: Some(Arc::new(move |msg| {
let rpc_client = rpc_client2.clone();
Box::pin(async move {
rpc_client
.check_public_key(
msg.user,
msg.public_key,
)
.await
})
Box::pin(async move { rpc_client.check_public_key(msg.user, msg.public_key).await })
})),
auth_banner: config.auth.banner,
do_key_exchange: Arc::new(move |msg| {

View file

@ -14,6 +14,7 @@ use cluelessh_keys::public::PublicKey;
use cluelessh_keys::signature::Signature;
use cluelessh_protocol::auth::VerifySignature;
use cluelessh_transport::crypto::AlgorithmName;
use cluelessh_transport::SessionId;
use eyre::bail;
use eyre::ensure;
use eyre::eyre;
@ -56,7 +57,7 @@ enum Request {
/// If it is okay, store the user so we can later spawn a process as them.
VerifySignature {
user: String,
session_identifier: [u8; 32],
session_id: SessionId,
public_key: PublicKey,
signature: Signature,
},
@ -115,7 +116,7 @@ impl secrecy::DebugSecret for SerializableSharedSecret {}
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyExchangeResponse {
pub hash: [u8; 32],
pub hash: SessionId,
pub server_ephemeral_public_key: Vec<u8>,
pub shared_secret: secrecy::Secret<SerializableSharedSecret>,
pub signature: Signature,
@ -259,7 +260,7 @@ impl Server {
}
Request::VerifySignature {
user,
session_identifier,
session_id,
public_key,
signature,
} => {
@ -269,7 +270,7 @@ impl Server {
}
let is_ok = crate::auth::verify_signature(VerifySignature {
user,
session_identifier,
session_id,
public_key,
signature,
})
@ -487,13 +488,13 @@ impl Client {
pub async fn verify_signature(
&self,
user: String,
session_identifier: [u8; 32],
session_id: SessionId,
public_key: PublicKey,
signature: Signature,
) -> Result<bool> {
self.request_response::<VerifySignatureResponse>(&Request::VerifySignature {
user,
session_identifier,
session_id,
public_key,
signature,
})

View file

@ -14,7 +14,9 @@ use rustix::{
use seccompiler::{BpfProgram, SeccompAction, SeccompFilter, SeccompRule, TargetArch};
use tracing::{debug, trace, warn};
use crate::{SerializedConnectionState, PRIVSEP_CONNECTION_RPC_CLIENT_FD, PRIVSEP_CONNECTION_STREAM_FD};
use crate::{
SerializedConnectionState, PRIVSEP_CONNECTION_RPC_CLIENT_FD, PRIVSEP_CONNECTION_STREAM_FD,
};
#[tracing::instrument(skip(state), ret)]
pub fn drop_privileges(state: &SerializedConnectionState) -> Result<()> {
@ -228,12 +230,24 @@ fn seccomp() -> Result<()> {
(libc::SYS_eventfd2, vec![]),
(libc::SYS_epoll_wait, vec![]),
(libc::SYS_epoll_ctl, vec![]),
(libc::SYS_fcntl, vec![]), // todo: restrict (72)
(libc::SYS_fcntl, vec![]), // todo: restrict this
(libc::SYS_socketpair, vec![]),
(libc::SYS_sendmsg, vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],),
(libc::SYS_recvmsg, vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)]),
(libc::SYS_sendto, vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)]),
(libc::SYS_recvfrom, vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)]),
(
libc::SYS_sendmsg,
vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],
),
(
libc::SYS_recvmsg,
vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],
),
(
libc::SYS_sendto,
vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)],
),
(
libc::SYS_recvfrom,
vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)],
),
(libc::SYS_getrandom, vec![]),
(libc::SYS_rt_sigaction, vec![]),
(libc::SYS_rt_sigprocmask, vec![]),

View file

@ -153,8 +153,8 @@ impl ChannelsState {
}
pub fn recv_packet(&mut self, packet: Packet) -> Result<()> {
// TODO: window
// TODO: what if we mostly ignored window and just always increased it again?
// there's an excention to ignore it entirely that we could also support...
let mut p = packet.payload_parser();
let packet_type = p.u8()?;
match packet_type {

View file

@ -2,7 +2,6 @@ use cluelessh_format::{ParseError, Reader, Writer};
use crate::{private::PrivateKey, public::PublicKey};
// TODO SessionId newtype
pub fn signature_data(session_id: [u8; 32], username: &str, pubkey: &PublicKey) -> Vec<u8> {
let mut s = Writer::new();

View file

@ -47,11 +47,11 @@ impl ServerConnection {
self.transport.recv_bytes(bytes)?;
if let ServerConnectionState::Setup(options, auth_banner) = &mut self.state {
if let Some(session_ident) = self.transport.is_open() {
if let Some(session_id) = self.transport.is_open() {
self.state = ServerConnectionState::Auth(auth::ServerAuth::new(
mem::take(options),
auth_banner.take(),
session_ident,
session_id,
));
}
}
@ -177,9 +177,9 @@ impl ClientConnection {
self.transport.recv_bytes(bytes)?;
if let ClientConnectionState::Setup(auth) = &mut self.state {
if let Some(session_ident) = self.transport.is_open() {
if let Some(session_id) = self.transport.is_open() {
let mut auth = mem::take(auth).unwrap();
auth.set_session_identifier(session_ident);
auth.set_session_id(session_id);
debug!("Connection has been opened");
self.state = ClientConnectionState::Auth(auth);
@ -278,7 +278,7 @@ pub mod auth {
use cluelessh_format::{numbers, NameList};
use cluelessh_keys::{public::PublicKey, signature::Signature};
use cluelessh_transport::{packet::Packet, peer_error, Result};
use cluelessh_transport::{packet::Packet, peer_error, Result, SessionId};
use tracing::debug;
pub struct ServerAuth {
@ -288,7 +288,7 @@ pub mod auth {
options: HashSet<AuthOption>,
banner: Option<String>,
server_requests: VecDeque<ServerRequest>,
session_ident: [u8; 32],
session_id: SessionId,
}
pub enum ServerRequest {
@ -314,7 +314,7 @@ pub mod auth {
#[derive(Debug, Clone)]
pub struct VerifySignature {
pub user: String,
pub session_identifier: [u8; 32],
pub session_id: SessionId,
pub public_key: PublicKey,
/// The signature. Guaranteed to match the algorithm of `public_key`.
pub signature: Signature,
@ -330,14 +330,14 @@ pub mod auth {
pub fn new(
options: HashSet<AuthOption>,
banner: Option<String>,
session_ident: [u8; 32],
session_id: SessionId,
) -> Self {
Self {
has_failed: false,
packets_to_send: VecDeque::new(),
options,
is_authenticated: None,
session_ident,
session_id,
banner,
server_requests: VecDeque::new(),
}
@ -426,7 +426,7 @@ pub mod auth {
self.server_requests
.push_back(ServerRequest::VerifySignature(VerifySignature {
user: username.to_owned(),
session_identifier: self.session_ident,
session_id: self.session_id,
public_key,
signature,
}));
@ -512,12 +512,12 @@ pub mod auth {
packets_to_send: VecDeque<Packet>,
user_requests: VecDeque<ClientUserRequest>,
is_authenticated: bool,
session_identifier: Option<[u8; 32]>,
session_id: Option<SessionId>,
}
pub enum ClientUserRequest {
Password,
PrivateKeySign { session_identifier: [u8; 32] },
PrivateKeySign { session_id: SessionId },
Banner(Vec<u8>),
}
@ -533,13 +533,13 @@ pub mod auth {
username,
user_requests: VecDeque::new(),
is_authenticated: false,
session_identifier: None,
session_id: None,
}
}
pub fn set_session_identifier(&mut self, ident: [u8; 32]) {
assert!(self.session_identifier.is_none());
self.session_identifier = Some(ident);
pub fn set_session_id(&mut self, session_id: SessionId) {
assert!(self.session_id.is_none());
self.session_id = Some(session_id);
}
pub fn is_authenticated(&self) -> bool {
@ -605,9 +605,9 @@ pub mod auth {
// TODO: Ask the server whether there are any keys we can use instead of just yoloing the signature.
self.user_requests
.push_back(ClientUserRequest::PrivateKeySign {
session_identifier: self
.session_identifier
.expect("set_session_identifier has not been called"),
session_id: self
.session_id
.expect("set_session_id has not been called"),
});
} else {
return Err(peer_error!(

View file

@ -1,4 +1,5 @@
use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation};
use cluelessh_transport::SessionId;
use std::{collections::HashMap, pin::Pin, sync::Arc};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
@ -31,7 +32,7 @@ pub struct ClientAuth {
pub username: String,
pub prompt_password: Arc<dyn Fn() -> BoxFuture<'static, Result<String>> + Send + Sync>,
pub sign_pubkey:
Arc<dyn Fn([u8; 32]) -> BoxFuture<'static, Result<SignatureResult>> + Send + Sync>,
Arc<dyn Fn(SessionId) -> BoxFuture<'static, Result<SignatureResult>> + Send + Sync>,
}
enum Operation {
@ -59,9 +60,7 @@ impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
channel_ops_recv,
channels: HashMap::new(),
proto: cluelessh_protocol::ClientConnection::new(
cluelessh_transport::client::ClientConnection::new(
cluelessh_protocol::OsRng,
),
cluelessh_transport::client::ClientConnection::new(cluelessh_protocol::OsRng),
cluelessh_protocol::auth::ClientAuth::new(auth.username.as_bytes().to_vec()),
),
auth,
@ -88,13 +87,11 @@ impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
let _ = send.send(Operation::PasswordEntered(password)).await;
});
}
cluelessh_protocol::auth::ClientUserRequest::PrivateKeySign {
session_identifier,
} => {
cluelessh_protocol::auth::ClientUserRequest::PrivateKeySign { session_id } => {
let send = self.operations_send.clone();
let sign_pubkey = self.auth.sign_pubkey.clone();
tokio::spawn(async move {
let signature_result = sign_pubkey(session_identifier).await;
let signature_result = sign_pubkey(session_id).await;
let _ = send.send(Operation::Signature(signature_result)).await;
});
}

View file

@ -213,10 +213,7 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
tokio::spawn(async move {
let result = check(check_pubkey.clone()).await;
let _ = send
.send(Operation::CheckPubkey(
result,
check_pubkey.public_key,
))
.send(Operation::CheckPubkey(result, check_pubkey.public_key))
.await;
});
}

View file

@ -22,6 +22,8 @@ x25519-dalek = "2.0.1"
tracing.workspace = true
base64 = "0.22.1"
secrecy = "0.8.0"
hex = "0.4.3"
serde = { version = "1.0.209", features = ["derive"] }
[dev-dependencies]
hex-literal = "0.4.1"

View file

@ -4,10 +4,11 @@ use tracing::{debug, info, trace};
use crate::{
crypto::{
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeyVerifyAlgorithm, KeyExchangeSecret, SharedSecret, SupportedAlgorithms
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeyVerifyAlgorithm,
KeyExchangeSecret, SharedSecret, SupportedAlgorithms,
},
packet::{Packet, PacketTransport, ProtocolIdentParser},
peer_error, Msg, Result, SshRng, SshStatus,
peer_error, Msg, Result, SessionId, SshRng, SshStatus,
};
use cluelessh_format::{numbers, NameList, Reader, Writer};
@ -50,10 +51,10 @@ enum ClientState {
encryption_server_to_client: EncryptionAlgorithm,
},
ServiceRequest {
session_identifier: [u8; 32],
session_id: SessionId,
},
Open {
session_identifier: [u8; 32],
session_id: SessionId,
},
}
@ -310,10 +311,10 @@ impl ClientConnection {
.queue_packet(Packet::new_msg_service_request(b"ssh-userauth"));
self.state = ClientState::ServiceRequest {
session_identifier: *h,
session_id: SessionId(*h),
};
}
ClientState::ServiceRequest { session_identifier } => {
ClientState::ServiceRequest { session_id } => {
let mut accept = packet.payload_parser();
let packet_type = accept.u8()?;
if packet_type != numbers::SSH_MSG_SERVICE_ACCEPT {
@ -326,7 +327,7 @@ impl ClientConnection {
debug!("Connection has been opened successfully");
self.state = ClientState::Open {
session_identifier: *session_identifier,
session_id: *session_id,
};
}
ClientState::Open { .. } => {
@ -349,9 +350,9 @@ impl ClientConnection {
self.packet_transport.queue_packet(packet);
}
pub fn is_open(&self) -> Option<[u8; 32]> {
pub fn is_open(&self) -> Option<SessionId> {
match self.state {
ClientState::Open { session_identifier } => Some(session_identifier),
ClientState::Open { session_id } => Some(session_id),
_ => None,
}
}

View file

@ -7,7 +7,7 @@ use sha2::Digest;
use crate::{
packet::{EncryptedPacket, MsgKind, Packet, RawPacket},
peer_error, Msg, Result, SshRng,
peer_error, Msg, Result, SessionId, SshRng,
};
pub type SharedSecret = secrecy::Secret<SharedSecretInner>;
@ -308,7 +308,7 @@ impl SupportedAlgorithms {
}
pub(crate) struct Session {
session_id: [u8; 32],
session_id: SessionId,
from_peer: Tunnel,
to_peer: Tunnel,
}
@ -326,6 +326,7 @@ pub(crate) trait Keys: Send + Sync + 'static {
fn encrypt_packet_to_msg(&mut self, packet: Packet, packet_number: u64) -> Msg;
fn additional_mac_len(&self) -> usize;
// TODO: actually rekey...
fn rekey(
&mut self,
h: [u8; 32],
@ -362,7 +363,7 @@ impl Keys for Plaintext {
impl Session {
pub(crate) fn new(
h: [u8; 32],
h: SessionId,
k: &SharedSecret,
encryption_client_to_server: EncryptionAlgorithm,
encryption_server_to_client: EncryptionAlgorithm,
@ -370,7 +371,7 @@ impl Session {
) -> Self {
Self::from_keys(
h,
h,
h.0,
k,
encryption_client_to_server,
encryption_server_to_client,
@ -380,7 +381,7 @@ impl Session {
/// <https://datatracker.ietf.org/doc/html/rfc4253#section-7.2>
fn from_keys(
session_id: [u8; 32],
session_id: SessionId,
h: [u8; 32],
k: &SharedSecret,
alg_c2s: EncryptionAlgorithm,
@ -462,7 +463,7 @@ fn derive_key(
k: &SharedSecret,
h: [u8; 32],
letter: &str,
session_id: [u8; 32],
session_id: SessionId,
key_size: usize,
) -> Vec<u8> {
let sha2len = sha2::Sha256::output_size();
@ -476,7 +477,7 @@ fn derive_key(
if i == 0 {
hash.update(letter.as_bytes());
hash.update(session_id);
hash.update(session_id.0);
} else {
hash.update(&output[..(i * sha2len)]);
}

View file

@ -3,8 +3,13 @@ pub mod crypto;
pub mod packet;
pub mod server;
use std::fmt::Debug;
use cluelessh_format::ParseError;
pub use packet::Msg;
use serde::{Deserialize, Serialize};
// TODO: extensions
#[derive(Debug)]
pub enum SshStatus {
@ -17,6 +22,17 @@ pub enum SshStatus {
PeerError(String),
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct SessionId(pub [u8; 32]);
impl Debug for SessionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SessionId")
.field(&hex::encode(self.0))
.finish()
}
}
pub type Result<T, E = SshStatus> = std::result::Result<T, E>;
impl From<ParseError> for SshStatus {

View file

@ -6,8 +6,8 @@ use std::mem;
use tracing::{debug, trace};
use crate::crypto::{self, EncryptionAlgorithm, Keys, Plaintext, Session, SharedSecret};
use crate::peer_error;
use crate::Result;
use crate::{peer_error, SessionId};
use cluelessh_format::numbers;
use cluelessh_format::{NameList, Reader, Writer};
@ -67,7 +67,8 @@ impl PacketTransport {
}
fn recv_bytes_step(&mut self, bytes: &[u8]) -> Result<Option<usize>> {
// TODO: This might not work if we buffer two packets where one changes keys in between?
// This would not work if we buffer two packets where one changes keys in between,
// but SSH_MSG_NEWKEYS messages guarantee that this cannot happen.
let result =
self.recv_next_packet
@ -125,7 +126,7 @@ impl PacketTransport {
is_server,
) {
self.keys = Box::new(Session::new(
h,
SessionId(h),
k,
encryption_client_to_server,
encryption_server_to_client,

View file

@ -7,18 +7,14 @@ use crate::crypto::{
use crate::packet::{
KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport, ProtocolIdentParser,
};
use crate::Result;
use crate::{peer_error, Msg, SshRng, SshStatus};
use crate::{Result, SessionId};
use cluelessh_format::numbers;
use cluelessh_format::{NameList, Reader, Writer};
use cluelessh_keys::private::PlaintextPrivateKey;
use cluelessh_keys::signature::Signature;
use tracing::{debug, info, trace};
// This is definitely who we are.
// TODO: dont make cluelesshd do this
pub const SERVER_IDENTIFICATION: &[u8] = b"SSH-2.0-OpenSSH_9.7\r\n";
pub struct ServerConnection {
state: ServerState,
packet_transport: PacketTransport,
@ -31,6 +27,7 @@ pub struct ServerConnection {
#[derive(Debug, Clone, Default)]
pub struct ServerConfig {
pub server_identification: Vec<u8>,
pub host_keys: Vec<cluelessh_keys::public::PublicKey>,
}
@ -69,10 +66,10 @@ enum ServerState {
encryption_server_to_client: EncryptionAlgorithm,
},
ServiceRequest {
session_ident: [u8; 32],
session_id: SessionId,
},
Open {
session_ident: [u8; 32],
session_id: SessionId,
},
}
@ -87,7 +84,7 @@ pub struct KeyExchangeParameters {
}
pub struct KeyExchangeResponse {
pub hash: [u8; 32],
pub hash: SessionId,
pub server_ephemeral_public_key: Vec<u8>,
pub shared_secret: SharedSecret,
pub signature: Signature,
@ -111,7 +108,7 @@ impl ServerConnection {
ident_parser.recv_bytes(bytes);
if let Some(client_identification) = ident_parser.get_peer_ident() {
self.packet_transport
.queue_send_protocol_info(SERVER_IDENTIFICATION.to_vec());
.queue_send_protocol_info(self.config.server_identification.clone());
self.state = ServerState::KeyExchangeInit {
client_identification,
};
@ -311,10 +308,11 @@ impl ServerConnection {
*encryption_server_to_client,
true,
);
self.state = ServerState::ServiceRequest { session_ident: *h };
self.state = ServerState::ServiceRequest {
session_id: SessionId(*h),
};
}
ServerState::ServiceRequest { session_ident } => {
// TODO: this should probably move out of here? unsure.
ServerState::ServiceRequest { session_id } => {
if packet.payload.first() != Some(&numbers::SSH_MSG_SERVICE_REQUEST) {
return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST"));
}
@ -335,7 +333,7 @@ impl ServerConnection {
},
});
self.state = ServerState::Open {
session_ident: *session_ident,
session_id: *session_id,
};
}
ServerState::Open { .. } => {
@ -346,9 +344,9 @@ impl ServerConnection {
Ok(())
}
pub fn is_open(&self) -> Option<[u8; 32]> {
pub fn is_open(&self) -> Option<SessionId> {
match self.state {
ServerState::Open { session_ident } => Some(session_ident),
ServerState::Open { session_id } => Some(session_id),
_ => None,
}
}
@ -365,7 +363,7 @@ impl ServerConnection {
..
} => Some(KeyExchangeParameters {
client_ident: client_identification.clone(),
server_ident: SERVER_IDENTIFICATION.to_vec(),
server_ident: self.config.server_identification.to_vec(),
client_kexinit: client_kexinit.clone(),
server_kexinit: server_kexinit.clone(),
eph_client_public_key: client_ephemeral_public_key.clone(),
@ -392,7 +390,7 @@ impl ServerConnection {
self.packet_transport.queue_packet(packet);
self.state = ServerState::NewKeys {
hash: response.hash,
hash: response.hash.0,
shared_secret: response.shared_secret.clone(),
encryption_client_to_server: *encryption_client_to_server,
encryption_server_to_client: *encryption_server_to_client,
@ -437,7 +435,7 @@ pub fn do_key_exchange(
);
Ok(KeyExchangeResponse {
hash,
hash: SessionId(hash),
server_ephemeral_public_key,
shared_secret,
signature: private.private_key.sign(&hash),