diff --git a/Cargo.lock b/Cargo.lock index 06da29bf..2f7b4137 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2749,7 +2749,6 @@ dependencies = [ name = "radicle-ssh" version = "0.9.0" dependencies = [ - "byteorder", "log", "thiserror 1.0.69", "winpipe", diff --git a/crates/radicle-ssh/Cargo.toml b/crates/radicle-ssh/Cargo.toml index 9b6d147b..cb4a3d49 100644 --- a/crates/radicle-ssh/Cargo.toml +++ b/crates/radicle-ssh/Cargo.toml @@ -14,10 +14,9 @@ edition.workspace = true rust-version.workspace = true [dependencies] -byteorder = "1.4" log = { workspace = true } thiserror = { workspace = true } zeroize = { workspace = true } [target.'cfg(windows)'.dependencies] -winpipe = { workspace = true } \ No newline at end of file +winpipe = { workspace = true } diff --git a/crates/radicle-ssh/src/agent/client.rs b/crates/radicle-ssh/src/agent/client.rs index 462f889f..df807498 100644 --- a/crates/radicle-ssh/src/agent/client.rs +++ b/crates/radicle-ssh/src/agent/client.rs @@ -1,6 +1,5 @@ use std::fmt; use std::io::{Read, Write}; -use std::ops::DerefMut; use std::path::{Path, PathBuf}; #[cfg(unix)] @@ -9,7 +8,6 @@ pub use std::os::unix::net::UnixStream as Stream; #[cfg(windows)] pub use winpipe::WinStream as Stream; -use byteorder::{BigEndian, ByteOrder as _, WriteBytesExt}; use log::*; use thiserror::Error; use zeroize::Zeroize as _; @@ -145,7 +143,7 @@ impl AgentClient { match *cons { Constraint::KeyLifetime { seconds } => { buf.push(msg::CONSTRAIN_LIFETIME); - buf.deref_mut().write_u32::(seconds)? + buf.extend_u32(seconds); } Constraint::Confirm => buf.push(msg::CONSTRAIN_CONFIRM), Constraint::Extensions { @@ -186,13 +184,12 @@ impl AgentClient { buf.extend_ssh_string(pin); if !constraints.is_empty() { - buf.deref_mut() - .write_u32::(constraints.len() as u32)?; + buf.extend_usize(constraints.len()); for cons in constraints { match *cons { Constraint::KeyLifetime { seconds } => { buf.push(msg::CONSTRAIN_LIFETIME); - buf.deref_mut().write_u32::(seconds)?; + buf.extend_u32(seconds); } Constraint::Confirm => buf.push(msg::CONSTRAIN_CONFIRM), Constraint::Extensions { @@ -304,14 +301,13 @@ impl AgentClient { let total = 1 + pk.len() + 4 + data.len() + 4; let mut buf = Buffer::default(); - buf.write_u32::(total as u32) - .expect("Writing to a vector never fails"); + buf.extend_usize(total); buf.push(msg::SIGN_REQUEST); buf.extend_from_slice(&pk); buf.extend_ssh_string(data); // Signature flags should be zero for ed25519. - buf.write_u32::(0).unwrap(); + buf.extend_u32(0); buf } @@ -338,7 +334,7 @@ impl AgentClient { let total = 1 + pk.len(); let mut buf = Buffer::default(); - buf.write_u32::(total as u32)?; + buf.extend_usize(total); buf.push(msg::REMOVE_IDENTITY); buf.extend_from_slice(&pk); @@ -422,7 +418,8 @@ impl ClientStream for S { self.read_exact(&mut resp)?; // Read the rest of the buffer - let len = BigEndian::read_u32(&resp) as usize; + let len = u32::from_be_bytes(resp.as_slice().try_into().unwrap()) as usize; + resp.zeroize(); resp.resize(len, 0); self.read_exact(&mut resp)?; diff --git a/crates/radicle-ssh/src/encoding.rs b/crates/radicle-ssh/src/encoding.rs index 8342eefa..a4599437 100644 --- a/crates/radicle-ssh/src/encoding.rs +++ b/crates/radicle-ssh/src/encoding.rs @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -use std::ops::DerefMut; +use std::ops::DerefMut as _; -use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use thiserror::Error; use zeroize::Zeroizing; @@ -53,6 +52,16 @@ pub trait Encoding { fn write_empty_list(&mut self); /// Write the buffer length at the beginning of the buffer. fn write_len(&mut self); + /// Push a [`usize`] as an SSH-encoded unsiged 32-bit integer. + /// May panic if the argument is greater than [`u32::MAX`]. + /// This is a convience method, to spare callers casting or converting + /// [`usize`] to [`u32`]. If callers end up in a situation where they + /// need to push a 32-bit unisgned integer, but the value they would + /// like to push does not fit 32 bits, then the implementation will not + /// comply with the SSH format anyway. + fn extend_usize(&mut self, u: usize) { + self.extend_u32(u.try_into().unwrap()) + } } /// Encoding length of the given mpint. @@ -66,12 +75,12 @@ pub fn mpint_len(s: &[u8]) -> usize { impl Encoding for Vec { fn extend_ssh_string(&mut self, s: &[u8]) { - self.write_u32::(s.len() as u32).unwrap(); + self.extend_usize(s.len()); self.extend(s); } fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] { - self.write_u32::(len as u32).unwrap(); + self.extend_usize(len); let current = self.len(); self.resize(current + len, 0u8); @@ -86,24 +95,20 @@ impl Encoding for Vec { } // If the first non-zero is >= 128, write its length (u32, BE), followed by 0. if s[i] & 0x80 != 0 { - self.write_u32::((s.len() - i + 1) as u32) - .unwrap(); + self.extend_usize(s.len() - i + 1); self.push(0) } else { - self.write_u32::((s.len() - i) as u32).unwrap(); + self.extend_usize(s.len() - i); } self.extend(&s[i..]); } fn extend_u32(&mut self, s: u32) { - let mut buf = [0x0; 4]; - BigEndian::write_u32(&mut buf, s); - self.extend(buf); + self.extend(s.to_be_bytes()); } fn extend_list<'a, I: Iterator>(&mut self, list: I) { let len0 = self.len(); - self.extend([0, 0, 0, 0]); let mut first = true; for i in list { @@ -116,7 +121,7 @@ impl Encoding for Vec { } let len = (self.len() - len0 - 4) as u32; - BigEndian::write_u32(&mut self[len0..], len); + self.splice(len0..len0, len.to_be_bytes()); } fn write_empty_list(&mut self) { @@ -125,7 +130,7 @@ impl Encoding for Vec { fn write_len(&mut self) { let len = self.len() - 4; - BigEndian::write_u32(&mut self[..], len as u32); + self[..4].copy_from_slice((len as u32).to_be_bytes().as_slice()); } } @@ -207,7 +212,8 @@ impl<'a> Cursor<'a> { /// Read a `u32` from this reader. pub fn read_u32(&mut self) -> Result { if self.position + 4 <= self.s.len() { - let u = BigEndian::read_u32(&self.s[self.position..]); + let u = + u32::from_be_bytes(self.s[self.position..self.position + 4].try_into().unwrap()); self.position += 4; Ok(u) } else {