mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-16 09:25:04 +01:00
initial privilege separation
This commit is contained in:
parent
46f77b7f58
commit
543b1b6e76
15 changed files with 887 additions and 108 deletions
|
|
@ -1,10 +1,6 @@
|
|||
pub mod encrypt;
|
||||
|
||||
use cluelessh_keys::{
|
||||
private::{PlaintextPrivateKey, PrivateKey},
|
||||
public::PublicKey,
|
||||
signature::Signature,
|
||||
};
|
||||
use cluelessh_keys::{public::PublicKey, signature::Signature};
|
||||
use p256::ecdsa::signature::Verifier;
|
||||
use sha2::Digest;
|
||||
|
||||
|
|
@ -110,26 +106,21 @@ impl AlgorithmName for EncryptionAlgorithm {
|
|||
pub struct EncodedSshSignature(pub Vec<u8>);
|
||||
|
||||
pub struct HostKeySigningAlgorithm {
|
||||
private_key: Box<PrivateKey>,
|
||||
public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl AlgorithmName for HostKeySigningAlgorithm {
|
||||
fn name(&self) -> &'static str {
|
||||
self.private_key.algorithm_name()
|
||||
self.public_key.algorithm_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl HostKeySigningAlgorithm {
|
||||
pub fn new(private_key: PrivateKey) -> Self {
|
||||
Self {
|
||||
private_key: Box::new(private_key),
|
||||
}
|
||||
}
|
||||
pub fn sign(&self, data: &[u8]) -> Signature {
|
||||
self.private_key.sign(data)
|
||||
pub fn new(public_key: PublicKey) -> Self {
|
||||
Self { public_key }
|
||||
}
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.private_key.public_key()
|
||||
self.public_key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -253,10 +244,10 @@ pub struct SupportedAlgorithms {
|
|||
|
||||
impl SupportedAlgorithms {
|
||||
/// A secure default using elliptic curves and AEAD.
|
||||
pub fn secure(host_keys: &[PlaintextPrivateKey]) -> Self {
|
||||
pub fn secure(host_keys: &[PublicKey]) -> Self {
|
||||
let supported_host_keys = host_keys
|
||||
.iter()
|
||||
.map(|key| HostKeySigningAlgorithm::new(key.private_key.clone()))
|
||||
.map(|key| HostKeySigningAlgorithm::new(key.clone()))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ use crate::Result;
|
|||
use crate::{peer_error, Msg, SshRng, SshStatus};
|
||||
use cluelessh_format::numbers;
|
||||
use cluelessh_format::{NameList, Reader, Writer};
|
||||
use cluelessh_keys::public::PublicKey;
|
||||
use cluelessh_keys::signature::Signature;
|
||||
use tracing::{debug, info, trace};
|
||||
|
||||
// This is definitely who we are.
|
||||
|
|
@ -28,7 +30,7 @@ pub struct ServerConnection {
|
|||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ServerConfig {
|
||||
pub host_keys: Vec<cluelessh_keys::private::PlaintextPrivateKey>,
|
||||
pub host_keys: Vec<cluelessh_keys::public::PublicKey>,
|
||||
}
|
||||
|
||||
enum ServerState {
|
||||
|
|
@ -47,9 +49,21 @@ enum ServerState {
|
|||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
WaitingForSignature {
|
||||
/// h
|
||||
hash: [u8; 32],
|
||||
pub_hostkey: PublicKey,
|
||||
/// k
|
||||
shared_secret: Vec<u8>,
|
||||
server_ephemeral_public_key: Vec<u8>,
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
NewKeys {
|
||||
h: [u8; 32],
|
||||
k: Vec<u8>,
|
||||
/// h
|
||||
hash: [u8; 32],
|
||||
/// k
|
||||
shared_secret: Vec<u8>,
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
|
|
@ -242,11 +256,11 @@ impl ServerConnection {
|
|||
} => {
|
||||
let dh = KeyExchangeEcDhInitPacket::parse(&packet.payload)?;
|
||||
|
||||
let client_public_key = dh.qc;
|
||||
let client_ephemeral_public_key = dh.qc;
|
||||
|
||||
let server_secret = (kex_algorithm.generate_secret)(&mut *self.rng);
|
||||
let server_public_key = server_secret.pubkey;
|
||||
let shared_secret = (server_secret.exchange)(client_public_key)?;
|
||||
let server_ephemeral_public_key = server_secret.pubkey;
|
||||
let shared_secret = (server_secret.exchange)(client_ephemeral_public_key)?;
|
||||
let pub_hostkey = server_host_key_algorithm.public_key();
|
||||
|
||||
let hash = crypto::key_exchange_hash(
|
||||
|
|
@ -255,35 +269,31 @@ impl ServerConnection {
|
|||
client_kexinit,
|
||||
server_kexinit,
|
||||
&pub_hostkey.to_wire_encoding(),
|
||||
client_public_key,
|
||||
&server_public_key,
|
||||
client_ephemeral_public_key,
|
||||
&server_ephemeral_public_key,
|
||||
&shared_secret,
|
||||
);
|
||||
|
||||
let signature = server_host_key_algorithm.sign(&hash);
|
||||
// eprintln!("client_ephemeral_public_key: {:x?}", client_ephemeral_public_key);
|
||||
// eprintln!("server_ephemeral_public_key: {:x?}", server_ephemeral_public_key);
|
||||
// eprintln!("shared_secret: {:x?}", shared_secret);
|
||||
// eprintln!("hash: {:x?}", hash);
|
||||
|
||||
// eprintln!("client_public_key: {:x?}", client_public_key);
|
||||
// eprintln!("server_public_key: {:x?}", server_public_key);
|
||||
// eprintln!("shared_secret: {:x?}", shared_secret);
|
||||
// eprintln!("hash: {:x?}", hash);
|
||||
|
||||
let packet = Packet::new_msg_kex_ecdh_reply(
|
||||
&pub_hostkey.to_wire_encoding(),
|
||||
&server_public_key,
|
||||
&signature.to_wire_encoding(),
|
||||
);
|
||||
|
||||
self.packet_transport.queue_packet(packet);
|
||||
self.state = ServerState::NewKeys {
|
||||
h: hash,
|
||||
k: shared_secret,
|
||||
self.state = ServerState::WaitingForSignature {
|
||||
hash,
|
||||
pub_hostkey,
|
||||
shared_secret,
|
||||
server_ephemeral_public_key,
|
||||
encryption_client_to_server: *encryption_client_to_server,
|
||||
encryption_server_to_client: *encryption_server_to_client,
|
||||
};
|
||||
}
|
||||
ServerState::WaitingForSignature { .. } => {
|
||||
return Err(peer_error!("unexpected packet"));
|
||||
}
|
||||
ServerState::NewKeys {
|
||||
h,
|
||||
k,
|
||||
hash: h,
|
||||
shared_secret: k,
|
||||
encryption_client_to_server,
|
||||
encryption_server_to_client,
|
||||
} => {
|
||||
|
|
@ -344,6 +354,43 @@ impl ServerConnection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_waiting_on_signature(&self) -> Option<(&PublicKey, [u8; 32])> {
|
||||
match &self.state {
|
||||
ServerState::WaitingForSignature {
|
||||
pub_hostkey, hash, ..
|
||||
} => Some((pub_hostkey, *hash)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_signature(&mut self, signature: Signature) {
|
||||
match &self.state {
|
||||
ServerState::WaitingForSignature {
|
||||
hash,
|
||||
pub_hostkey,
|
||||
shared_secret,
|
||||
server_ephemeral_public_key,
|
||||
encryption_client_to_server,
|
||||
encryption_server_to_client,
|
||||
} => {
|
||||
let packet = Packet::new_msg_kex_ecdh_reply(
|
||||
&pub_hostkey.to_wire_encoding(),
|
||||
&server_ephemeral_public_key,
|
||||
&signature.to_wire_encoding(),
|
||||
);
|
||||
|
||||
self.packet_transport.queue_packet(packet);
|
||||
self.state = ServerState::NewKeys {
|
||||
hash: *hash,
|
||||
shared_secret: shared_secret.clone(),
|
||||
encryption_client_to_server: *encryption_client_to_server,
|
||||
encryption_server_to_client: *encryption_server_to_client,
|
||||
};
|
||||
}
|
||||
_ => unreachable!("doing signature while not waiting for it"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_msg_to_send(&mut self) -> Option<Msg> {
|
||||
self.packet_transport.next_msg_to_send()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue