Expand description
§serde-sshfmt
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 =falseand 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 astring, 8 bits per byte, MSB first.name-list: Astringcontaining 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:
boolis supported.- From the integers only
u8,u32andu64are supported. charis supported and encoded as a 32-bit unsigned integer.- Strings and byte sequences are fully supported.
Optionis always supported for serialization (Noneis omitted whileSome(v)has the same encoding asv), 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/deserializebyte[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. ImplementSerializeandDeserializemanually to provide the correct values.
Additionally this crate provides some specialized types:
- For
name-listsemantics, thename_listmodule provides types that guarantee correct serialization and deserialization. - For
mpintsemantics, thempintmodule provides a wrapper type to help handling bigint types. - To help with implementing the SFTP protocol, the
pathmodule provides newtypes forstd::path::Pathandstd::path::PathBufthat 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-versionNote 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 thepathmodule (enabled by default)alloc: use theallocmodule; enables most features that don’t depend on an operating system.crypto-bigint: enablesmpint-encoding support for thecrypto-bigintlibrary.
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_formatonly supports the OpenSSH mux protocol variant of this format and doesn’t provide no-std support.ssh-encodinghas no-std support and focuses on the RFC 4251 standard, but provides noserde-compatible serializer or deserializer.russh-sftpfocuses on the SFTP protocol and doesn’t provide no-std nor zero-copy support.
§License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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-bigintpub 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). - path
std - 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
bufferin 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_vec
allocorstd - Serialize the given data structure as a byte vector in standard SSH wire format.
- to_
vec_ with_ len_ prefix allocorstd - 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.