mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-15 17:05:05 +01:00
ECDSA!
This commit is contained in:
parent
982de53668
commit
a8d25d856f
6 changed files with 49 additions and 84 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -928,6 +928,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"chacha20",
|
"chacha20",
|
||||||
|
"crypto-bigint",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"eyre",
|
"eyre",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes-gcm = "0.10.3"
|
aes-gcm = "0.10.3"
|
||||||
chacha20 = "0.9.1"
|
chacha20 = "0.9.1"
|
||||||
|
crypto-bigint = "0.5.5"
|
||||||
ed25519-dalek = { version = "2.1.1" }
|
ed25519-dalek = { version = "2.1.1" }
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
p256 = { version = "0.13.2", features = ["ecdh", "ecdsa"] }
|
p256 = { version = "0.13.2", features = ["ecdh", "ecdsa"] }
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use subtle::ConstantTimeEq;
|
||||||
use crate::{
|
use crate::{
|
||||||
client_error,
|
client_error,
|
||||||
packet::{EncryptedPacket, MsgKind, Packet, RawPacket},
|
packet::{EncryptedPacket, MsgKind, Packet, RawPacket},
|
||||||
parse::Writer,
|
parse::{self, Writer},
|
||||||
Msg, Result, SshRng,
|
Msg, Result, SshRng,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -187,17 +187,30 @@ pub fn hostkey_ecdsa_sha2_p256(hostkey_private: Vec<u8>) -> HostKeySigningAlgori
|
||||||
hostkey_private,
|
hostkey_private,
|
||||||
public_key: |key| {
|
public_key: |key| {
|
||||||
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
||||||
key.verifying_key()
|
let public_key = key.verifying_key();
|
||||||
.to_encoded_point(true)
|
let mut data = Writer::new();
|
||||||
.as_bytes()
|
|
||||||
.to_vec();
|
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1>
|
||||||
todo!()
|
data.string(b"ecdsa-sha2-nistp256");
|
||||||
|
data.string(b"nistp256");
|
||||||
|
// > point compression MAY be used.
|
||||||
|
// But OpenSSH does not appear to support that, so let's NOT use it.
|
||||||
|
data.string(public_key.to_encoded_point(false).as_bytes());
|
||||||
|
EncodedSshPublicHostKey(data.finish())
|
||||||
},
|
},
|
||||||
sign: |key, data| {
|
sign: |key, data| {
|
||||||
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
|
||||||
let signature: p256::ecdsa::Signature = key.sign(data);
|
let signature: p256::ecdsa::Signature = key.sign(data);
|
||||||
signature.to_vec();
|
let (r, s) = signature.split_scalars();
|
||||||
todo!()
|
|
||||||
|
// <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())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -404,12 +417,8 @@ fn derive_key(
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn encode_mpint_for_hash(mut key: &[u8], mut add_to_hash: impl FnMut(&[u8])) {
|
pub(crate) fn encode_mpint_for_hash(key: &[u8], mut add_to_hash: impl FnMut(&[u8])) {
|
||||||
while key[0] == 0 {
|
let (key, pad_zero) = parse::fixup_mpint(key);
|
||||||
key = &key[1..];
|
|
||||||
}
|
|
||||||
// If the first high bit is set, pad it with a zero.
|
|
||||||
let pad_zero = (key[0] & 0b10000000) > 1;
|
|
||||||
add_to_hash(&u32::to_be_bytes((key.len() + (pad_zero as usize)) as u32));
|
add_to_hash(&u32::to_be_bytes((key.len() + (pad_zero as usize)) as u32));
|
||||||
if pad_zero {
|
if pad_zero {
|
||||||
add_to_hash(&[0]);
|
add_to_hash(&[0]);
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ use core::str;
|
||||||
use std::{collections::VecDeque, mem::take};
|
use std::{collections::VecDeque, mem::take};
|
||||||
|
|
||||||
use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm, HostKeySigningAlgorithm};
|
use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm, HostKeySigningAlgorithm};
|
||||||
use packet::{
|
use packet::{KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport};
|
||||||
KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport, SshPublicKey,
|
|
||||||
SshSignature,
|
|
||||||
};
|
|
||||||
use parse::{NameList, Parser, Writer};
|
use parse::{NameList, Parser, Writer};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
|
|
@ -199,7 +196,7 @@ impl ServerConnection {
|
||||||
|
|
||||||
let hostkey_algorithms = AlgorithmNegotiation {
|
let hostkey_algorithms = AlgorithmNegotiation {
|
||||||
supported: vec![
|
supported: vec![
|
||||||
//crypto::hostkey_ed25519(ED25519_PRIVKEY_BYTES.to_vec()),
|
crypto::hostkey_ed25519(ED25519_PRIVKEY_BYTES.to_vec()),
|
||||||
crypto::hostkey_ecdsa_sha2_p256(ECDSA_P256_PRIVKEY_BYTES.to_vec()),
|
crypto::hostkey_ecdsa_sha2_p256(ECDSA_P256_PRIVKEY_BYTES.to_vec()),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -318,69 +318,6 @@ impl<'a> KeyExchangeEcDhInitPacket<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SshPublicKey<'a> {
|
|
||||||
pub(crate) format: &'a [u8],
|
|
||||||
pub(crate) data: &'a [u8],
|
|
||||||
}
|
|
||||||
impl SshPublicKey<'_> {
|
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut data = Writer::new();
|
|
||||||
// ed25519-specific!
|
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc8709#section-4>
|
|
||||||
data.string(self.format);
|
|
||||||
data.string(self.data);
|
|
||||||
data.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SshPublicKeyEcdsa<'a> {
|
|
||||||
pub(crate) format: &'a [u8],
|
|
||||||
pub(crate) ident: &'a [u8],
|
|
||||||
pub(crate) data: &'a [u8],
|
|
||||||
}
|
|
||||||
impl SshPublicKeyEcdsa<'_> {
|
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut data = Writer::new();
|
|
||||||
// ECDSA-specific!
|
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1>
|
|
||||||
data.string(self.format);
|
|
||||||
data.string(self.ident);
|
|
||||||
data.string(self.data);
|
|
||||||
data.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SshSignature<'a> {
|
|
||||||
pub(crate) format: &'a [u8],
|
|
||||||
pub(crate) data: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SshSignature<'_> {
|
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut data = Writer::new();
|
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc8709#section-6>
|
|
||||||
data.string(self.format);
|
|
||||||
data.string(self.data);
|
|
||||||
data.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct SshSignatureEcdsa<'a> {
|
|
||||||
pub(crate) format: &'a [u8],
|
|
||||||
pub(crate) data: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SshSignatureEcdsa<'_> {
|
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let mut data = Writer::new();
|
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1.2>
|
|
||||||
data.string(self.format);
|
|
||||||
data.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct RawPacket {
|
pub(crate) struct RawPacket {
|
||||||
pub mac_len: usize,
|
pub mac_len: usize,
|
||||||
pub raw: Vec<u8>,
|
pub raw: Vec<u8>,
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,18 @@ impl Writer {
|
||||||
self.string(list.0.as_bytes());
|
self.string(list.0.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mpint(&mut self, _mpint: MpInt<'_>) {
|
pub fn mpint<const LIMBS: usize>(&mut self, uint: crypto_bigint::Uint<LIMBS>)
|
||||||
todo!("implement correctly?")
|
where
|
||||||
|
crypto_bigint::Uint<LIMBS>: crypto_bigint::ArrayEncoding,
|
||||||
|
{
|
||||||
|
let bytes = crypto_bigint::ArrayEncoding::to_be_byte_array(&uint);
|
||||||
|
let (bytes, pad_zero) = fixup_mpint(&bytes);
|
||||||
|
let len = bytes.len() + (pad_zero as usize);
|
||||||
|
self.u32(len as u32);
|
||||||
|
if pad_zero {
|
||||||
|
self.u8(0);
|
||||||
|
}
|
||||||
|
self.write(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string(&mut self, data: &[u8]) {
|
pub fn string(&mut self, data: &[u8]) {
|
||||||
|
|
@ -122,6 +132,16 @@ impl Writer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an array of significant bits for the mpint,
|
||||||
|
/// and whether a leading 0 needs to be added for padding.
|
||||||
|
pub fn fixup_mpint(mut int_encoded: &[u8]) -> (&[u8], bool) {
|
||||||
|
while int_encoded[0] == 0 {
|
||||||
|
int_encoded = &int_encoded[1..];
|
||||||
|
}
|
||||||
|
// If the first high bit is set, pad it with a zero.
|
||||||
|
(int_encoded, (int_encoded[0] & 0b10000000) > 1)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct NameList<'a>(pub &'a str);
|
pub struct NameList<'a>(pub &'a str);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue