Crate ssh_encoding

Source
Expand description

§RustCrypto: SSH Encoding

crate Docs Build Status Apache2/MIT licensed Rust Version Project Chat

Documentation

§About

Pure Rust implementation of SSH data type decoders/encoders as described in RFC4251.

§Minimum Supported Rust Version

This crate requires Rust 1.81 at a minimum.

We may change the MSRV in the future, but it will be accompanied by a minor version bump.

§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.

§Conventions used in this crate

This crate uses the following type labels which are described in RFC4251 § 5, and also lists types with Decode/Encode trait impls which are compatible with this format:

§byte, byte[n], byte[]: arbitrary 8-bit value (octet) or sequence thereof

§Decode/Encode trait impls:
  • byte: u8
  • byte[n]: [u8; N]
  • byte[]: Vec<u8>, bytes::Bytes (requires bytes crate feature)

Fixed length data is sometimes represented as an array of bytes, written byte[n] where n is the number of bytes in the array.

byte[] is a newer convention from OpenSSH for describing arbitrary length bytestrings (similar to string, see below) but identifies data which is inherently binary in nature, as opposed to text.

§boolean: boolean value stored as a single byte

§Decode/Encode trait impls: bool

The value 0 represents FALSE, and the value 1 represents TRUE. All non-zero values MUST be interpreted as TRUE; however, applications MUST NOT store values other than 0 and 1.

§uint32: 32-bit unsigned integer

§Decode/Encode trait impls: u32, usize

Stored as four bytes in the order of decreasing significance (network byte order).

For example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa.

§uint64: 64-bit unsigned integer

§Decode/Encode trait impls: u64

Stored as eight bytes in the order of decreasing significance (network byte order).

§string: arbitrary length binary string

§Decode/Encode trait impls: Vec<u8>, String, bytes::Bytes (requires bytes crate feature)

NOTE: string is effectively equivalent to byte[], however the latter is not defined in RFC4251 and so trait impls in this crate for bytestring types like [u8; N] and Vec<u8> are described as being impls of string.

Strings are allowed to contain arbitrary binary data, including null characters and 8-bit characters.

They are stored as a uint32 containing its length (number of bytes that follow) and zero (= empty string) or more bytes that are the value of the string. Terminating null characters are not used.

Strings are also used to store text. In that case, US-ASCII is used for internal names, and ISO-10646 UTF-8 for text that might be displayed to the user.

The terminating null character SHOULD NOT normally be stored in the string.

For example: the US-ASCII string “testing” is represented as 00 00 00 07 t e s t i n g. The UTF-8 mapping does not alter the encoding of US-ASCII characters.

§mpint: multiple precision integers in two’s complement format

§Decode/Encode trait impls: Mpint

Stored as a byte string, 8 bits per byte, MSB first (a.k.a. big endian).

Negative numbers have the value 1 as the most significant bit of the first byte of the data partition. If the most significant bit would be set for a positive number, the number MUST be preceded by a zero byte. Unnecessary leading bytes with the value 0 or 255 MUST NOT be included. The value zero MUST be stored as a string with zero bytes of data.

By convention, a number that is used in modular computations in Z_n SHOULD be represented in the range 0 <= x < n.

§Examples:
value (hex)representation (hex)
000 00 00 00
9a378f9b2e332a700 00 00 08 09 a3 78 f9 b2 e3 32 a7
8000 00 00 02 00 80
-123400 00 00 02 ed cc
-deadbeef00 00 00 05 ff 21 52 41 11

§name-list: string containing a comma-separated list of names

§Decode/Encode trait impls: Vec<String>

A name-list is represented as a uint32 containing its length (number of bytes that follow) followed by a comma-separated list of zero or more names. A name MUST have a non-zero length, and it MUST NOT contain a comma (“,”).

As this is a list of names, all the elements contained are names and MUST be in US-ASCII.

Context may impose additional restrictions on the names. For example, the names in a name-list may have to be a list of valid algorithm identifiers (see Section 6 below), or a list of RFC3066 language tags. The order of the names in a name-list may or may not be significant. Again, this depends on the context in which the list is used.

Terminating null characters MUST NOT be used, neither for the individual names, nor for the list as a whole.

§Examples:
valuerepresentation (hex)
(), the empty name-list00 00 00 00
("zlib")00 00 00 04 7a 6c 69 62
("zlib,none")00 00 00 09 7a 6c 69 62 2c 6e 6f 6e 65

§Deriving Decode and Encode

The traits Decode and Encode can be derived for any struct or enum where all its fields implement Decode and Encode respectively.

To use this functionality, enable the derive crate feature for ssh-encoding.

§Example

Here is an example of how you could define a handful of the SSH message types:

use ssh_encoding::{Decode, Encode};

#[derive(Debug, PartialEq, Encode, Decode)]
#[repr(u8)]
enum Message {
    Disconnect {
        reason_code: u32,
        description: String,
        language_tag: String,
    } = 1,
    EcdhInit {
        client_public_key: Vec<u8>,
    } = 30,
    EcdhReply {
        host_key: HostKey,
        server_public_key: Vec<u8>,
        #[ssh(length_prefixed)]
        host_signature: HostSignature,
    } = 31,
}

#[derive(Debug, PartialEq, Encode, Decode)]
#[ssh(length_prefixed)]
struct HostKey {
    key_type: String,
    ecdsa_curve_identifier: String,
    ecdsa_public_key: Vec<u8>,
}

#[derive(Debug, PartialEq, Encode, Decode)]
struct HostSignature {
    signature_type: String,
    signature: Vec<u8>,
}

let message = Message::EcdhReply {
    host_key: HostKey {
        key_type: "ecdsa-sha2-nistp256".into(),
        ecdsa_curve_identifier: "nistp256".into(),
        ecdsa_public_key: vec![0x01, 0x02, 0x03],
    },
    server_public_key: vec![0x04, 0x05, 0x06],
    host_signature: HostSignature {
        signature_type: "ecdsa-sha2-nistp256".into(),
        signature: vec![0x07, 0x08, 0x09],
    },
};

let encoded = message.encode_vec().unwrap();
assert_eq!(&encoded[..13], &[31, 0, 0, 0, 42, 0, 0, 0, 19, 101, 99, 100, 115]);
let decoded = Message::decode(&mut &encoded[..]).unwrap();
assert_eq!(message, decoded);

Re-exports§

pub use crate::base64::Base64Reader;base64
pub use crate::base64::Base64Writer;base64
pub use crate::pem::DecodePem;pem
pub use crate::pem::EncodePem;pem
pub use bigint;bigint
pub use bytes;bytes
pub use digest;digest

Modules§

base64base64
Base64 support.
pempem
PEM encoding support.

Structs§

DigestWriterdigest
Wrapper for digests.
LabelError
Errors related to labels.
Mpintalloc
Multiple precision integer, a.k.a. mpint.
Uintbigint
Fixed-precision heap-allocated big unsigned integer.

Enums§

Error
Error type.

Traits§

CheckedSum
Extension trait for providing checked Iterator::sum-like functionality.
Decode
Decoding trait.
Encode
Encoding trait.
Label
Labels for e.g. cryptographic algorithms.
Reader
Reader trait which decodes the binary SSH protocol serialization from various inputs.
Writer
Writer trait which encodes the SSH binary format to various output encodings.

Type Aliases§

NonZeroUintbigint
Non-zero Uint.
OddUintbigint
Odd Uint.
Result
Result type with ssh-encoding crate’s Error as the error type.

Derive Macros§

Decodederive
Derive the Decode trait on a struct.
Encodederive
Derive the Encode trait on a struct.