mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 16:35:06 +01:00
refactor
This commit is contained in:
parent
d5794d3ef0
commit
ca4213ba81
7 changed files with 130 additions and 111 deletions
|
|
@ -157,7 +157,7 @@ pub(crate) fn generate_private_key(params: KeyGenerationParams) -> PrivateKey {
|
|||
|
||||
PrivateKey::Ed25519 {
|
||||
public_key: private_key.verifying_key(),
|
||||
private_key: private_key.to_bytes(),
|
||||
private_key,
|
||||
}
|
||||
}
|
||||
KeyType::Ecdsa => {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl Debug for PlaintextPrivateKey {
|
|||
pub enum PrivateKey {
|
||||
Ed25519 {
|
||||
public_key: ed25519_dalek::VerifyingKey,
|
||||
private_key: [u8; 32], // TODO: store a signing key!
|
||||
private_key: ed25519_dalek::SigningKey,
|
||||
},
|
||||
EcdsaSha2NistP256 {
|
||||
public_key: p256::ecdsa::VerifyingKey,
|
||||
|
|
@ -326,9 +326,9 @@ impl PlaintextPrivateKey {
|
|||
// <https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent#name-eddsa-keys>
|
||||
enc.string(b"ssh-ed25519");
|
||||
enc.string(public_key);
|
||||
let combined = private_key.len() + public_key.as_bytes().len();
|
||||
let combined = private_key.as_bytes().len() + public_key.as_bytes().len();
|
||||
enc.u32(combined as u32);
|
||||
enc.raw(private_key);
|
||||
enc.raw(private_key.as_bytes());
|
||||
enc.raw(public_key.as_bytes());
|
||||
}
|
||||
PrivateKey::EcdsaSha2NistP256 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use cluelessh_format::Writer;
|
||||
|
||||
use crate::public::PublicKey;
|
||||
use crate::{private::PrivateKey, public::PublicKey};
|
||||
|
||||
// TODO SessionId newtype
|
||||
pub fn signature_data(session_id: [u8; 32], username: &str, pubkey: &PublicKey) -> Vec<u8> {
|
||||
|
|
@ -17,3 +17,58 @@ pub fn signature_data(session_id: [u8; 32], username: &str, pubkey: &PublicKey)
|
|||
|
||||
s.finish()
|
||||
}
|
||||
|
||||
pub enum Signature {
|
||||
Ed25519 { signature: ed25519_dalek::Signature },
|
||||
EcdsaSha2NistP256 { signature: p256::ecdsa::Signature },
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
pub fn to_wire_encoding(&self) -> Vec<u8> {
|
||||
let mut data = Writer::new();
|
||||
data.string(self.algorithm_name());
|
||||
match self {
|
||||
Self::Ed25519 { signature } => {
|
||||
// <https://datatracker.ietf.org/doc/html/rfc8709#name-signature-format>
|
||||
data.string(signature.to_bytes());
|
||||
}
|
||||
Self::EcdsaSha2NistP256 { signature } => {
|
||||
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1.2>
|
||||
|
||||
let (r, s) = signature.split_scalars();
|
||||
|
||||
let mut signature_blob = Writer::new();
|
||||
signature_blob.mpint(p256::U256::from(r.as_ref()));
|
||||
signature_blob.mpint(p256::U256::from(s.as_ref()));
|
||||
data.string(signature_blob.finish());
|
||||
}
|
||||
}
|
||||
data.finish()
|
||||
}
|
||||
|
||||
pub fn algorithm_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Ed25519 { .. } => "ssh-ed25519",
|
||||
Self::EcdsaSha2NistP256 { .. } => "ecdsa-sha2-nistp256",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn sign(&self, data: &[u8]) -> Signature {
|
||||
match self {
|
||||
Self::Ed25519 { private_key, .. } => {
|
||||
use ed25519_dalek::Signer;
|
||||
|
||||
let sig = private_key.sign(data);
|
||||
Signature::Ed25519 { signature: sig }
|
||||
}
|
||||
Self::EcdsaSha2NistP256 { private_key, .. } => {
|
||||
use p256::ecdsa::signature::Signer;
|
||||
|
||||
let sig = private_key.sign(data);
|
||||
Signature::EcdsaSha2NistP256 { signature: sig }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use tracing::{debug, info, trace};
|
|||
|
||||
use crate::{
|
||||
crypto::{
|
||||
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeySigningAlgorithm,
|
||||
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeyVerifyAlgorithm,
|
||||
KeyExchangeSecret, SupportedAlgorithms,
|
||||
},
|
||||
packet::{Packet, PacketTransport, ProtocolIdentParser},
|
||||
|
|
@ -36,7 +36,7 @@ enum ClientState {
|
|||
client_ident: Vec<u8>,
|
||||
server_ident: Vec<u8>,
|
||||
kex_secret: Option<KeyExchangeSecret>,
|
||||
server_hostkey_algorithm: HostKeySigningAlgorithm,
|
||||
server_hostkey_algorithm: HostKeyVerifyAlgorithm,
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
client_kexinit: Vec<u8>,
|
||||
|
|
@ -165,7 +165,7 @@ impl ClientConnection {
|
|||
|
||||
let server_hostkey_algorithm = kexinit.name_list()?;
|
||||
let server_hostkey_algorithm =
|
||||
sup_algs.hostkey.find(server_hostkey_algorithm.0)?;
|
||||
sup_algs.hostkey_verify.find(server_hostkey_algorithm.0)?;
|
||||
debug!(name = %server_hostkey_algorithm.name(), "Using host key algorithm");
|
||||
|
||||
let encryption_algorithms_client_to_server = kexinit.name_list()?;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
pub mod encrypt;
|
||||
|
||||
use cluelessh_format::{Reader, Writer};
|
||||
use cluelessh_format::Reader;
|
||||
use cluelessh_keys::{
|
||||
private::{PlaintextPrivateKey, PrivateKey},
|
||||
public::PublicKey,
|
||||
signature::Signature,
|
||||
};
|
||||
use p256::ecdsa::signature::Signer;
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -110,108 +110,73 @@ impl AlgorithmName for EncryptionAlgorithm {
|
|||
pub struct EncodedSshSignature(pub Vec<u8>);
|
||||
|
||||
pub struct HostKeySigningAlgorithm {
|
||||
private_key: PrivateKey,
|
||||
}
|
||||
|
||||
impl AlgorithmName for HostKeySigningAlgorithm {
|
||||
fn name(&self) -> &'static str {
|
||||
self.private_key.algorithm_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl HostKeySigningAlgorithm {
|
||||
pub fn new(private_key: PrivateKey) -> Self {
|
||||
Self { private_key }
|
||||
}
|
||||
pub fn sign(&self, data: &[u8]) -> Signature {
|
||||
self.private_key.sign(data)
|
||||
}
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.private_key.public_key()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HostKeyVerifyAlgorithm {
|
||||
name: &'static str,
|
||||
hostkey_private: Vec<u8>,
|
||||
public_key: fn(private_key: &[u8]) -> PublicKey,
|
||||
sign: fn(private_key: &[u8], data: &[u8]) -> EncodedSshSignature,
|
||||
pub verify:
|
||||
fn(public_key: &[u8], message: &[u8], signature: &EncodedSshSignature) -> Result<()>,
|
||||
}
|
||||
|
||||
impl AlgorithmName for HostKeySigningAlgorithm {
|
||||
impl AlgorithmName for HostKeyVerifyAlgorithm {
|
||||
fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl HostKeySigningAlgorithm {
|
||||
pub fn sign(&self, data: &[u8]) -> EncodedSshSignature {
|
||||
(self.sign)(&self.hostkey_private, data)
|
||||
}
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
(self.public_key)(&self.hostkey_private)
|
||||
}
|
||||
}
|
||||
const HOSTKEY_VERIFY_ED25519: HostKeyVerifyAlgorithm = HostKeyVerifyAlgorithm {
|
||||
name: "ssh-ed25519",
|
||||
verify: |public_key, message, signature| {
|
||||
// Parse out public key
|
||||
let mut public_key = Reader::new(public_key);
|
||||
let public_key_alg = public_key.string()?;
|
||||
if public_key_alg != b"ssh-ed25519" {
|
||||
return Err(peer_error!("incorrect algorithm public host key"));
|
||||
}
|
||||
let public_key = public_key.string()?;
|
||||
let Ok(public_key) = public_key.try_into() else {
|
||||
return Err(peer_error!("incorrect length for public host key"));
|
||||
};
|
||||
let public_key = ed25519_dalek::VerifyingKey::from_bytes(public_key)
|
||||
.map_err(|err| peer_error!("incorrect public host key: {err}"))?;
|
||||
|
||||
pub fn hostkey_ed25519(hostkey_private: Vec<u8>) -> HostKeySigningAlgorithm {
|
||||
HostKeySigningAlgorithm {
|
||||
name: "ssh-ed25519",
|
||||
hostkey_private,
|
||||
public_key: |key| {
|
||||
let key = ed25519_dalek::SigningKey::from_bytes(key.try_into().unwrap());
|
||||
let public_key = key.verifying_key();
|
||||
PublicKey::Ed25519 { public_key }
|
||||
},
|
||||
sign: |key, data| {
|
||||
let key = ed25519_dalek::SigningKey::from_bytes(key.try_into().unwrap());
|
||||
let signature = key.sign(data);
|
||||
// Parse out signature
|
||||
let mut signature = Reader::new(&signature.0);
|
||||
let alg = signature.string()?;
|
||||
if alg != b"ssh-ed25519" {
|
||||
return Err(peer_error!("incorrect algorithm for signature"));
|
||||
}
|
||||
let signature = signature.string()?;
|
||||
let Ok(signature) = signature.try_into() else {
|
||||
return Err(peer_error!("incorrect length for signature"));
|
||||
};
|
||||
let signature = ed25519_dalek::Signature::from_bytes(signature);
|
||||
|
||||
// <https://datatracker.ietf.org/doc/html/rfc8709#section-6>
|
||||
let mut data = Writer::new();
|
||||
data.string(b"ssh-ed25519");
|
||||
data.string(signature.to_bytes());
|
||||
EncodedSshSignature(data.finish())
|
||||
},
|
||||
verify: |public_key, message, signature| {
|
||||
// Parse out public key
|
||||
let mut public_key = Reader::new(public_key);
|
||||
let public_key_alg = public_key.string()?;
|
||||
if public_key_alg != b"ssh-ed25519" {
|
||||
return Err(peer_error!("incorrect algorithm public host key"));
|
||||
}
|
||||
let public_key = public_key.string()?;
|
||||
let Ok(public_key) = public_key.try_into() else {
|
||||
return Err(peer_error!("incorrect length for public host key"));
|
||||
};
|
||||
let public_key = ed25519_dalek::VerifyingKey::from_bytes(public_key)
|
||||
.map_err(|err| peer_error!("incorrect public host key: {err}"))?;
|
||||
|
||||
// Parse out signature
|
||||
let mut signature = Reader::new(&signature.0);
|
||||
let alg = signature.string()?;
|
||||
if alg != b"ssh-ed25519" {
|
||||
return Err(peer_error!("incorrect algorithm for signature"));
|
||||
}
|
||||
let signature = signature.string()?;
|
||||
let Ok(signature) = signature.try_into() else {
|
||||
return Err(peer_error!("incorrect length for signature"));
|
||||
};
|
||||
let signature = ed25519_dalek::Signature::from_bytes(signature);
|
||||
|
||||
// Verify
|
||||
public_key
|
||||
.verify_strict(message, &signature)
|
||||
.map_err(|err| peer_error!("incorrect signature: {err}"))
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn hostkey_ecdsa_sha2_p256(hostkey_private: Vec<u8>) -> HostKeySigningAlgorithm {
|
||||
HostKeySigningAlgorithm {
|
||||
name: "ecdsa-sha2-nistp256",
|
||||
hostkey_private,
|
||||
public_key: |key| {
|
||||
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
||||
PublicKey::EcdsaSha2NistP256 {
|
||||
public_key: *key.verifying_key(),
|
||||
}
|
||||
},
|
||||
sign: |key, data| {
|
||||
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
||||
let signature: p256::ecdsa::Signature = key.sign(data);
|
||||
let (r, s) = signature.split_scalars();
|
||||
|
||||
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1.2>
|
||||
let mut data = Writer::new();
|
||||
data.string(b"ecdsa-sha2-nistp256");
|
||||
let mut signature_blob = Writer::new();
|
||||
signature_blob.mpint(p256::U256::from(r.as_ref()));
|
||||
signature_blob.mpint(p256::U256::from(s.as_ref()));
|
||||
data.string(signature_blob.finish());
|
||||
EncodedSshSignature(data.finish())
|
||||
},
|
||||
verify: |_public_key, _message, _signature| todo!("ecdsa p256 verification"),
|
||||
}
|
||||
}
|
||||
// Verify
|
||||
public_key
|
||||
.verify_strict(message, &signature)
|
||||
.map_err(|err| peer_error!("incorrect signature: {err}"))
|
||||
},
|
||||
};
|
||||
|
||||
pub struct AlgorithmNegotiation<T> {
|
||||
pub supported: Vec<T>,
|
||||
|
|
@ -244,7 +209,8 @@ impl<T: AlgorithmName> AlgorithmNegotiation<T> {
|
|||
|
||||
pub struct SupportedAlgorithms {
|
||||
pub key_exchange: AlgorithmNegotiation<KexAlgorithm>,
|
||||
pub hostkey: AlgorithmNegotiation<HostKeySigningAlgorithm>,
|
||||
pub hostkey_sign: AlgorithmNegotiation<HostKeySigningAlgorithm>,
|
||||
pub hostkey_verify: AlgorithmNegotiation<HostKeyVerifyAlgorithm>,
|
||||
pub encryption_to_peer: AlgorithmNegotiation<EncryptionAlgorithm>,
|
||||
pub encryption_from_peer: AlgorithmNegotiation<EncryptionAlgorithm>,
|
||||
pub mac_to_peer: AlgorithmNegotiation<&'static str>,
|
||||
|
|
@ -258,21 +224,19 @@ impl SupportedAlgorithms {
|
|||
pub fn secure(host_keys: &[PlaintextPrivateKey]) -> Self {
|
||||
let supported_host_keys = host_keys
|
||||
.iter()
|
||||
.map(|key| match &key.private_key {
|
||||
PrivateKey::Ed25519 { private_key, .. } => hostkey_ed25519(private_key.to_vec()),
|
||||
PrivateKey::EcdsaSha2NistP256 { private_key, .. } => {
|
||||
hostkey_ecdsa_sha2_p256(private_key.to_bytes().to_vec())
|
||||
}
|
||||
})
|
||||
.map(|key| HostKeySigningAlgorithm::new(key.private_key.clone()))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
key_exchange: AlgorithmNegotiation {
|
||||
supported: vec![KEX_CURVE_25519_SHA256, KEX_ECDH_SHA2_NISTP256],
|
||||
},
|
||||
hostkey: AlgorithmNegotiation {
|
||||
hostkey_sign: AlgorithmNegotiation {
|
||||
supported: supported_host_keys,
|
||||
},
|
||||
hostkey_verify: AlgorithmNegotiation {
|
||||
supported: vec![HOSTKEY_VERIFY_ED25519], // TODO: p256
|
||||
},
|
||||
encryption_to_peer: AlgorithmNegotiation {
|
||||
supported: vec![encrypt::CHACHA20POLY1305, encrypt::AES256_GCM],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ impl ServerConnection {
|
|||
debug!(name = %kex_algorithm.name(), "Using KEX algorithm");
|
||||
|
||||
let server_host_key_algorithm =
|
||||
sup_algs.hostkey.find(kex.server_host_key_algorithms.0)?;
|
||||
sup_algs.hostkey_sign.find(kex.server_host_key_algorithms.0)?;
|
||||
debug!(name = %server_host_key_algorithm.name(), "Using host key algorithm");
|
||||
|
||||
// TODO: Implement aes128-ctr
|
||||
|
|
@ -268,7 +268,7 @@ impl ServerConnection {
|
|||
let packet = Packet::new_msg_kex_ecdh_reply(
|
||||
&pub_hostkey.to_wire_encoding(),
|
||||
&server_public_key,
|
||||
&signature.0,
|
||||
&signature.to_wire_encoding(),
|
||||
);
|
||||
|
||||
self.packet_transport.queue_packet(packet);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue