This commit is contained in:
nora 2024-08-13 17:46:24 +02:00
parent 982de53668
commit a8d25d856f
6 changed files with 49 additions and 84 deletions

1
Cargo.lock generated
View file

@ -928,6 +928,7 @@ version = "0.1.0"
dependencies = [
"aes-gcm",
"chacha20",
"crypto-bigint",
"ed25519-dalek",
"eyre",
"hex-literal",

View file

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
aes-gcm = "0.10.3"
chacha20 = "0.9.1"
crypto-bigint = "0.5.5"
ed25519-dalek = { version = "2.1.1" }
eyre = "0.6.12"
p256 = { version = "0.13.2", features = ["ecdh", "ecdsa"] }

View file

@ -7,7 +7,7 @@ use subtle::ConstantTimeEq;
use crate::{
client_error,
packet::{EncryptedPacket, MsgKind, Packet, RawPacket},
parse::Writer,
parse::{self, Writer},
Msg, Result, SshRng,
};
@ -187,17 +187,30 @@ pub fn hostkey_ecdsa_sha2_p256(hostkey_private: Vec<u8>) -> HostKeySigningAlgori
hostkey_private,
public_key: |key| {
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
key.verifying_key()
.to_encoded_point(true)
.as_bytes()
.to_vec();
todo!()
let public_key = key.verifying_key();
let mut data = Writer::new();
// <https://datatracker.ietf.org/doc/html/rfc5656#section-3.1>
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| {
let key = p256::ecdsa::SigningKey::from_slice(key).unwrap();
let signature: p256::ecdsa::Signature = key.sign(data);
signature.to_vec();
todo!()
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())
},
}
}
@ -404,12 +417,8 @@ fn derive_key(
output
}
pub(crate) fn encode_mpint_for_hash(mut key: &[u8], mut add_to_hash: impl FnMut(&[u8])) {
while key[0] == 0 {
key = &key[1..];
}
// If the first high bit is set, pad it with a zero.
let pad_zero = (key[0] & 0b10000000) > 1;
pub(crate) fn encode_mpint_for_hash(key: &[u8], mut add_to_hash: impl FnMut(&[u8])) {
let (key, pad_zero) = parse::fixup_mpint(key);
add_to_hash(&u32::to_be_bytes((key.len() + (pad_zero as usize)) as u32));
if pad_zero {
add_to_hash(&[0]);

View file

@ -7,10 +7,7 @@ use core::str;
use std::{collections::VecDeque, mem::take};
use crypto::{AlgorithmName, AlgorithmNegotiation, EncryptionAlgorithm, HostKeySigningAlgorithm};
use packet::{
KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport, SshPublicKey,
SshSignature,
};
use packet::{KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport};
use parse::{NameList, Parser, Writer};
use rand::RngCore;
use sha2::Digest;
@ -199,7 +196,7 @@ impl ServerConnection {
let hostkey_algorithms = AlgorithmNegotiation {
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()),
],
};

View file

@ -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 mac_len: usize,
pub raw: Vec<u8>,

View file

@ -104,8 +104,18 @@ impl Writer {
self.string(list.0.as_bytes());
}
pub fn mpint(&mut self, _mpint: MpInt<'_>) {
todo!("implement correctly?")
pub fn mpint<const LIMBS: usize>(&mut self, uint: crypto_bigint::Uint<LIMBS>)
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]) {
@ -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)]
pub struct NameList<'a>(pub &'a str);