From 49a37937703dd4ba3cb8b7e21a7fb4c3b734e50b Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:29:13 +0200 Subject: [PATCH] prepare for different signing alg --- ssh-transport/Cargo.toml | 2 +- ssh-transport/src/crypto.rs | 38 +++++++++++++++++++ ssh-transport/src/lib.rs | 74 +++++++++++++++++++++++-------------- 3 files changed, 85 insertions(+), 29 deletions(-) diff --git a/ssh-transport/Cargo.toml b/ssh-transport/Cargo.toml index 3d8de75..9118844 100644 --- a/ssh-transport/Cargo.toml +++ b/ssh-transport/Cargo.toml @@ -8,7 +8,7 @@ aes-gcm = "0.10.3" chacha20 = "0.9.1" ed25519-dalek = { version = "2.1.1" } eyre = "0.6.12" -p256 = { version = "0.13.2", features = ["ecdh"] } +p256 = { version = "0.13.2", features = ["ecdh", "ecdsa"] } poly1305 = "0.8.0" rand = "0.8.5" rand_core = "0.6.4" diff --git a/ssh-transport/src/crypto.rs b/ssh-transport/src/crypto.rs index 70326e9..a068bff 100644 --- a/ssh-transport/src/crypto.rs +++ b/ssh-transport/src/crypto.rs @@ -1,5 +1,6 @@ use aes_gcm::aead::AeadMutInPlace; use chacha20::cipher::{KeyInit, StreamCipher, StreamCipherSeek}; +use p256::ecdsa::signature::{Signer}; use sha2::Digest; use subtle::ConstantTimeEq; @@ -128,6 +129,43 @@ pub const ENC_AES256_GCM: EncryptionAlgorithm = EncryptionAlgorithm { }, }; +pub struct HostKeySigningAlgorithm { + name: &'static str, + hostkey_private: Vec, + public_key: fn(private_key: &[u8]) -> Vec, + sign: fn(private_key: &[u8], data: &[u8]) -> Vec, +} + +impl AlgorithmName for HostKeySigningAlgorithm { + fn name(&self) -> &'static str { + self.name + } +} + +impl HostKeySigningAlgorithm { + pub fn sign(&self, data: &[u8]) -> Vec { + (self.sign)(&self.hostkey_private, data) + } + pub fn public_key(&self) -> Vec { + (self.public_key)(&self.hostkey_private) + } +} + +pub fn hostkey_ed25519(hostkey_private: Vec) -> HostKeySigningAlgorithm { + HostKeySigningAlgorithm { + name: "ssh-ed25519", + hostkey_private, + public_key: |key| { + let key = ed25519_dalek::SigningKey::from_bytes(key.try_into().unwrap()); + key.verifying_key().as_bytes().to_vec() + }, + sign: |key, data| { + let key = ed25519_dalek::SigningKey::from_bytes(key.try_into().unwrap()); + key.sign(data).to_vec() + }, + } +} + pub struct AlgorithmNegotiation { pub supported: Vec, } diff --git a/ssh-transport/src/lib.rs b/ssh-transport/src/lib.rs index 0f0957b..94e048e 100644 --- a/ssh-transport/src/lib.rs +++ b/ssh-transport/src/lib.rs @@ -6,7 +6,7 @@ pub mod parse; use core::str; use std::{collections::VecDeque, mem::take}; -use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm}; +use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm, HostKeySigningAlgorithm}; use ed25519_dalek::ed25519::signature::Signer; use packet::{ KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport, SshPublicKey, @@ -65,6 +65,7 @@ enum ServerState { client_kexinit: Vec, server_kexinit: Vec, kex_algorithm: crypto::KexAlgorithm, + server_host_key_algorithm: HostKeySigningAlgorithm, encryption_client_to_server: EncryptionAlgorithm, encryption_server_to_client: EncryptionAlgorithm, }, @@ -198,8 +199,12 @@ impl ServerConnection { debug!(name = %kex_algorithm.name(), "Using KEX algorithm"); // TODO: support ecdsa-sha2-nistp256 + let hostkey_algorithms = AlgorithmNegotiation { + supported: vec![crypto::hostkey_ed25519(ED25519_PRIVKEY_BYTES.to_vec())], + }; + let server_host_key_algorithm = - require_algorithm("ssh-ed25519", kex.server_host_key_algorithms)?; + hostkey_algorithms.find(kex.server_host_key_algorithms.0)?; let encryption_algorithms_client_to_server = AlgorithmNegotiation { supported: vec![crypto::ENC_CHACHA20POLY1305, crypto::ENC_AES256_GCM], @@ -237,7 +242,7 @@ impl ServerConnection { let server_kexinit = KeyExchangeInitPacket { cookie: [0; 16], kex_algorithms: NameList::one(kex_algorithm.name()), - server_host_key_algorithms: NameList::one(server_host_key_algorithm), + server_host_key_algorithms: NameList::one(server_host_key_algorithm.name()), encryption_algorithms_client_to_server: NameList::one( encryption_client_to_server.name(), ), @@ -271,6 +276,7 @@ impl ServerConnection { client_kexinit: packet.payload, server_kexinit: server_kexinit_payload, kex_algorithm, + server_host_key_algorithm, encryption_client_to_server, encryption_server_to_client, }; @@ -280,6 +286,7 @@ impl ServerConnection { client_kexinit, server_kexinit, kex_algorithm, + server_host_key_algorithm, encryption_client_to_server, encryption_server_to_client, } => { @@ -292,9 +299,22 @@ impl ServerConnection { shared_secret, } = (kex_algorithm.exchange)(client_public_key, &mut *self.rng)?; + /*let hostkey = + p256::ecdsa::SigningKey::random(&mut SshRngRandAdapter(&mut *self.rng)); + + eprintln!( + "{}", + hostkey + .to_bytes() + .iter() + .map(|b| format!("0x{b:x}")) + .collect::>() + .join(", ") + );*/ + let pub_hostkey = SshPublicKey { - format: b"ssh-ed25519", - data: PUB_HOSTKEY_BYTES, + format: server_host_key_algorithm.name().as_bytes(), + data: &server_host_key_algorithm.public_key(), }; let mut hash = sha2::Sha256::new(); @@ -330,10 +350,7 @@ impl ServerConnection { let hash = hash.finalize(); - let host_priv_key = ed25519_dalek::SigningKey::from_bytes(PRIVKEY_BYTES); - assert_eq!(PUB_HOSTKEY_BYTES, host_priv_key.verifying_key().as_bytes()); - - let signature = host_priv_key.sign(&hash); + let signature = server_host_key_algorithm.sign(&hash); // eprintln!("client_public_key: {:x?}", client_public_key.0); // eprintln!("server_public_key: {:x?}", server_public_key.as_bytes()); @@ -344,8 +361,8 @@ impl ServerConnection { &pub_hostkey.to_bytes(), &server_public_key, &SshSignature { - format: b"ssh-ed25519", - data: &signature.to_bytes(), + format: server_host_key_algorithm.name().as_bytes(), + data: &signature, } .to_bytes(), ); @@ -423,28 +440,29 @@ impl ServerConnection { } } -// hardcoded test keys. lol. -const _PUBKEY: &str = - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOk5zfpvwNc3MztTTpE90zLI1Ref4AwwRVdSFyJLGbj2 testkey"; -/// Manually extracted, even worse, , help -const PUB_HOSTKEY_BYTES: &[u8; 32] = &[ - 0xe9, 0x39, 0xcd, 0xfa, 0x6f, 0xc0, 0xd7, 0x37, 0x33, 0x3b, 0x53, 0x4e, 0x91, 0x3d, 0xd3, 0x32, - 0xc8, 0xd5, 0x17, 0x9f, 0xe0, 0x0c, 0x30, 0x45, 0x57, 0x52, 0x17, 0x22, 0x4b, 0x19, 0xb8, 0xf6, -]; -const _PRIVKEY: &str = "-----BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW -QyNTUxOQAAACDpOc36b8DXNzM7U06RPdMyyNUXn+AMMEVXUhciSxm49gAAAJDpgLSk6YC0 -pAAAAAtzc2gtZWQyNTUxOQAAACDpOc36b8DXNzM7U06RPdMyyNUXn+AMMEVXUhciSxm49g -AAAECSeskxuEtJrr9L7ZkbpogXC5pKRNVHx1ueMX2h1XUnmek5zfpvwNc3MztTTpE90zLI -1Ref4AwwRVdSFyJLGbj2AAAAB3Rlc3RrZXkBAgMEBQY= ------END OPENSSH PRIVATE KEY----- -"; /// Manually extracted from the key using , probably wrong -const PRIVKEY_BYTES: &[u8; 32] = &[ +/// ```text +/// ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOk5zfpvwNc3MztTTpE90zLI1Ref4AwwRVdSFyJLGbj2 testkey +/// ``` +/// ```text +/// -----BEGIN OPENSSH PRIVATE KEY----- +/// b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +/// QyNTUxOQAAACDpOc36b8DXNzM7U06RPdMyyNUXn+AMMEVXUhciSxm49gAAAJDpgLSk6YC0 +/// pAAAAAtzc2gtZWQyNTUxOQAAACDpOc36b8DXNzM7U06RPdMyyNUXn+AMMEVXUhciSxm49g +/// AAAECSeskxuEtJrr9L7ZkbpogXC5pKRNVHx1ueMX2h1XUnmek5zfpvwNc3MztTTpE90zLI +/// 1Ref4AwwRVdSFyJLGbj2AAAAB3Rlc3RrZXkBAgMEBQY= +/// -----END OPENSSH PRIVATE KEY----- +/// ``` +const ED25519_PRIVKEY_BYTES: &[u8; 32] = &[ 0x92, 0x7a, 0xc9, 0x31, 0xb8, 0x4b, 0x49, 0xae, 0xbf, 0x4b, 0xed, 0x99, 0x1b, 0xa6, 0x88, 0x17, 0x0b, 0x9a, 0x4a, 0x44, 0xd5, 0x47, 0xc7, 0x5b, 0x9e, 0x31, 0x7d, 0xa1, 0xd5, 0x75, 0x27, 0x99, ]; +const ECDSA_P256_PRIVKEY_BYTES: &[u8; 32] = &[ + 0x89, 0xdd, 0x0c, 0x96, 0x22, 0x85, 0x10, 0xec, 0x3c, 0xa4, 0xa1, 0xb8, 0xac, 0x2a, 0x77, 0xa8, + 0xd4, 0x4d, 0xcb, 0x9d, 0x90, 0x25, 0xc6, 0xd8, 0x3a, 0x02, 0x74, 0x4f, 0x9e, 0x44, 0xcd, 0xa3, +]; + #[macro_export] macro_rules! client_error { ($($tt:tt)*) => {