diff --git a/Cargo.lock b/Cargo.lock index ada644474af3ba24c7ffa0cded41dc7d84de6f3a..f9122e1b24a78435e4c8a3441e11f2149bc25340 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,19 +271,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "curve25519-dalek" -version = "4.0.0-pre.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc3116fe595d7847c701796ac1b189bd86b81f4f593c6f775f9d80fb2e29f4" -dependencies = [ - "byteorder", - "digest 0.10.3", - "rand_core 0.6.3", - "subtle", - "zeroize", -] - [[package]] name = "der" version = "0.5.1" @@ -352,7 +339,6 @@ dependencies = [ "chacha20", "ctr", "digest 0.10.3", - "ed25519-dalek 2.0.0-pre.1", "heapless", "hmac", "log", @@ -363,13 +349,14 @@ dependencies = [ "proptest", "rand", "rand_core 0.6.3", + "salty", "serde_json", "sha2 0.10.2", + "signature", "simplelog", "snafu", "ssh-key", "sshwire_derive", - "x25519-dalek", ] [[package]] @@ -401,23 +388,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek 3.2.1", + "curve25519-dalek", "ed25519", "sha2 0.9.9", "zeroize", ] -[[package]] -name = "ed25519-dalek" -version = "2.0.0-pre.1" -dependencies = [ - "curve25519-dalek 4.0.0-pre.2", - "ed25519", - "rand", - "sha2 0.10.2", - "zeroize", -] - [[package]] name = "embedded-hal" version = "0.2.7" @@ -930,6 +906,16 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "salty" +version = "0.2.0" +dependencies = [ + "digest 0.10.3", + "ed25519", + "subtle", + "zeroize", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1102,7 +1088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e09d8b0ff2fca365c89ad535055c17adbd9fb0a5daf338299049882e95b6931" dependencies = [ "base64ct", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "pem-rfc7468", "rand_core 0.6.3", "sec1", @@ -1386,16 +1372,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "x25519-dalek" -version = "2.0.0-pre.2" -source = "git+https://github.com/mobilecoinfoundation/x25519-dalek?branch=mobilecoin#c1966b8743d320cd07a54191475e5c0f94b2ea30" -dependencies = [ - "curve25519-dalek 4.0.0-pre.2", - "rand_core 0.6.3", - "zeroize", -] - [[package]] name = "zeroize" version = "1.3.0" @@ -1416,3 +1392,7 @@ dependencies = [ "syn", "synstructure", ] + +[[patch.unused]] +name = "ed25519-dalek" +version = "2.0.0-pre.1" diff --git a/smol/examples/con1.rs b/smol/examples/con1.rs index cd97cc26325e7030f826780679bab9941a1382c2..74f3d30cb1275a2bb705010af69117ba13b4a2f9 100644 --- a/smol/examples/con1.rs +++ b/smol/examples/con1.rs @@ -67,12 +67,6 @@ fn parse_args() -> Result<Args> { fn main() -> Result<()> { let args = parse_args()?; - use std::panic; - - panic::set_hook(Box::new(|_| { - println!("Custom panic hook"); - })); - // time crate won't read TZ if we're threaded, in case someone // tries to mutate shared state with setenv. diff --git a/smol/src/async_client.rs b/smol/src/async_client.rs index ec2dc9538125c9f36c3997db5a0f5a8345527afc..4c02e1cb2c9e84b8da8b165513b2b028c55be736 100644 --- a/smol/src/async_client.rs +++ b/smol/src/async_client.rs @@ -35,7 +35,6 @@ impl SimpleClient { self.authkeys.push_back(k) } - fn write_stdout } #[async_trait(?Send)] diff --git a/sshproto/Cargo.toml b/sshproto/Cargo.toml index ff92e139b83c9f2777b79d0aeb4c066560da93c4..01b8fb6ab76868984b4c6f2f794bee111bb965ba 100644 --- a/sshproto/Cargo.toml +++ b/sshproto/Cargo.toml @@ -22,6 +22,7 @@ aes = "0.8" sha2 = { version = "0.10", default-features = false } hmac = "0.12" digest = "0.10" +signature = { version = "1.4", default-features = false } ssh-key = { version = "0.4", default-features = false, features = ["ed25519", "ecdsa", "sha2"] } chacha20 = "0.9" poly1305 = "0.7" @@ -33,27 +34,7 @@ pin-utils = "0.1" # tokio = { version = "1.18", features = ["sync"], optional = true } async-trait = { version = "0.1", optional = true } -[dependencies.ed25519-dalek] -version = "2.0.0-pre.1" -default-features = false -# TODO: switch u32/u64 based on targets somehow? needs to match x25519's -# TODO: is "rand" needed here, or only for tests? -features = [ "u32_backend", "rand" ] -# pending https://github.com/dalek-cryptography/ed25519-dalek/pull/193 -# and also sign_parts() -# git = "https://github.com/mkj/ed25519-dalek" -# branch = "parts" -path = "/home/matt/3rd/rs/ed25519-dalek" - -[dependencies.x25519-dalek] -version = "2.0.0-pre.2" -default-features = false -# TODO: switch based on targets somehow? -features = [ "u32_backend" ] -# pending https://github.com/dalek-cryptography/x25519-dalek/pull/87 -# upstream version uses older rand -git = "https://github.com/mobilecoinfoundation/x25519-dalek" -branch = "mobilecoin" +salty = { version = "0.2", path = "/home/matt/3rd/rs/salty" } [features] default = [ "getrandom" ] diff --git a/sshproto/src/error.rs b/sshproto/src/error.rs index 04bca5a182495484f31e35305a06837771f8a4e9..b311a9a9b7b02ddc9a73a911673abe0d1a2cd825 100644 --- a/sshproto/src/error.rs +++ b/sshproto/src/error.rs @@ -23,10 +23,6 @@ pub enum Error { /// Input buffer ran out RanOut, - /// Not implemented (unused in SSH protocol) - // internal - NoSerializer, - /// Not a UTF8 string BadString, @@ -37,13 +33,13 @@ pub enum Error { BadDecrypt, /// Signature is incorrect - BadSignature, + BadSig, /// Key exchange incorrect BadKex, // Signature doesn't match key type - SignatureMismatch, + SigMismatch, /// Error in received SSH protocol SSHProtoError, diff --git a/sshproto/src/kex.rs b/sshproto/src/kex.rs index c934b6c7317738ed302bb62ec2b24b187ee033cb..95fd08cee5ca681550a08428bc1874fedbecfba6 100644 --- a/sshproto/src/kex.rs +++ b/sshproto/src/kex.rs @@ -442,9 +442,9 @@ impl SharedSecret { kex_hash.prefinish(&fake_hostkey, p.q_c.0, algos.kex.pubkey())?; let kex_out = match algos.kex { SharedSecret::KexCurve25519(ref k) => { - let pubkey = (k.ours.as_ref().trap()?).into(); + let pubkey: salty::agreement::PublicKey = k.ours.as_ref().trap()?.into(); let mut kex_out = KexCurve25519::secret(&mut algos, p.q_c.0, kex_hash, sess_id)?; - kex_out.pubkey = Some(pubkey); + kex_out.pubkey = Some(pubkey.to_bytes()); kex_out } }; @@ -464,8 +464,10 @@ pub(crate) struct KexOutput { pub keys: Keys, // storage for kex packet reply content that outlives Kex - // TODO: this should become generic - pubkey: Option<x25519_dalek::PublicKey>, + // in make_kexdhreply(). + + // TODO: this should become generic for other kex methods + pubkey: Option<[u8; 32]>, } impl fmt::Debug for KexOutput { @@ -490,7 +492,7 @@ impl<'a> KexOutput { // server only pub fn make_kexdhreply(&'a self) -> Result<Packet<'a>> { - let q_s = BinString(self.pubkey.as_ref().trap()?.as_bytes()); + let q_s = BinString(self.pubkey.as_ref().trap()?); // TODO let k_s = Blob(PubKey::Ed25519(packets::Ed25519PubKey{ key: BinString(&[]) })); let sig = Blob(Signature::Ed25519(packets::Ed25519Sig{ sig: BinString(&[]) })); @@ -500,28 +502,32 @@ impl<'a> KexOutput { } pub(crate) struct KexCurve25519 { - ours: Option<x25519_dalek::EphemeralSecret>, - pubkey: x25519_dalek::PublicKey, + ours: Option<salty::agreement::SecretKey>, + pubkey: [u8; 32], } impl core::fmt::Debug for KexCurve25519 { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("KexCurve25519") .field("ours", &if self.ours.is_some() { "Some" } else { "None" }) - .field("pubkey", self.pubkey.as_bytes()) + .field("pubkey", &self.pubkey) .finish() } } impl KexCurve25519 { fn new() -> Result<Self> { - let ours = x25519_dalek::EphemeralSecret::new(random::DoorRng::default()); - let pubkey = (&ours).into(); + let mut s = [0u8; 32]; + random::fill_random(s.as_mut_slice())?; + // TODO: check that pure random bytes are OK + let ours = salty::agreement::SecretKey::from_seed(&s); + let pubkey: salty::agreement::PublicKey = (&ours).into(); + let pubkey = pubkey.to_bytes(); Ok(KexCurve25519 { ours: Some(ours), pubkey }) } fn pubkey<'a>(&'a self) -> &'a [u8] { - self.pubkey.as_bytes() + self.pubkey.as_slice() } fn secret<'a>( @@ -536,9 +542,9 @@ impl KexCurve25519 { }; let ours = kex.ours.take().trap()?; let theirs: [u8; 32] = theirs.try_into().map_err(|_| Error::BadKex)?; - let theirs = theirs.into(); - let shsec = ours.diffie_hellman(&theirs); - KexOutput::make(shsec.as_bytes(), algos, kex_hash, sess_id) + let theirs = theirs.try_into().map_err(|_| Error::BadKex)?; + let shsec = ours.agree(&theirs); + KexOutput::make(&shsec.to_bytes(), algos, kex_hash, sess_id) } } diff --git a/sshproto/src/sign.rs b/sshproto/src/sign.rs index 819a701f558d585547018888c0f8ff7bc743509d..638499fd7e107a3d8367e25d0bc41b9a1da73cbc 100644 --- a/sshproto/src/sign.rs +++ b/sshproto/src/sign.rs @@ -5,10 +5,11 @@ use { log::{debug, error, info, log, trace, warn}, }; -use ed25519_dalek as dalek; -use ed25519_dalek::{Verifier, Signer}; +use salty::{SecretKey, PublicKey}; +use signature::Verifier; -use crate::{*, packets::ParseContext}; +use crate::*; +use packets::ParseContext; use sshnames::*; use packets::{PubKey, Signature, Ed25519PubKey}; use sshwire::{BinString, SSHEncode}; @@ -56,21 +57,23 @@ impl SigType { if discriminant(&sig_type) != discriminant(self) { warn!("Received {:?} signature, expecting {}", sig.algorithm_name(), self.algorithm_name()); - return Err(Error::BadSignature) + return Err(Error::BadSig) } match (self, pubkey, sig) { (SigType::Ed25519, PubKey::Ed25519(k), Signature::Ed25519(s)) => { - let k = dalek::PublicKey::from_bytes(k.key.0).map_err(|_| Error::BadSignature)?; - let s = dalek::Signature::from_bytes(s.sig.0).map_err(|_| Error::BadSignature)?; - k.verify(message, &s).map_err(|_| Error::BadSignature) + let k: &[u8; 32] = k.key.0.try_into().map_err(|_| Error::BadKey)?; + let k: salty::PublicKey = k.try_into().map_err(|_| Error::BadKey)?; + let s: &[u8; 64] = s.sig.0.try_into().map_err(|_| Error::BadSig)?; + let s: salty::Signature = s.into(); + k.verify(message, &s).map_err(|_| Error::BadSig) } (SigType::RSA256, PubKey::RSA(_k), Signature::RSA256(_s)) => { // TODO warn!("RSA256 is not implemented for no_std"); - Err(Error::BadSignature) + Err(Error::BadSig) // // untested // use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, PaddingScheme}; // let k: RsaPublicKey = k.try_into()?; @@ -80,7 +83,7 @@ impl SigType { // s.sig.0) // .map_err(|e| { // trace!("RSA signature failed: {e}"); - // Error::BadSignature + // Error::BadSig // }) } @@ -89,21 +92,21 @@ impl SigType { sig.algorithm_name(), pubkey.algorithm_name(), ); - Err(Error::SignatureMismatch) + Err(Error::SigMismatch) } } } } pub(crate) enum OwnedSig { - // dalek::Signature doesn't let us borrow the inner bytes, + // Signature doesn't let us borrow the inner bytes, // so we just store raw bytes here. Ed25519([u8; 64]), RSA256, // TODO } -impl From<dalek::Signature> for OwnedSig { - fn from(s: dalek::Signature) -> Self { +impl From<salty::Signature> for OwnedSig { + fn from(s: salty::Signature) -> Self { OwnedSig::Ed25519(s.to_bytes()) } @@ -113,7 +116,7 @@ impl From<dalek::Signature> for OwnedSig { /// or could potentially send the signing requests to a SSH agent /// or other entitiy. pub enum SignKey { - Ed25519(dalek::Keypair), + Ed25519(salty::Keypair), } impl SignKey { @@ -137,10 +140,9 @@ impl SignKey { pub(crate) fn sign_encode<'s>(&self, msg: &'s impl SSHEncode, parse_ctx: Option<&ParseContext>) -> Result<OwnedSig> { match self { SignKey::Ed25519(k) => { - let exk: dalek::ExpandedSecretKey = (&k.secret).into(); - exk.sign_parts(|h| { - sshwire::hash_ser(h, parse_ctx, msg).map_err(|_| dalek::SignatureError::new()) - }, &k.public) + k.sign_parts(|h| { + sshwire::hash_ser(h, parse_ctx, msg).map_err(|_| salty::Error::ContextTooLong) + }) .trap() .map(|s| s.into()) } @@ -154,13 +156,11 @@ impl TryFrom<ssh_key::PrivateKey> for SignKey { fn try_from(k: ssh_key::PrivateKey) -> Result<Self> { match k.key_data() { ssh_key::private::KeypairData::Ed25519(k) => { - let edk = dalek::Keypair { - secret: dalek::SecretKey::from_bytes(&k.private.to_bytes()) - .map_err(|_| Error::BadKey)?, - public: dalek::PublicKey::from_bytes(&k.public.0) - .map_err(|_| Error::BadKey)?, + let key = salty::Keypair { + secret: (&k.private.to_bytes()).into(), + public: (&k.public.0).try_into().map_err(|_| Error::BadKey)?, }; - Ok(SignKey::Ed25519(edk)) + Ok(SignKey::Ed25519(key)) } _ => Err(Error::NotAvailable { what: k.algorithm().as_str() }) } @@ -169,7 +169,6 @@ impl TryFrom<ssh_key::PrivateKey> for SignKey { #[cfg(test)] pub(crate) mod tests { - use ed25519_dalek::Signer; use crate::*; use sshnames::SSH_NAME_ED25519; @@ -178,8 +177,9 @@ pub(crate) mod tests { use doorlog::init_test_log; pub(crate) fn make_ed25519_signkey() -> SignKey { - let mut rng = random::DoorRng::default(); - let ed = dalek::Keypair::generate(&mut rng); + let mut s = [0u8; 32]; + random::fill_random(s.as_mut_slice()).unwrap(); + let ed = salty::Keypair::from(&s); sign::SignKey::Ed25519(ed) }