Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

Crate serde_sshfmt

Crate serde_sshfmt 

Source
Expand description

§serde-sshfmt

Documentation Apache2/MIT licensed

serde-sshfmt is a #![no_std] compatible serializer and deserializer for the SSH wire format used in the SSH protocol for key exchange and negotiation, in the SSH agent protocol, in the SFTP protocol and for communication with the OpenSSH mux server.

§The SSH wire format

The SSH wire format is defined in RFC 4251.

It is a non-self-describing, binary format for which 8 data types are specified:

  • byte: An arbitrary 8-bit value (octet).
  • byte[n]: Fixed length binary data.
  • boolean: A boolean value stored as a single byte with 0 = false and 1 = true.
  • uint32: A 32-bit unsigned integer in network byte order.
  • uint64: A 64-bit unsigned integer in network byte order.
  • string: Arbitrary length binary string. They are stored as a uint32 containing its length and zero or more bytes of data.
  • mpint: A multiple precision integer in two’s complement format, stored as a string, 8 bits per byte, MSB first.
  • name-list: A string containing a comma-separated list of names. Names have non-zero length, are always encoded in US-ASCII and don’t contain control characters or whitespace.

The OpenSSH mux protocol uses a slight variation of the format, where boolean values are stored as 32-bit integers. The mux module provides serializer and deserializer for this format variant.

§Serialization/Deserialization

Because of the limited data types available in the format, as described above, the following notes and restrictions apply:

  • bool is supported.
  • From the integers only u8, u32 and u64 are supported.
  • char is supported and encoded as a 32-bit unsigned integer.
  • Strings and byte sequences are fully supported.
  • Option is always supported for serialization (None is omitted while Some(v) has the same encoding as v), but can only be deserialized with #[serde(rename = "sshfmt:tail")].
  • Structs and tuples are encoded as their contents in-order, unit structs/tuples are omitted.
  • Fixed length byte arrays ([u8; N]) are handled like tuples by serde. This means [u8; N] is one possible way to serialize/deserialize byte[n].
  • Sequences are encoded with a length-prefix. This is most certainly only correct for byte string sequences in the SSH wire format. Use #[serde(rename = "sshfmt:tail")] for variable sequences of elements at the end of a packet that don’t have a length-prefix.
  • Maps are only supported with #[serde(rename = "sshfmt:tail")].
  • Enums are only supported if renamed to "sshfmt:enum8" or "sshfmt:enum32". Note that Serde’s derive macros use the variant index; not the enum discriminant. Implement Serialize and Deserialize manually to provide the correct values.

Additionally this crate provides some specialized types:

  • For name-list semantics, the name_list module provides types that guarantee correct serialization and deserialization.
  • For mpint semantics, the mpint module provides a wrapper type to help handling bigint types.
  • To help with implementing the SFTP protocol, the path module provides newtypes for std::path::Path and std::path::PathBuf that allow non-UTF-8 bytes on serialization and deserialization, as far as that is supported by the platform and the standard library (e.g. on unix-like platforms and wasm).

To support the different uses and variable data structures, this crate use special type and field names for use with #[serde(rename = "…")]:

§"sshfmt:tail"

May only be used on the last field of a struct. This signifies that the field is variable length without storing a length in the data stream.

This means that the length is not serialized before payload of variable length types (strings, byte strings, sequences, …) and that the whole remaining input will be used when deserializing the value of this field.

Some types like Option, HashMap and Vec<{complex type}> are only fully usable as a sshfmt:tail field.

§Usage example

The payload of the SSH_FXP_VERSION packet in the SFTP protocol is described as follows:

uint32		3		/* protocol version */
string		ext1-name
string		ext1-version
string		ext2-name
string		ext2-version
...
string		extN-name
string		extN-version

Note that there’s no length information preceeding the variable extension fields.

This data structure can be mapped with

#[derive(serde::Serialize, serde::Deserialize)]
struct VersionData<'a> {
	protocol_version: u32,
	#[serde(rename = "sshfmt:tail")]
	extensions: Vec<(&'a str, &'a [u8])>
}

or e.g.

#[derive(serde::Serialize, serde::Deserialize)]
struct VersionData {
	protocol_version: u32,
	#[serde(rename = "sshfmt:tail")]
	extensions: HashMap<String, Vec<u8>>
}

§"sshfmt:enum8"

May only be used on enums. This signifies that the variant index should be encoded as a byte.

This more compact encoding is mostly used for packets in the SSH transport layer protocol and in the SSH agent protocol.

§"sshfmt:enum32"

May only be used on enums. This signifies that the variant index should be encoded as a uint32.

This encoding is used e.g. in the OpenSSH mux protocol and the SFTP protocol.

§Usage example

const SSH_FXP_VERSION: u32 = 2;

enum SftpResponsePacket {
	Version(VersionData),
	// …
}

impl Serialize for SftpResponsePacket {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: Serializer,
	{
		match self {
			Self::Version(version_data) => serializer.serialize_newtype_variant(
				"sshfmt:enum32",
				SSH_FXP_VERSION,
				"Version" /* variant name is ignored */,
				version_data
			),
			// …
		}
	}
}

§Reserved names

All field and type names starting with sshfmt: not mentioned above are considered reserved by this crate and will cause an error on serialization.

§Feature flags

This crate supports three feature flags:

  • std: use the full standard library; enables the path module (enabled by default)
  • alloc: use the alloc module; enables most features that don’t depend on an operating system.
  • crypto-bigint: enables mpint-encoding support for the crypto-bigint library.

As std is a default feature, for no-std support you need to use default-features = false in the [dependencies] section of your Cargo.toml. In no-std mode and without the alloc feature, allocating types are not available and error reporting is limited, but basic serialization and deserialization is supported.

§Comparison with similar crates

  • ssh_format only supports the OpenSSH mux protocol variant of this format and doesn’t provide no-std support.
  • ssh-encoding has no-std support and focuses on the RFC 4251 standard, but provides no serde-compatible serializer or deserializer.
  • russh-sftp focuses on the SFTP protocol and doesn’t provide no-std nor zero-copy support.

§License

Licensed under either of

at your option.

§Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Re-exports§

pub use crypto_bigint;crypto-bigint
pub use serde;
pub use bytes;

Modules§

de
Deserialize SSH wire format data into a Rust data structure.
flavor
Marker types for SSH wire format variants.
mpint
Multi-precision integers (SSH mpint).
mux
Type variants for the OpenSSH mux protocol
name_list
Comma-separated name lists (SSH name-list).
pathstd
Filesystem paths (SFTP “file names”).
ser
Serialize a Rust data structure into SSH wire format data.

Functions§

from_slice
Deserialize an instance of type T from bytes of standard SSH wire format data.
from_slice_with_len_prefix
Deserialize an instance of type T from bytes of standard SSH wire format data with a 32-bit length prefix.
to_buf
Serialize the given data structure into buffer in standard SSH wire format.
to_slice
Serialize the given data structure into a [MaybeUninit<u8>] buffer in standard SSH wire format.
to_slice_with_len_prefix
Serialize the given data structure into a [MaybeUninit<u8>] buffer in standard SSH wire format with a 32-bit length prefix.
to_vecalloc or std
Serialize the given data structure as a byte vector in standard SSH wire format.
to_vec_with_len_prefixalloc or std
Serialize the given data structure as a byte vector in standard SSH wire format with a 32-bit length prefix.

Type Aliases§

Deserializer
A structure that deserializes standard SSH wire format data into Rust values.
Serializer
A structure for serializing Rust values into the standard SSH wire format.