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
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ssh-cipher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ssh-cipher"
version = "0.3.0"
version = "0.4.0-pre"
description = """
Pure Rust implementation of SSH symmetric encryption including support for the modern
aes128-gcm@openssh.com/aes256-gcm@openssh.com and chacha20-poly1305@openssh.com algorithms as well
Expand All @@ -27,7 +27,7 @@ aes-gcm = { version = "0.11", optional = true, default-features = false, feature
ctutils = { version = "0.4.8", optional = true, default-features = false }
chacha20 = { version = "0.10", optional = true, default-features = false, features = ["cipher", "legacy"] }
des = { version = "0.9", optional = true, default-features = false }
encoding = { package = "ssh-encoding", version = "0.3", optional = true }
encoding = { package = "ssh-encoding", version = "0.4.0-pre", optional = true }
poly1305 = { version = "0.9", optional = true, default-features = false }
zeroize = { version = "1", optional = true, default-features = false }

Expand Down
2 changes: 1 addition & 1 deletion ssh-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ssh-derive"
version = "0.3.0"
version = "0.4.0-pre"
description = "Custom derive support for the `ssh-encoding` crate"
authors = ["RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
Expand Down
8 changes: 4 additions & 4 deletions ssh-derive/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn try_derive_decode_for_struct(
impl #impl_generics ::ssh_encoding::Decode for #struct_name #type_generics #where_clause {
type Error = ::ssh_encoding::Error;

fn decode(reader: &mut impl ::ssh_encoding::Reader) -> ::core::result::Result<Self, Self::Error> {
fn decode(reader: &mut ::ssh_encoding::Decoder<impl ::ssh_encoding::Reader>) -> ::core::result::Result<Self, Self::Error> {
#body
}
}
Expand Down Expand Up @@ -85,7 +85,7 @@ fn try_derive_decode_for_enum(
impl #impl_generics ::ssh_encoding::Decode for #enum_name #type_generics #where_clause {
type Error = ::ssh_encoding::Error;

fn decode(reader: &mut impl ::ssh_encoding::Reader) -> ::core::result::Result<Self, Self::Error> {
fn decode(reader: &mut ::ssh_encoding::Decoder<impl ::ssh_encoding::Reader>) -> ::core::result::Result<Self, Self::Error> {
#body
}
}
Expand Down Expand Up @@ -245,7 +245,7 @@ mod tests {
impl ::ssh_encoding::Decode for Foo {
type Error = ::ssh_encoding::Error;

fn decode(reader: &mut impl ::ssh_encoding::Reader) -> ::core::result::Result<Self, Self::Error> {
fn decode(reader: &mut ::ssh_encoding::Decoder<impl ::ssh_encoding::Reader>) -> ::core::result::Result<Self, Self::Error> {
Ok({
Self {
a: reader.read_prefixed(|reader| <u32 as ::ssh_encoding::Decode>::decode(reader))?,
Expand Down Expand Up @@ -274,7 +274,7 @@ mod tests {
impl ::ssh_encoding::Decode for Foo {
type Error = ::ssh_encoding::Error;

fn decode(reader: &mut impl ::ssh_encoding::Reader) -> ::core::result::Result<Self, Self::Error> {
fn decode(reader: &mut ::ssh_encoding::Decoder<impl ::ssh_encoding::Reader>) -> ::core::result::Result<Self, Self::Error> {
reader.read_prefixed(|reader| {
Ok::<_, ::ssh_encoding::Error>({
let discriminant = <u8 as ::ssh_encoding::Decode>::decode(reader)?;
Expand Down
4 changes: 2 additions & 2 deletions ssh-encoding/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ssh-encoding"
version = "0.3.0"
version = "0.4.0-pre"
description = "Pure Rust implementation of SSH data type decoders/encoders as described in RFC4251"
authors = ["RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
Expand All @@ -19,7 +19,7 @@ bytes = { version = "1.11", optional = true, default-features = false }
ctutils = { version = "0.4.8", optional = true, default-features = false }
digest = { version = "0.11", optional = true, default-features = false }
pem-rfc7468 = { version = "1", optional = true }
ssh-derive = { version = "0.3", optional = true }
ssh-derive = { version = "0.4.0-pre", optional = true }
zeroize = { version = "1.8", optional = true, default-features = false }

[dev-dependencies]
Expand Down
25 changes: 25 additions & 0 deletions ssh-encoding/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,31 @@
Pure Rust implementation of SSH data type decoders/encoders as described
in [RFC4251].

## Decoding

Convert a reader into a `Decoder` to start decoding SSH data types:

```rust
use ssh_encoding::{Decode, Reader};

let mut decoder = [0, 0, 0, 4, 0, 0, 0, 42].as_slice().into_decoder();
let value = decoder.read_prefixed(u32::decode).expect("invalid u32");
assert_eq!(decoder.finish(value).expect("remaining data"), 42);
```

`Reader` supplies bytes and their remaining length. `Decoder<R>` owns the reader and
handles nested bounds, complete consumption, and failure state. Nested callbacks use
the same decoder type and store enclosing bounds on the call stack without allocation.
`Decode` implementations accept `&mut Decoder<impl Reader>`.

The blanket `From<R>` implementation supports `.into()` wherever a decoder is
expected, for example `Message::decode(&mut bytes.into())`. For any `R: Reader`,
`Reader::into_decoder` also constructs a decoder without naming its type.

Raw byte slices implement `Reader`. With the corresponding features enabled,
`Base64Reader` decodes Base64 incrementally, and `DecodePem` wraps a PEM reader in
the same decoder internally.

## License

Licensed under either of:
Expand Down
25 changes: 13 additions & 12 deletions ssh-encoding/src/base64/reader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Base64 reader support (constant-time).

use crate::{Decode, Error, Reader, Result};
use crate::{Reader, Result};
use core::fmt::{self, Debug};

/// Inner constant-time Base64 reader type from the `base64ct` crate.
Expand All @@ -10,34 +10,35 @@ type Inner<'i> = base64ct::Decoder<'i, base64ct::Base64>;
pub struct Base64Reader<'i> {
/// Inner Base64 reader.
inner: Inner<'i>,

/// Custom length of remaining data, used for nested length-prefixed reading.
remaining_len: usize,
}

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

Ok(Self {
inner,
remaining_len,
})
Ok(Self { inner })
}
}

impl Debug for Base64Reader<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Base64Reader")
.field("remaining_len", &self.remaining_len)
.field("remaining_len", &self.remaining_len())
.finish_non_exhaustive()
}
}

impl_reader_for_newtype!(Base64Reader<'_>);
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()
}
}
26 changes: 13 additions & 13 deletions ssh-encoding/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//!
//! [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5

use crate::{Error, Result, reader::Reader};
use crate::{Decoder, Error, Reader, Result};

#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
Expand All @@ -21,18 +21,18 @@ pub trait Decode: Sized {
/// Type returned in the event of a decoding error.
type Error: From<Error>;

/// Attempt to decode a value of this type using the provided [`Reader`].
/// Attempt to decode a value of this type using the provided [`Decoder`].
///
/// # Errors
/// Returns errors specific to the concrete implementation of this trait.
fn decode(reader: &mut impl Reader) -> core::result::Result<Self, Self::Error>;
fn decode(reader: &mut Decoder<impl Reader>) -> core::result::Result<Self, Self::Error>;
}

/// Decode a single `byte` from the input data.
impl Decode for u8 {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let mut buf = [0];
reader.read(&mut buf)?;
Ok(buf[0])
Expand All @@ -50,7 +50,7 @@ impl Decode for u8 {
impl Decode for bool {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let byte = u8::decode(reader)?;
match byte {
0 => Ok(false),
Expand All @@ -69,7 +69,7 @@ impl Decode for bool {
impl Decode for u32 {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let mut bytes = [0u8; 4];
reader.read(&mut bytes)?;
Ok(u32::from_be_bytes(bytes))
Expand All @@ -85,7 +85,7 @@ impl Decode for u32 {
impl Decode for u64 {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let mut bytes = [0u8; 8];
reader.read(&mut bytes)?;
Ok(u64::from_be_bytes(bytes))
Expand All @@ -102,7 +102,7 @@ impl Decode for u64 {
impl Decode for usize {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let n = usize::try_from(u32::decode(reader)?)?;

if n <= MAX_USIZE {
Expand All @@ -126,7 +126,7 @@ impl Decode for usize {
impl<const N: usize> Decode for [u8; N] {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
let mut result = [0u8; N];
reader.read(&mut result)?;
Ok(result)
Expand All @@ -147,7 +147,7 @@ impl<const N: usize> Decode for [u8; N] {
impl Decode for Vec<u8> {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
reader.read_prefixed(|reader| {
let mut result = vec![0u8; reader.remaining_len()];
reader.read(&mut result)?;
Expand All @@ -160,7 +160,7 @@ impl Decode for Vec<u8> {
impl Decode for String {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
String::from_utf8(Vec::decode(reader)?).map_err(|_| Error::CharacterEncoding)
}
}
Expand All @@ -187,7 +187,7 @@ impl Decode for String {
impl Decode for Vec<String> {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
reader.read_prefixed(|reader| {
let mut entries = Self::new();

Expand All @@ -214,7 +214,7 @@ impl Decode for Vec<String> {
impl Decode for Bytes {
type Error = Error;

fn decode(reader: &mut impl Reader) -> Result<Self> {
fn decode(reader: &mut Decoder<impl Reader>) -> Result<Self> {
Vec::<u8>::decode(reader).map(Into::into)
}
}
Loading
Loading