From 66150183b7ee6f6510c4f2f1f5610e38502c0bf5 Mon Sep 17 00:00:00 2001 From: Chris Chance Date: Sun, 15 Oct 2023 19:22:36 -0400 Subject: [PATCH 1/4] initial dss-sha1 implementation --- Cargo.toml | 3 +++ README.md | 1 + README_ZH.md | 1 + src/algorithm/mod.rs | 3 +++ src/algorithm/public_key/dss.rs | 47 +++++++++++++++++++++++++++++++++ src/algorithm/public_key/mod.rs | 5 ++++ tests/algorithms.rs | 17 ++++++++++++ 7 files changed, 77 insertions(+) create mode 100644 src/algorithm/public_key/dss.rs diff --git a/Cargo.toml b/Cargo.toml index f8e6903..7ada84a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,10 @@ deprecated-algorithms = [ "deprecated-dh-group1-sha1", "deprecated-aes-cbc", "deprecated-des-cbc", + "deprecated-dss-sha1" ] deprecated-rsa-sha1 = ["dep:sha1"] +deprecated-dss-sha1 = ["dep:sha1", "dep:dsa"] deprecated-dh-group1-sha1 = ["dep:sha1"] deprecated-aes-cbc = ["dep:cbc", "dep:cipher"] deprecated-des-cbc = ["dep:cbc", "dep:cipher", "dep:des"] @@ -48,6 +50,7 @@ num-bigint = { version = "0.4", features = ["rand"] } # the crate rsa has removed the internal hash implement from 0.7.0 sha1 = { version = "0.10.5", default-features = false, features = ["oid"], optional = true } sha2 = { version = "0.10.6", default-features = false, features = ["oid"]} +dsa = { version = "0.6.1", optional = true } rsa = "0.9" aes = "0.8" ctr = "0.9" diff --git a/README.md b/README.md index 89e5b61..12a8463 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ match ssh::create_session() * `rsa-sha2-256` * `rsa-sha2-512` * `rsa-sha` (behind feature "deprecated-rsa-sha1") +* `ssh-dss` (behind feature "deprecated-dss-sha1") ### 3. Encryption algorithms diff --git a/README_ZH.md b/README_ZH.md index f515611..fdcd72c 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -96,6 +96,7 @@ ssh::create_session().timeout(Some(std::time::Duration::from_secs(5))); * `rsa-sha2-512` * `rsa-sha2-256` * `rsa-sha` (features = ["deprecated-rsa-sha1"]) +* `ssh-dss` (features = ["deprecated-dss-sha1"]) #### 3. 加密算法 diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs index 1c8ecee..e97924a 100644 --- a/src/algorithm/mod.rs +++ b/src/algorithm/mod.rs @@ -62,6 +62,9 @@ pub enum PubKey { RsaSha2_256, #[strum(serialize = "rsa-sha2-512")] RsaSha2_512, + #[cfg(feature = "deprecated-dss-sha1")] + #[strum(serialize = "ssh-dss")] + SshDss } /// MAC(message authentication code) algorithm diff --git a/src/algorithm/public_key/dss.rs b/src/algorithm/public_key/dss.rs new file mode 100644 index 0000000..a45f628 --- /dev/null +++ b/src/algorithm/public_key/dss.rs @@ -0,0 +1,47 @@ +#[cfg(feature = "deprecated-dss-sha1")] +use sha1::Sha1; +use signature::DigestVerifier; +use sha2::Digest; + +use crate::algorithm::public_key::PublicKey as PubK; +use crate::model::Data; +use crate::SshError; + +#[cfg(feature = "deprecated-dss-sha1")] +pub(super) struct DssSha1; + +#[cfg(feature = "deprecated-dss-sha1")] +impl PubK for DssSha1 { + fn new() -> Self + where + Self: Sized, + { + Self + } + + fn verify_signature(&self, ks: &[u8], message: &[u8], sig: &[u8]) -> Result { + let mut data = Data::from(ks[4..].to_vec()); + data.get_u8s(); + + let p = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); + let q = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); + let g = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); + let y = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); + + let components = dsa::Components::from_components(p, q, g) + .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; + + let public_key = dsa::VerifyingKey::from_components(components, y) + .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; + + let digest = Sha1::new().chain_update(message); + + let r = dsa::BigUint::from_bytes_be(&sig[0..20]); + let s = dsa::BigUint::from_bytes_be(&sig[20..40]); + + let signature = dsa::Signature::from_components(r, s) + .map_err(|_| SshError::SshPubKeyError("SSH Signature was not valid".to_string()))?; + + Ok(public_key.verify_digest(digest, &signature).is_ok()) + } +} diff --git a/src/algorithm/public_key/mod.rs b/src/algorithm/public_key/mod.rs index b706dec..903a597 100644 --- a/src/algorithm/public_key/mod.rs +++ b/src/algorithm/public_key/mod.rs @@ -2,6 +2,7 @@ use crate::SshError; mod ed25519; mod rsa; +mod dss; #[cfg(feature = "deprecated-rsa-sha1")] use self::rsa::RsaSha1; @@ -9,6 +10,8 @@ use self::rsa::RsaSha256; use self::rsa::RsaSha512; use super::PubKey; use ed25519::Ed25519; +#[cfg(feature = "deprecated-dss-sha1")] +use self::dss::DssSha1; /// # Public Key Algorithms /// @@ -28,5 +31,7 @@ pub(crate) fn from(s: &PubKey) -> Box { PubKey::SshRsa => Box::new(RsaSha1::new()), PubKey::RsaSha2_256 => Box::new(RsaSha256::new()), PubKey::RsaSha2_512 => Box::new(RsaSha512::new()), + #[cfg(feature = "deprecated-dss-sha1")] + PubKey::SshDss => Box::new(DssSha1::new()), } } diff --git a/tests/algorithms.rs b/tests/algorithms.rs index 81ef478..f74ca09 100644 --- a/tests/algorithms.rs +++ b/tests/algorithms.rs @@ -32,6 +32,23 @@ mod test { .run_local(); session.close(); } + + #[cfg(feature = "deprecated-algorithms")] + #[test] + fn test_ssh_dss() { + let session = ssh::create_session_without_default() + .username(&get_username()) + .private_key_path(get_pem_rsa()) + .add_kex_algorithms(algorithm::Kex::DiffieHellmanGroup1Sha1) + .add_pubkey_algorithms(algorithm::PubKey::SshDss) + .add_enc_algorithms(algorithm::Enc::Aes256Cbc) + .add_compress_algorithms(algorithm::Compress::None) + .add_mac_algortihms(algorithm::Mac::HmacSha1) + .connect(get_server()) + .unwrap() + .run_local(); + session.close(); + } #[cfg(feature = "deprecated-algorithms")] #[test] From 1e6c9727e23ef05e6b795a129e8295ba3a9326f8 Mon Sep 17 00:00:00 2001 From: Chris Chance Date: Sun, 15 Oct 2023 19:23:24 -0400 Subject: [PATCH 2/4] comments and corrected digest --- src/algorithm/public_key/dss.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/algorithm/public_key/dss.rs b/src/algorithm/public_key/dss.rs index a45f628..e688c4f 100644 --- a/src/algorithm/public_key/dss.rs +++ b/src/algorithm/public_key/dss.rs @@ -1,7 +1,6 @@ #[cfg(feature = "deprecated-dss-sha1")] -use sha1::Sha1; -use signature::DigestVerifier; -use sha2::Digest; +use sha1::{Sha1, Digest}; +use signature::DigestVerifier; use crate::algorithm::public_key::PublicKey as PubK; use crate::model::Data; @@ -23,6 +22,7 @@ impl PubK for DssSha1 { let mut data = Data::from(ks[4..].to_vec()); data.get_u8s(); + // RFC4253 6.6 DSS Signature key blob are 4x mpint's that need to be pulled out to be used as components in the public key. let p = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); let q = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); let g = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); @@ -31,17 +31,21 @@ impl PubK for DssSha1 { let components = dsa::Components::from_components(p, q, g) .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; + // Build the public key for verification of the message let public_key = dsa::VerifyingKey::from_components(components, y) .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; + // Perform an SHA1 hash on the message let digest = Sha1::new().chain_update(message); + // RFC4253 6.6 DSS Signature blob is actually 2x160bit blobs so r and s are each 160bit (20 bytes) let r = dsa::BigUint::from_bytes_be(&sig[0..20]); let s = dsa::BigUint::from_bytes_be(&sig[20..40]); let signature = dsa::Signature::from_components(r, s) .map_err(|_| SshError::SshPubKeyError("SSH Signature was not valid".to_string()))?; + // Verify the hashed message with the provided signature, matches the public_key Ok(public_key.verify_digest(digest, &signature).is_ok()) } -} +} \ No newline at end of file From 452a856edf6d41e3c2e9dfb6b7bba2953eec6017 Mon Sep 17 00:00:00 2001 From: Chris Chance Date: Mon, 16 Oct 2023 17:46:19 -0400 Subject: [PATCH 3/4] Fix clippy errors, and cargo fmt --- src/algorithm/mod.rs | 2 +- src/algorithm/public_key/dss.rs | 18 ++++++++++-------- src/algorithm/public_key/mod.rs | 7 ++++--- tests/algorithms.rs | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs index e97924a..566c2e0 100644 --- a/src/algorithm/mod.rs +++ b/src/algorithm/mod.rs @@ -64,7 +64,7 @@ pub enum PubKey { RsaSha2_512, #[cfg(feature = "deprecated-dss-sha1")] #[strum(serialize = "ssh-dss")] - SshDss + SshDss, } /// MAC(message authentication code) algorithm diff --git a/src/algorithm/public_key/dss.rs b/src/algorithm/public_key/dss.rs index e688c4f..f967f8e 100644 --- a/src/algorithm/public_key/dss.rs +++ b/src/algorithm/public_key/dss.rs @@ -1,6 +1,6 @@ #[cfg(feature = "deprecated-dss-sha1")] -use sha1::{Sha1, Digest}; -use signature::DigestVerifier; +use sha1::{Digest, Sha1}; +use signature::DigestVerifier; use crate::algorithm::public_key::PublicKey as PubK; use crate::model::Data; @@ -28,12 +28,14 @@ impl PubK for DssSha1 { let g = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); let y = dsa::BigUint::from_bytes_be(data.get_u8s().as_slice()); - let components = dsa::Components::from_components(p, q, g) - .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; - + let components = dsa::Components::from_components(p, q, g).map_err(|_| { + SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()) + })?; + // Build the public key for verification of the message - let public_key = dsa::VerifyingKey::from_components(components, y) - .map_err(|_| SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()))?; + let public_key = dsa::VerifyingKey::from_components(components, y).map_err(|_| { + SshError::SshPubKeyError("SSH Public Key components were not valid".to_string()) + })?; // Perform an SHA1 hash on the message let digest = Sha1::new().chain_update(message); @@ -48,4 +50,4 @@ impl PubK for DssSha1 { // Verify the hashed message with the provided signature, matches the public_key Ok(public_key.verify_digest(digest, &signature).is_ok()) } -} \ No newline at end of file +} diff --git a/src/algorithm/public_key/mod.rs b/src/algorithm/public_key/mod.rs index 903a597..62b47e8 100644 --- a/src/algorithm/public_key/mod.rs +++ b/src/algorithm/public_key/mod.rs @@ -1,17 +1,18 @@ use crate::SshError; +#[cfg(feature = "deprecated-rsa-sha1")] +mod dss; mod ed25519; mod rsa; -mod dss; +#[cfg(feature = "deprecated-dss-sha1")] +use self::dss::DssSha1; #[cfg(feature = "deprecated-rsa-sha1")] use self::rsa::RsaSha1; use self::rsa::RsaSha256; use self::rsa::RsaSha512; use super::PubKey; use ed25519::Ed25519; -#[cfg(feature = "deprecated-dss-sha1")] -use self::dss::DssSha1; /// # Public Key Algorithms /// diff --git a/tests/algorithms.rs b/tests/algorithms.rs index f74ca09..056975b 100644 --- a/tests/algorithms.rs +++ b/tests/algorithms.rs @@ -32,7 +32,7 @@ mod test { .run_local(); session.close(); } - + #[cfg(feature = "deprecated-algorithms")] #[test] fn test_ssh_dss() { From cf48493d5dce372f2e1f4fec8f509057bc154a47 Mon Sep 17 00:00:00 2001 From: Chris Chance Date: Mon, 16 Oct 2023 17:51:38 -0400 Subject: [PATCH 4/4] enable ssh-dss for CI --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea3f8d0..be1524e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -84,7 +84,7 @@ jobs: - name: add user run: addgroup ubuntu && adduser --shell /bin/ash --disabled-password --home /home/ubuntu --ingroup ubuntu ubuntu && echo "ubuntu:password" | chpasswd - name: config ssh - run: ssh-keygen -A && sed -i -E "s|(AuthorizedKeysFile).*|\1 %h/.ssh/authorized_keys|g" /etc/ssh/sshd_config && echo "HostKeyAlgorithms=+ssh-rsa" >> /etc/ssh/sshd_config && echo "PubkeyAcceptedAlgorithms=+ssh-rsa" >> /etc/ssh/sshd_config && echo "KexAlgorithms=+diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" >> /etc/ssh/sshd_config && echo "Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc" >> /etc/ssh/sshd_config && sed -i -E "s/#?(ChallengeResponseAuthentication|PasswordAuthentication).*/\1 yes/g" /etc/ssh/sshd_config + run: ssh-keygen -A && sed -i -E "s|(AuthorizedKeysFile).*|\1 %h/.ssh/authorized_keys|g" /etc/ssh/sshd_config && echo "HostKeyAlgorithms=+ssh-rsa,ssh-dss" >> /etc/ssh/sshd_config && echo "PubkeyAcceptedAlgorithms=+ssh-rsa,ssh-dss" >> /etc/ssh/sshd_config && echo "KexAlgorithms=+diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" >> /etc/ssh/sshd_config && echo "Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc" >> /etc/ssh/sshd_config && sed -i -E "s/#?(ChallengeResponseAuthentication|PasswordAuthentication).*/\1 yes/g" /etc/ssh/sshd_config - name: create .ssh run: mkdir -p /home/ubuntu/.ssh && umask 066; touch /home/ubuntu/.ssh/authorized_keys - name: generate rsa files