[CHECKPOINT] Starting to parse these things.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
//! that a new user should use, along with documentation regarding how and
|
||||
//! when they should use it, and examples. For now, it mostly just fowards
|
||||
//! off to more detailed modules. Help requested!
|
||||
extern crate base64;
|
||||
extern crate byteorder;
|
||||
extern crate chrono;
|
||||
extern crate cryptonum;
|
||||
|
||||
42
src/ssh/errors.rs
Normal file
42
src/ssh/errors.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use base64::DecodeError;
|
||||
use simple_asn1::ASN1DecodeErr;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SSHKeyParseError
|
||||
{
|
||||
ASN1Error(ASN1DecodeErr),
|
||||
DecodeError(DecodeError),
|
||||
IOError(io::Error),
|
||||
NoBeginBannerFound, NoEndBannerFound,
|
||||
NoOpenSSHMagicHeader
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for SSHKeyParseError {
|
||||
fn from(e: ASN1DecodeErr) -> SSHKeyParseError {
|
||||
println!("asn1 error: {:?}", e);
|
||||
SSHKeyParseError::ASN1Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecodeError> for SSHKeyParseError {
|
||||
fn from(e: DecodeError) -> SSHKeyParseError {
|
||||
SSHKeyParseError::DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SSHKeyParseError {
|
||||
fn from(e: io::Error) -> SSHKeyParseError {
|
||||
SSHKeyParseError::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SSHKeyRenderError {
|
||||
IOError(io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for SSHKeyRenderError {
|
||||
fn from(e: io::Error) -> SSHKeyRenderError {
|
||||
SSHKeyRenderError::IOError(e)
|
||||
}
|
||||
}
|
||||
134
src/ssh/frame.rs
Normal file
134
src/ssh/frame.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use base64::{decode,encode};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
use std::io::Cursor;
|
||||
#[cfg(test)]
|
||||
use std::fs::File;
|
||||
#[cfg(test)]
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
|
||||
const OPENER: &'static str = "-----BEGIN OPENSSH PRIVATE KEY-----\n";
|
||||
const CLOSER: &'static str = "-----END OPENSSH PRIVATE KEY-----";
|
||||
|
||||
pub fn parse_ssh_private_key_data(s: &str) -> Result<Vec<u8>,SSHKeyParseError>
|
||||
{
|
||||
if s.starts_with(OPENER) {
|
||||
if let Some(endidx) = s.find(CLOSER) {
|
||||
let b64str: String = s[OPENER.len()..endidx].chars().filter(|x| *x != '\n').collect();
|
||||
let bytes = decode(&b64str)?;
|
||||
Ok(bytes)
|
||||
} else {
|
||||
Err(SSHKeyParseError::NoEndBannerFound)
|
||||
}
|
||||
} else {
|
||||
Err(SSHKeyParseError::NoBeginBannerFound)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_ssh_private_key_data(bytes: &[u8]) -> String
|
||||
{
|
||||
let mut bytestr = encode(bytes);
|
||||
let mut output = String::new();
|
||||
|
||||
output.push_str(OPENER);
|
||||
while bytestr.len() > 70 {
|
||||
let rest = bytestr.split_off(70);
|
||||
output.push_str(&bytestr);
|
||||
output.push_str("\n");
|
||||
bytestr = rest;
|
||||
}
|
||||
output.push_str(&bytestr);
|
||||
output.push_str("\n");
|
||||
output.push_str(CLOSER);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const OPENSSH_MAGIC_HEADER: &'static str = "openssh-key-v1\0";
|
||||
|
||||
pub fn parse_openssh_header(input: &mut Cursor<Vec<u8>>) -> Result<(),SSHKeyParseError>
|
||||
{
|
||||
let input_header = input.take(OPENSSH_MAGIC_HEADER.len()).bytes();
|
||||
if input_header.eq(OPENSSH_MAGIC_HEADER.as_bytes().iter()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SSHKeyParseError::NoOpenSSHMagicHeader)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_openssh_header<O: Write>(output: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
Ok(output.write_all(OPENSSH_MAGIC_HEADER.as_bytes())?)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
pub fn parse_openssh_u32(input: &mut Cursor<Vec<u8>>) -> Result<u32,SSHKeyParseError>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
pub fn parse_openssh_string(input: &mut Cursor<Vec<u8>>) -> Result<(),SSHKeyParseError>
|
||||
{
|
||||
panic!("string")
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
quickcheck! {
|
||||
fn bytes_roundtrip(x: Vec<u8>) -> bool {
|
||||
let rendered = render_ssh_private_key_data(&x);
|
||||
let returned = parse_ssh_private_key_data(&rendered).unwrap();
|
||||
returned == x
|
||||
}
|
||||
|
||||
fn blocks_formatted(x: Vec<u8>) -> bool {
|
||||
let rendered = render_ssh_private_key_data(&x);
|
||||
let mut is_ok = true;
|
||||
|
||||
for line in rendered.lines() {
|
||||
is_ok &= line.len() <= 70;
|
||||
}
|
||||
|
||||
is_ok
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn pregenerated_reencode() {
|
||||
let test_files = ["dsa1024-1", "dsa1024-2", "dsa1024-3",
|
||||
"ecdsa256-1", "ecdsa256-2", "ecdsa256-3",
|
||||
"ecdsa384-1", "ecdsa384-2", "ecdsa384-3",
|
||||
"ecdsa521-1", "ecdsa521-2", "ecdsa521-3",
|
||||
"ed25519-1", "ed25519-2", "ed25519-3",
|
||||
"rsa1024-1", "rsa1024-2", "rsa1024-3",
|
||||
"rsa2048-1", "rsa2048-2", "rsa2048-3",
|
||||
"rsa3072-1", "rsa3072-2", "rsa3072-3",
|
||||
"rsa4096-1", "rsa4096-2", "rsa4096-3",
|
||||
"rsa8192-1", "rsa8192-2", "rsa8192-3" ];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
let mut fd = File::open(path).unwrap();
|
||||
let mut contents = String::new();
|
||||
fd.read_to_string(&mut contents).unwrap();
|
||||
let parsed = parse_ssh_private_key_data(&contents).unwrap();
|
||||
let rendered = render_ssh_private_key_data(&parsed);
|
||||
// starts_with() avoids newline unpleasantness
|
||||
assert!(contents.starts_with(&rendered));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn header_roundtrips() {
|
||||
let mut vec = Vec::new();
|
||||
assert!(render_openssh_header(&mut vec).is_ok());
|
||||
assert!(parse_openssh_header(&mut vec.iter()).is_ok());
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
mod errors;
|
||||
mod frame;
|
||||
|
||||
pub use self::errors::SSHKeyParseError;
|
||||
|
||||
use cryptonum::unsigned::U192;
|
||||
use dsa::{DSAPrivKey,L1024N160};
|
||||
use self::frame::*;
|
||||
use simple_asn1::from_der;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Cursor,Read,Write};
|
||||
use std::path::Path;
|
||||
|
||||
pub trait SSHKey: Sized {
|
||||
fn decode_ssh_private_key(x: &str) -> Result<Self,SSHKeyParseError>;
|
||||
fn read_ssh_private_key_file<P: AsRef<Path>>(path: P) -> Result<Self,SSHKeyParseError> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
Self::decode_ssh_private_key(&contents)
|
||||
}
|
||||
|
||||
fn encode_ssh_private_key(&self) -> String;
|
||||
fn write_ssh_private_key_file<P: AsRef<Path>>(&self, path: P) -> Result<(),io::Error> {
|
||||
let mut file = File::create(path)?;
|
||||
let contents = self.encode_ssh_private_key();
|
||||
let bytes = contents.into_bytes();
|
||||
file.write_all(&bytes)?;
|
||||
file.sync_all()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl SSHKey for DSAPrivKey<L1024N160,U192> {
|
||||
fn decode_ssh_private_key(x: &str) -> Result<Self,SSHKeyParseError>
|
||||
{
|
||||
let bytes = parse_ssh_private_key_data(x)?;
|
||||
let mut byte_cursor = Cursor::new(bytes);
|
||||
|
||||
parse_openssh_header(&mut byte_cursor)?;
|
||||
let ciphername = parse_openssh_string(&mut byte_cursor)?;
|
||||
//
|
||||
println!("bytes: {:?}", bytes);
|
||||
panic!("decode")
|
||||
}
|
||||
|
||||
fn encode_ssh_private_key(&self) -> String {
|
||||
panic!("encode")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn read_dsa_examples() {
|
||||
let test_files = ["dsa1024-1", "dsa1024-2", "dsa1024-3"];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
let privkey = DSAPrivKey::<L1024N160,U192>::read_ssh_private_key_file(path);
|
||||
assert!(privkey.is_ok());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user