Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
439 changes: 350 additions & 89 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ sha2 = { version = "0.10", default-features = false }
hmac = "0.12"
poly1305 = "0.8"
digest = "0.10"
signature = { version = "2.0", default-features = false }
zeroize = { version = "1", default-features = false, features = ["derive"] }
cipher = { version = "0.4", features = ["zeroize"] }
subtle = { version = "2.4", default-features = false }
Expand All @@ -57,19 +56,27 @@ ed25519-dalek = { version = "2.1", default-features = false, features = ["zeroiz
x25519-dalek = { version = "2.0", default-features = false, features = ["zeroize"] }
curve25519-dalek = { version = "4.1", default-features = false, features = ["zeroize"] }
ml-kem = { version = "0.2.1", default-features = false, features = ["zeroize"], optional = true }
# p521 = { version = "0.13.2", default-features = false, features = ["ecdh", "ecdsa"] }
rsa = { version = "0.9", default-features = false, optional = true, features = ["sha2"] }
# TODO: getrandom feature is a workaround for missing ssh-key dependency with rsa. fixed in pending 0.6
ssh-key = { version = "0.6", default-features = false, optional = true, features = ["getrandom"] }

embedded-io = { version = "0.6", optional = true }

# TODO rc versions
p256 = { version = "0.14.0-rc.9", optional = true, default-features = false, features = ["ecdsa"] }
ecdsa = { version = "0.17.0-rc.17", default-features = false, optional = true , features = ["hazmat", "algorithm"]}

[features]
default = []
std = ["snafu/std", "ssh-key/alloc", "larger", "mlkem"]
backtrace = ["snafu/backtrace"]
rsa = ["dep:rsa", "ssh-key/rsa"]
mlkem = ["dep:ml-kem"]

_ecdsa = ["dep:ecdsa", "ssh-key/ecdsa"]
# ecdsa-sha2-nistp256
ecdsa256 = ["_ecdsa", "dep:p256", "ssh-key/p256"]

# allows conversion to/from OpenSSH key formats
openssh-key = ["ssh-key"]
# implements embedded_io::Error for sunset::Error
Expand Down
10 changes: 2 additions & 8 deletions demo/common/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,8 @@ impl DemoCommon {
}

fn handle_password(&mut self, a: ServPasswordAuth) -> Result<()> {
let username = match a.username() {
Ok(u) => u,
Err(_) => return Ok(()),
};
let password = match a.password() {
Ok(u) => u,
Err(_) => return Ok(()),
};
let username = a.username()?;
let password = a.password()?;

let p = if self.is_admin(username) {
&self.config.admin_pw
Expand Down
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub enum Error {
/// Signature is incorrect
BadSig,

/// Received a badly formatted number
BadNumber,

/// Error in received SSH protocol. Will disconnect.
SSHProto {
#[cfg(feature = "backtrace")]
Expand All @@ -61,9 +64,6 @@ pub enum Error {
// TODO: 'static disconnect message to return?
SSHProtoUnsupported,

/// Received a key with invalid structure, or too large.
BadKeyFormat,

/// Remote peer isn't SSH 2.0
NotSSH,

Expand Down
2 changes: 2 additions & 0 deletions src/kex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const fixed_options_hostsig: &[&str] = &[
SSH_NAME_ED25519,
#[cfg(feature = "rsa")]
SSH_NAME_RSA_SHA256,
#[cfg(feature = "ecdsa256")]
SSH_NAME_ECDSA256,
];

const fixed_options_cipher: &[&str] = &[SSH_NAME_CHAPOLY, SSH_NAME_AES256_CTR];
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ mod traffic;

use conn::DispatchEvent;

// Application API
pub use sshwire::TextString;

pub use auth::AuthSigMsg;
Expand Down
104 changes: 103 additions & 1 deletion src/packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use core::fmt::{Debug, Display};
#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;

#[cfg(feature = "ecdsa256")]
use p256::NistP256;

use sunset_sshwire_derive::*;

use crate::*;
Expand Down Expand Up @@ -331,6 +334,10 @@ pub enum PubKey<'a> {
#[sshwire(variant = SSH_NAME_RSA)]
RSA(RSAPubKey),

#[cfg(feature = "ecdsa256")]
#[sshwire(variant = SSH_NAME_ECDSA256)]
ECDSA256(ECDSAPubKey<p256::NistP256>),

#[sshwire(unknown)]
Unknown(Unknown<'a>),
}
Expand All @@ -342,6 +349,8 @@ impl PubKey<'_> {
PubKey::Ed25519(_) => Ok(SSH_NAME_ED25519),
#[cfg(feature = "rsa")]
PubKey::RSA(_) => Ok(SSH_NAME_RSA),
#[cfg(feature = "ecdsa256")]
PubKey::ECDSA256(_) => Ok(SSH_NAME_ECDSA256),
PubKey::Unknown(u) => Err(u),
}
}
Expand Down Expand Up @@ -390,6 +399,19 @@ impl TryFrom<&PubKey<'_>> for ssh_key::PublicKey {
Ok(k.into())
}

#[cfg(feature = "ecdsa256")]
PubKey::ECDSA256(k) => {
// TODO can simplify once ecdsa crate isn't rc
let k = ssh_key::public::EcdsaPublicKey::NistP256(
k.key
.to_sec1_point(false)
.as_bytes()
.try_into()
.map_err(|_| Error::BadKex)?,
);
Ok(k.into())
}

PubKey::Unknown(u) => {
trace!("unsupported {u}");
Err(Error::msg("Unsupported OpenSSH key"))
Expand Down Expand Up @@ -431,7 +453,7 @@ impl<'de> SSHDecode<'de> for RSAPubKey {
let n = SSHDecode::dec(s)?;
let key = rsa::RsaPublicKey::new(n, e).map_err(|e| {
debug!("Invalid RSA public key: {e}");
WireError::BadKeyFormat
WireError::BadKey
})?;
Ok(Self { key })
}
Expand All @@ -457,6 +479,64 @@ impl Arbitrary<'_> for RSAPubKey {
}
}

#[cfg(feature = "_ecdsa")]
#[derive(Clone, PartialEq)]
pub struct ECDSAPubKey<C: ecdsa::EcdsaCurve + ecdsa::elliptic_curve::CurveArithmetic>
{
pub key: ecdsa::VerifyingKey<C>,
}

#[cfg(feature = "_ecdsa")]
const ECDSA_ID_NISTP256: &str = "nistp256";

#[cfg(feature = "ecdsa256")]
impl SSHEncode for ECDSAPubKey<NistP256> {
fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
ECDSA_ID_NISTP256.enc(s)?;
let pt = self.key.to_sec1_point(false);
BinString(pt.as_bytes()).enc(s)
}
}

#[cfg(feature = "ecdsa256")]
impl<'de> SSHDecode<'de> for ECDSAPubKey<NistP256> {
fn dec<S>(s: &mut S) -> WireResult<Self>
where
S: SSHSource<'de>,
{
let name: &str = SSHDecode::dec(s)?;
if name != ECDSA_ID_NISTP256 {
trace!("Wrong ecdsa name {name}");
return Err(WireError::BadKey);
}

let key = BinString::dec(s)?;
let key = ecdsa::VerifyingKey::from_sec1_bytes(key.0).map_err(|_| {
trace!("Bad ecdsa key");
WireError::BadKey
})?;
Ok(Self { key })
}
}

#[cfg(feature = "ecdsa256")]
impl Debug for ECDSAPubKey<p256::NistP256> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ECDSAPubKey<p256>").finish_non_exhaustive()
}
}

