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
7 changes: 4 additions & 3 deletions ssh-encoding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ edition = "2021"
rust-version = "1.60"

[dependencies]
base64 = { package = "base64ct", version = "1.4", optional = true }
base64ct = { version = "1.4", optional = true }
bytes = { version = "1", optional = true, default-features = false }
pem = { package = "pem-rfc7468", version = "=1.0.0-pre.0", optional = true }
sha2 = { version = "=0.11.0-pre.3", optional = true, default-features = false }
Expand All @@ -24,9 +24,10 @@ sha2 = { version = "=0.11.0-pre.3", optional = true, default-features = false }
hex-literal = "0.4.1"

[features]
alloc = ["base64?/alloc", "pem?/alloc"]
std = ["alloc", "base64?/std", "pem?/std", "sha2?/std"]
alloc = ["base64ct?/alloc", "pem?/alloc"]
std = ["alloc", "base64ct?/std", "pem?/std", "sha2?/std"]

base64 = ["dep:base64ct"]
bytes = ["alloc", "dep:bytes"]
pem = ["base64", "dep:pem"]

Expand Down
10 changes: 5 additions & 5 deletions ssh-encoding/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub type Result<T> = core::result::Result<T, Error>;
pub enum Error {
/// Base64-related errors.
#[cfg(feature = "base64")]
Base64(base64::Error),
Base64(base64ct::Error),

/// Character encoding-related errors.
CharacterEncoding,
Expand Down Expand Up @@ -82,15 +82,15 @@ impl From<alloc::string::FromUtf8Error> for Error {
}

#[cfg(feature = "base64")]
impl From<base64::Error> for Error {
fn from(err: base64::Error) -> Error {
impl From<base64ct::Error> for Error {
fn from(err: base64ct::Error) -> Error {
Error::Base64(err)
}
}

#[cfg(feature = "base64")]
impl From<base64::InvalidLengthError> for Error {
fn from(_: base64::InvalidLengthError) -> Error {
impl From<base64ct::InvalidLengthError> for Error {
fn from(_: base64ct::InvalidLengthError) -> Error {
Error::Length
}
}
Expand Down
4 changes: 2 additions & 2 deletions ssh-encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ pub use crate::{

#[cfg(feature = "base64")]
pub use {
crate::{reader::Base64Reader, writer::Base64Writer},
base64,
crate::{reader::base64::Base64Reader, writer::base64::Base64Writer},
base64ct as base64,
};

#[cfg(feature = "pem")]
Expand Down
18 changes: 3 additions & 15 deletions ssh-encoding/src/reader.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//! Reader trait and associated implementations.

#[cfg(feature = "base64")]
pub(crate) mod base64;

use crate::{decode::Decode, Error, Result};
use core::str;

/// Constant-time Base64 reader implementation.
#[cfg(feature = "base64")]
pub type Base64Reader<'i> = base64::Decoder<'i, base64::Base64>;

/// Reader trait which decodes the binary SSH protocol serialization from
/// various inputs.
pub trait Reader: Sized {
Expand Down Expand Up @@ -145,17 +144,6 @@ impl Reader for &[u8] {
}
}

#[cfg(feature = "base64")]
impl Reader for Base64Reader<'_> {
fn read<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8]> {
Ok(self.decode(out)?)
}

fn remaining_len(&self) -> usize {
self.remaining_len()
}
}

#[cfg(feature = "pem")]
impl Reader for pem::Decoder<'_> {
fn read<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8]> {
Expand Down
36 changes: 36 additions & 0 deletions ssh-encoding/src/reader/base64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Base64 reader support (constant-time).

use super::Reader;
use crate::Result;

/// Inner constant-time Base64 reader type from the `base64ct` crate.
type Inner<'i> = base64ct::Decoder<'i, base64ct::Base64>;

/// Constant-time Base64 reader implementation.
pub struct Base64Reader<'i> {
inner: Inner<'i>,
}

impl<'i> Base64Reader<'i> {
/// Create a new Base64 reader for a byte slice containing contiguous (non-newline-delimited)
/// Base64-encoded data.
///
/// # Returns
/// - `Ok(reader)` on success.
/// - `Err(Error::Base64)` if the input buffer is empty.
pub fn new(input: &'i [u8]) -> Result<Self> {
Ok(Self {
inner: Inner::new(input)?,
})
}
}

impl Reader for Base64Reader<'_> {
fn read<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8]> {
Ok(self.inner.decode(out)?)
}

fn remaining_len(&self) -> usize {
self.inner.remaining_len()
}
}
14 changes: 3 additions & 11 deletions ssh-encoding/src/writer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Writer trait and associated implementations.

#[cfg(feature = "base64")]
pub(crate) mod base64;

use crate::Result;

#[cfg(feature = "alloc")]
Expand All @@ -8,10 +11,6 @@ use alloc::vec::Vec;
#[cfg(feature = "sha2")]
use sha2::{Digest, Sha256, Sha512};

/// Constant-time Base64 writer implementation.
#[cfg(feature = "base64")]
pub type Base64Writer<'o> = base64::Encoder<'o, base64::Base64>;

/// Writer trait which encodes the SSH binary format to various output
/// encodings.
pub trait Writer: Sized {
Expand All @@ -27,13 +26,6 @@ impl Writer for Vec<u8> {
}
}

#[cfg(feature = "base64")]
impl Writer for Base64Writer<'_> {
fn write(&mut self, bytes: &[u8]) -> Result<()> {
Ok(self.encode(bytes)?)
}
}

#[cfg(feature = "pem")]
impl Writer for pem::Encoder<'_, '_> {
fn write(&mut self, bytes: &[u8]) -> Result<()> {
Expand Down
34 changes: 34 additions & 0 deletions ssh-encoding/src/writer/base64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! Base64 writer support (constant-time).

use super::Writer;
use crate::Result;

/// Inner constant-time Base64 reader type from the `base64ct` crate.
type Inner<'o> = base64ct::Encoder<'o, base64ct::Base64>;

/// Constant-time Base64 writer implementation.
pub struct Base64Writer<'o> {
inner: Inner<'o>,
}

impl<'o> Base64Writer<'o> {
/// Create a new Base64 writer which writes output to the given byte slice.
///
/// Output constructed using this method is not line-wrapped.
pub fn new(output: &'o mut [u8]) -> Result<Self> {
Ok(Self {
inner: Inner::new(output)?,
})
}

/// Finish encoding data, returning the resulting Base64 as a `str`.
pub fn finish(self) -> Result<&'o str> {
Ok(self.inner.finish()?)
}
}

impl Writer for Base64Writer<'_> {
fn write(&mut self, bytes: &[u8]) -> Result<()> {
Ok(self.inner.encode(bytes)?)
}
}