ssh_encoding/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
7)]
8
9//! ## Conventions used in this crate
10//!
11//! This crate uses the following type labels which are described in [RFC4251 § 5], and also lists
12//! types with [`Decode`]/[`Encode`] trait impls which are compatible with this format:
13//!
14//! ### `byte`, `byte[n]`, `byte[]`: arbitrary 8-bit value (octet) or sequence thereof
15//! #### [`Decode`]/[`Encode`] trait impls:
16//! - `byte`: `u8`
17//! - `byte[n]`: `[u8; N]`
18//! - `byte[]`: `Vec<u8>`, `bytes::Bytes` (requires `bytes` crate feature)
19//!
20//! Fixed length data is sometimes represented as an array of bytes, written
21//! `byte[n]` where `n` is the number of bytes in the array.
22//!
23//! `byte[]` is a newer convention from OpenSSH for describing arbitrary
24//! length bytestrings (similar to `string`, see below) but identifies data
25//! which is inherently binary in nature, as opposed to text.
26//!
27//!
28//! ### `boolean`: boolean value stored as a single byte
29//! #### [`Decode`]/[`Encode`] trait impls: `bool`
30//!
31//! The value 0 represents FALSE, and the value 1 represents TRUE. All non-zero
32//! values MUST be interpreted as TRUE; however, applications MUST NOT
33//! store values other than 0 and 1.
34//!
35//! ### `uint32`: 32-bit unsigned integer
36//! #### [`Decode`]/[`Encode`] trait impls: `u32`, `usize`
37//!
38//! Stored as four bytes in the order of decreasing significance (network byte order).
39//!
40//! For example: the value `699921578` (`0x29b7f4aa`) is stored as
41//! `29 b7 f4 aa`.
42//!
43//! ### `uint64`: 64-bit unsigned integer
44//! #### [`Decode`]/[`Encode`] trait impls: `u64`
45//!
46//! Stored as eight bytes in the order of decreasing significance (network byte order).
47//!
48//! ### `string`: arbitrary length *binary* string
49//! #### [`Decode`]/[`Encode`] trait impls: `Vec<u8>`, `String`, `bytes::Bytes` (requires `bytes` crate feature)
50//!
51//! *NOTE: `string` is effectively equivalent to `byte[]`, however the latter is not defined in
52//! [RFC4251] and so trait impls in this crate for bytestring types like `[u8; N]` and `Vec<u8>`
53//! are described as being impls of `string`*.
54//!
55//! Strings are allowed to contain arbitrary binary data, including null characters and 8-bit
56//! characters.
57//!
58//! They are stored as a `uint32` containing its length (number of bytes that follow) and
59//! zero (= empty string) or more bytes that are the value of the string. Terminating null
60//! characters are not used.
61//!
62//! Strings are also used to store text. In that case, US-ASCII is used for internal names, and
63//! ISO-10646 UTF-8 for text that might be displayed to the user.
64//!
65//! The terminating null character SHOULD NOT normally be stored in the string.
66//!
67//! For example: the US-ASCII string "testing" is represented as `00 00 00 07 t e s t i n g`.
68//! The UTF-8 mapping does not alter the encoding of US-ASCII characters.
69//!
70//! ### `mpint`: multiple precision integers in two's complement format
71//! #### [`Decode`]/[`Encode`] trait impls: `Mpint`
72//!
73//! Stored as a byte string, 8 bits per byte, MSB first (a.k.a. big endian).
74//!
75//! Negative numbers have the value 1 as the most significant bit of the first byte of
76//! the data partition. If the most significant bit would be set for
77//! a positive number, the number MUST be preceded by a zero byte.
78//! Unnecessary leading bytes with the value 0 or 255 MUST NOT be
79//! included. The value zero MUST be stored as a string with zero
80//! bytes of data.
81//!
82//! By convention, a number that is used in modular computations in
83//! `Z_n` SHOULD be represented in the range `0 <= x < n`.
84//!
85//! #### Examples:
86//!
87//! value (hex) | representation (hex)
88//! -------------------|---------------------
89//! `0` | `00 00 00 00`
90//! `9a378f9b2e332a7` | `00 00 00 08 09 a3 78 f9 b2 e3 32 a7`
91//! `80` | `00 00 00 02 00 80`
92//! `-1234` | `00 00 00 02 ed cc`
93//! `-deadbeef` | `00 00 00 05 ff 21 52 41 11`
94//!
95//! ### `name-list`: string containing a comma-separated list of names
96//! #### [`Decode`]/[`Encode`] trait impls: `Vec<String>`
97//!
98//! A `name-list` is represented as a `uint32` containing its length
99//! (number of bytes that follow) followed by a comma-separated list of zero or more
100//! names. A name MUST have a non-zero length, and it MUST NOT
101//! contain a comma (",").
102//!
103//! As this is a list of names, all the elements contained are names and MUST be in US-ASCII.
104//!
105//! Context may impose additional restrictions on the names. For example,
106//! the names in a name-list may have to be a list of valid algorithm
107//! identifiers (see Section 6 below), or a list of [RFC3066] language
108//! tags. The order of the names in a name-list may or may not be
109//! significant. Again, this depends on the context in which the list
110//! is used.
111//!
112//! Terminating null characters MUST NOT be used, neither
113//! for the individual names, nor for the list as a whole.
114//!
115//! #### Examples:
116//!
117//! value | representation (hex)
118//! ---------------------------|---------------------
119//! `()`, the empty name-list | `00 00 00 00`
120//! `("zlib")` | `00 00 00 04 7a 6c 69 62`
121//! `("zlib,none")` | `00 00 00 09 7a 6c 69 62 2c 6e 6f 6e 65`
122//!
123//! [RFC3066]: https://datatracker.ietf.org/doc/html/rfc3066
124//! [RFC4251]: https://datatracker.ietf.org/doc/html/rfc4251
125//! [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
126//!
127//! ## Deriving [`Decode`] and [`Encode`]
128//!
129//! The traits [`Decode`] and [`Encode`] can be derived for any struct or enum where all its fields
130//! implement [`Decode`] and [`Encode`] respectively.
131//!
132//! To use this functionality, enable the `derive` crate feature for `ssh-encoding`.
133//!
134//! ### Example
135//!
136//! Here is an example of how you could define a handful of the SSH message types:
137//!
138#![cfg_attr(all(feature = "alloc", feature = "derive"), doc = "```")]
139#![cfg_attr(not(all(feature = "alloc", feature = "derive")), doc = "```ignore")]
140//! use ssh_encoding::{Decode, Encode};
141//!
142//! #[derive(Debug, PartialEq, Encode, Decode)]
143//! #[repr(u8)]
144//! enum Message {
145//! Disconnect {
146//! reason_code: u32,
147//! description: String,
148//! language_tag: String,
149//! } = 1,
150//! EcdhInit {
151//! client_public_key: Vec<u8>,
152//! } = 30,
153//! EcdhReply {
154//! host_key: HostKey,
155//! server_public_key: Vec<u8>,
156//! #[ssh(length_prefixed)]
157//! host_signature: HostSignature,
158//! } = 31,
159//! }
160//!
161//! #[derive(Debug, PartialEq, Encode, Decode)]
162//! #[ssh(length_prefixed)]
163//! struct HostKey {
164//! key_type: String,
165//! ecdsa_curve_identifier: String,
166//! ecdsa_public_key: Vec<u8>,
167//! }
168//!
169//! #[derive(Debug, PartialEq, Encode, Decode)]
170//! struct HostSignature {
171//! signature_type: String,
172//! signature: Vec<u8>,
173//! }
174//!
175//! let message = Message::EcdhReply {
176//! host_key: HostKey {
177//! key_type: "ecdsa-sha2-nistp256".into(),
178//! ecdsa_curve_identifier: "nistp256".into(),
179//! ecdsa_public_key: vec![0x01, 0x02, 0x03],
180//! },
181//! server_public_key: vec![0x04, 0x05, 0x06],
182//! host_signature: HostSignature {
183//! signature_type: "ecdsa-sha2-nistp256".into(),
184//! signature: vec![0x07, 0x08, 0x09],
185//! },
186//! };
187//!
188//! let encoded = message.encode_vec().unwrap();
189//! assert_eq!(&encoded[..13], &[31, 0, 0, 0, 42, 0, 0, 0, 19, 101, 99, 100, 115]);
190//! let decoded = Message::decode(&mut &encoded[..]).unwrap();
191//! assert_eq!(message, decoded);
192//! ```
193
194#[cfg(feature = "alloc")]
195#[macro_use]
196extern crate alloc;
197
198mod checked;
199mod decode;
200mod encode;
201mod error;
202mod label;
203#[cfg(feature = "alloc")]
204mod mpint;
205#[macro_use]
206mod reader;
207mod writer;
208
209#[cfg(feature = "base64")]
210pub mod base64;
211#[cfg(feature = "pem")]
212pub mod pem;
213
214pub use crate::{
215 checked::CheckedSum,
216 decode::Decode,
217 encode::Encode,
218 error::{Error, Result},
219 label::{Label, LabelError},
220 reader::Reader,
221 writer::Writer,
222};
223
224#[cfg(feature = "alloc")]
225pub use crate::mpint::Mpint;
226
227#[cfg(feature = "base64")]
228pub use crate::{base64::Base64Reader, base64::Base64Writer};
229
230#[cfg(feature = "bigint")]
231pub use bigint;
232
233#[cfg(feature = "bytes")]
234pub use bytes;
235
236#[cfg(feature = "digest")]
237pub use crate::writer::DigestWriter;
238#[cfg(feature = "digest")]
239pub use digest;
240
241#[cfg(feature = "pem")]
242pub use crate::pem::{DecodePem, EncodePem};
243
244#[cfg(feature = "derive")]
245pub use ssh_derive::{Decode, Encode};
246
247#[cfg(all(doc, feature = "alloc"))]
248use alloc::vec::Vec;
249
250#[cfg(feature = "bigint")]
251pub use bigint::BoxedUint as Uint;