#[cfg(all(feature = "arbitrary", feature = "ecdsa256"))]
impl Arbitrary<'_> for ECDSAPubKey<NistP256> {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
let key = ecdsa::VerifyingKey::from_sec1_bytes(
arbitrary::Arbitrary::arbitrary(u)?,
)
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
Ok(Self { key })
}
}

#[derive(Debug, SSHEncode, SSHDecode, Clone)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[sshwire(variant_prefix)]
Expand All @@ -468,6 +548,10 @@ pub enum Signature<'a> {
#[sshwire(variant = SSH_NAME_RSA_SHA256)]
RSA(RSASig<'a>),

#[cfg(feature = "ecdsa256")]
#[sshwire(variant = SSH_NAME_ECDSA256)]
ECDSA256(Blob<ECDSASig<'a>>),

#[sshwire(unknown)]
Unknown(Unknown<'a>),
}
Expand All @@ -479,6 +563,8 @@ impl<'a> Signature<'a> {
Signature::Ed25519(_) => Ok(SSH_NAME_ED25519),
#[cfg(feature = "rsa")]
Signature::RSA(_) => Ok(SSH_NAME_RSA_SHA256),
#[cfg(feature = "ecdsa256")]
Signature::ECDSA256(_) => Ok(SSH_NAME_ECDSA256),
Signature::Unknown(u) => Err(u),
}
}
Expand All @@ -494,6 +580,8 @@ impl<'a> Signature<'a> {
PubKey::Ed25519(_) => Ok(SSH_NAME_ED25519),
#[cfg(feature = "rsa")]
PubKey::RSA(_) => Ok(SSH_NAME_RSA_SHA256),
#[cfg(feature = "ecdsa256")]
PubKey::ECDSA256(_) => Ok(SSH_NAME_ECDSA256),
PubKey::Unknown(u) => {
warn!("Unknown key type \"{}\"", u);
Err(Error::UnknownMethod { kind: "key" })
Expand All @@ -506,6 +594,8 @@ impl<'a> Signature<'a> {
Signature::Ed25519(_) => Ok(SigType::Ed25519),
#[cfg(feature = "rsa")]
Signature::RSA(_) => Ok(SigType::RSA),
#[cfg(feature = "ecdsa256")]
Signature::ECDSA256(_) => Ok(SigType::ECDSA256),
Signature::Unknown(u) => {
warn!("Unknown signature type \"{}\"", u);
Err(Error::UnknownMethod { kind: "signature" })
Expand All @@ -524,6 +614,11 @@ impl<'a> From<&'a OwnedSig> for Signature<'a> {
OwnedSig::RSA(s) => {
Signature::RSA(RSASig { sig: BinString(s.as_ref()) })
}
#[cfg(feature = "ecdsa256")]
OwnedSig::ECDSA256 { r, s } => Signature::ECDSA256(Blob(ECDSASig {
r: sshwire::Mpint::new(r),
s: sshwire::Mpint::new(s),
})),
}
}
}
Expand All @@ -540,6 +635,13 @@ pub struct Ed25519Sig<'a> {
pub struct RSASig<'a> {
pub sig: BinString<'a>,
}
#[cfg(feature = "_ecdsa")]
#[derive(Debug, SSHEncode, SSHDecode, Clone)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub struct ECDSASig<'a> {
pub r: sshwire::Mpint<'a>,
pub s: sshwire::Mpint<'a>,
}

#[derive(Debug, SSHEncode, SSHDecode)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
Expand Down
Loading
Loading