[CHECKPOINT] Starting to parse these things.

This commit is contained in:
2019-03-28 18:23:38 -07:00
parent 47fae77a4f
commit 69cef498f2
6 changed files with 241 additions and 1 deletions

42
src/ssh/errors.rs Normal file
View 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
View 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());
}

View File

@@ -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());
}
}