Update the SSH documentation.

This commit is contained in:
2019-05-27 20:28:34 -07:00
parent 4d2e43620a
commit ba2ceee725
3 changed files with 50 additions and 1 deletions

View File

@@ -2,6 +2,8 @@ use base64::DecodeError;
use ed25519::ED25519PublicImportError;
use std::io;
/// A whole pile of errors that you can get when parsing an SSH key from
/// disk or memory.
#[derive(Debug)]
pub enum SSHKeyParseError
{
@@ -49,6 +51,8 @@ impl From<ED25519PublicImportError> for SSHKeyParseError {
}
}
/// A much smaller set of errors you can get when rendering an SSH key into
/// a file or memory block.
#[derive(Debug)]
pub enum SSHKeyRenderError {
IOError(io::Error),

View File

@@ -13,6 +13,9 @@ use std::iter::Iterator;
const OPENER: &'static str = "-----BEGIN OPENSSH PRIVATE KEY-----\n";
const CLOSER: &'static str = "-----END OPENSSH PRIVATE KEY-----";
/// Given a string defining an ASCII SSH key blob (one that starts with
/// "--BEGIN..."), decode the body of the blob and return it as binary
/// data.
pub fn parse_ssh_private_key_data(s: &str) -> Result<Vec<u8>,SSHKeyParseError>
{
if s.starts_with(OPENER) {
@@ -28,6 +31,8 @@ pub fn parse_ssh_private_key_data(s: &str) -> Result<Vec<u8>,SSHKeyParseError>
}
}
/// Once you've figured out the binary data you want to produce for an SSH key
/// blob, use this routine to render it into its ASCII encoding.
pub fn render_ssh_private_key_data(bytes: &[u8]) -> String
{
let mut bytestr = encode(bytes);
@@ -52,6 +57,7 @@ pub fn render_ssh_private_key_data(bytes: &[u8]) -> String
const OPENSSH_MAGIC_HEADER: &'static str = "openssh-key-v1\0";
const OPENSSH_MAGIC_HEADER_LEN: usize = 15;
/// Parse the magic header in an SSH key file.
pub fn parse_openssh_header<R: Read>(input: &mut R) -> Result<(),SSHKeyParseError>
{
let mut limited_input_header = input.take(OPENSSH_MAGIC_HEADER_LEN as u64);
@@ -70,6 +76,7 @@ pub fn parse_openssh_header<R: Read>(input: &mut R) -> Result<(),SSHKeyParseErro
Ok(())
}
/// Render the magic header in an SSH key file.
pub fn render_openssh_header<O: Write>(output: &mut O) -> Result<(),SSHKeyRenderError>
{
Ok(output.write_all(OPENSSH_MAGIC_HEADER.as_bytes())?)
@@ -77,6 +84,8 @@ pub fn render_openssh_header<O: Write>(output: &mut O) -> Result<(),SSHKeyRender
//------------------------------------------------------------------------------
/// Parse an unsigned u32 from the SSH key stream. (This does the appropriate
/// conversion from network order to native order.)
pub fn parse_openssh_u32<I: Read>(input: &mut I) -> Result<u32,SSHKeyParseError>
{
let mut limited_input_header = input.take(4);
@@ -84,6 +93,8 @@ pub fn parse_openssh_u32<I: Read>(input: &mut I) -> Result<u32,SSHKeyParseError>
Ok(res)
}
/// Render an unsigned u32 from the SSH key stream. (This does the appropriate
/// conversion from network order to native order.)
pub fn render_openssh_u32<O: Write>(output: &mut O, val: u32) -> Result<(),SSHKeyRenderError>
{
Ok(output.write_u32::<BigEndian>(val)?)
@@ -91,6 +102,9 @@ pub fn render_openssh_u32<O: Write>(output: &mut O, val: u32) -> Result<(),SSHKe
//------------------------------------------------------------------------------
/// Parse a string from the SSH key stream. This does some validation to ensure
/// that the data being read is actually in a form that Rust will recognize as
/// being a valid string.
pub fn parse_openssh_string<I: Read>(input: &mut I) -> Result<String,SSHKeyParseError>
{
let length = parse_openssh_u32(input)?;
@@ -100,6 +114,7 @@ pub fn parse_openssh_string<I: Read>(input: &mut I) -> Result<String,SSHKeyParse
Ok(result)
}
/// Render a string into the SSH key stream.
pub fn render_openssh_string<O: Write>(output: &mut O, v: &str) -> Result<(),SSHKeyRenderError>
{
let vbytes: Vec<u8> = v.bytes().collect();
@@ -116,6 +131,7 @@ pub fn render_openssh_string<O: Write>(output: &mut O, v: &str) -> Result<(),SSH
//------------------------------------------------------------------------------
/// Read a buffer from the SSH key stream.
pub fn parse_openssh_buffer<I: Read>(input: &mut I) -> Result<Vec<u8>,SSHKeyParseError>
{
let length = parse_openssh_u32(input)?;
@@ -125,6 +141,7 @@ pub fn parse_openssh_buffer<I: Read>(input: &mut I) -> Result<Vec<u8>,SSHKeyPars
Ok(res)
}
/// Render a buffer into the SSH key stream.
pub fn render_openssh_buffer<O: Write>(output: &mut O, b: &[u8]) -> Result<(),SSHKeyRenderError>
{
if b.len() > 0xFFFFFFFF {
@@ -141,6 +158,7 @@ pub fn render_openssh_buffer<O: Write>(output: &mut O, b: &[u8]) -> Result<(),SS
//------------------------------------------------------------------------------
/// Parse a fixed-width number from the SSH key stream and return it.
pub fn parse_openssh_number<I,D>(input: &mut I) -> Result<D,SSHKeyParseError>
where
I: Read,
@@ -151,6 +169,7 @@ pub fn parse_openssh_number<I,D>(input: &mut I) -> Result<D,SSHKeyParseError>
Ok(D::from_bytes(&buffer))
}
/// Render a fixed-width number into the SSH key stream.
pub fn render_openssh_number<O,D>(output: &mut O, n: &D) -> Result<(),SSHKeyRenderError>
where
O: Write,

View File

@@ -2,7 +2,7 @@ mod dsa;
mod ecdsa;
mod ed25519;
mod errors;
mod frame;
pub mod frame;
mod rsa;
pub use self::errors::{SSHKeyParseError,SSHKeyRenderError};
@@ -14,16 +14,35 @@ use std::io::{Cursor,Read,Write};
use std::path::Path;
use super::KeyPair;
/// A trait defining keys that can be parsed / rendered by this library. Note
/// that you probably don't want to use these routines directly; they're mostly
/// used by the internal functions. Perhaps the only reason to use them is to
/// implement them, because you've got another kind of key you want to parse that
/// isn't already part of the library. (In that case, though ... maybe send a
/// patch?)
pub trait SSHKey: Sized + KeyPair {
/// Return true if the given string is a valid key type identifier for this
/// key type. (i.e., "ssh-ed25519" is the identifier for ED25519, and "dss"
/// and "ssh-dss" are both valid identifiers for DSA keys.)
fn valid_keytype(s: &str) -> bool;
/// Parse the public blob info within an SSH blob. I strongly recommend
/// using the functions in `ssh::frame` for this.
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>;
/// Parse the private blob info within an SSH blob. I strongly recommend
/// using the functions in `ssh::frame` for this.
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>;
/// Render the public blob info within an SSH blob. I strongly recommend
/// using the functions in `ssh::frame` for this.
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>;
/// Render the private blob info within an SSH blob. I strongly recommend
/// using the functions in `ssh::frame` for this.
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>;
}
/// Decode a string containing a private key into the appropriate key type and
/// the comment associated with it, usually an email address or similar.
pub fn decode_ssh<KP: SSHKey>(x: &str) -> Result<(KP, String),SSHKeyParseError>
{
let bytes = parse_ssh_private_key_data(x)?;
@@ -61,6 +80,8 @@ pub fn decode_ssh<KP: SSHKey>(x: &str) -> Result<(KP, String),SSHKeyParseError>
Ok((KP::new(public, private), comment))
}
/// Decode a string containing a public key into an appropriate key type and
/// the comment associated with it, usually an email address or similar.
pub fn decode_ssh_pubkey<KP: SSHKey>(s: &str) -> Result<(KP::Public, String),SSHKeyParseError>
{
let mut splitter = s.split_whitespace();
@@ -82,6 +103,8 @@ pub fn decode_ssh_pubkey<KP: SSHKey>(s: &str) -> Result<(KP::Public, String),SSH
}
}
/// Load an SSH private key file, returning the appropriate key type and the
/// comment associated with it.
pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError>
where
KP: SSHKey,
@@ -93,6 +116,7 @@ pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError>
decode_ssh(&contents)
}
/// Load all the public keys from a file into memory.
pub fn load_ssh_pubkeys<KP,P>(path: P) -> Result<Vec<(KP::Public, String)>,SSHKeyParseError>
where
KP: SSHKey,
@@ -110,6 +134,7 @@ pub fn load_ssh_pubkeys<KP,P>(path: P) -> Result<Vec<(KP::Public, String)>,SSHKe
Ok(result)
}
/// Encode a supported key into its ASCII SSH format, with the given comment.
pub fn encode_ssh<KP: SSHKey>(x: &KP, comment: &str) -> Result<String,SSHKeyRenderError>
{
let mut pubkeybin = Vec::with_capacity(8192);
@@ -140,6 +165,7 @@ pub fn encode_ssh<KP: SSHKey>(x: &KP, comment: &str) -> Result<String,SSHKeyRend
Ok(render_ssh_private_key_data(&binary))
}
/// Encode a supported key into the given file, with the given comment.
pub fn write_ssh_keyfile<KP,P>(path: P, x: &KP, comment: &str) -> Result<(),SSHKeyRenderError>
where
KP: SSHKey,