Support reading DSA public keys

This commit is contained in:
2019-04-04 20:49:02 -07:00
parent 22b4fcbd94
commit 6613f85ff3
3 changed files with 65 additions and 4 deletions

View File

@@ -6,10 +6,14 @@ use ssh::frame::*;
use ssh::SSHKey; use ssh::SSHKey;
impl SSHKey for DSAKeyPair<L1024N160> { impl SSHKey for DSAKeyPair<L1024N160> {
fn valid_keytype(s: &str) -> bool {
(s == "ssh-dss") || (s == "dss")
}
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError> fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
{ {
let pubkey_type = parse_openssh_string(inp)?; let pubkey_type = parse_openssh_string(inp)?;
if pubkey_type != "ssh-dss" { if !Self::valid_keytype(&pubkey_type) {
return Err(SSHKeyParseError::UnknownKeyType(pubkey_type)); return Err(SSHKeyParseError::UnknownKeyType(pubkey_type));
} }
let pubp = parse_openssh_number(inp)?; let pubp = parse_openssh_number(inp)?;
@@ -29,7 +33,7 @@ impl SSHKey for DSAKeyPair<L1024N160> {
return Err(SSHKeyParseError::PrivateKeyCorruption); return Err(SSHKeyParseError::PrivateKeyCorruption);
} }
let privkey_type = parse_openssh_string(inp)?; let privkey_type = parse_openssh_string(inp)?;
if privkey_type != "ssh-dss" { if !Self::valid_keytype(&privkey_type) {
return Err(SSHKeyParseError::InconsistentKeyTypes("ssh-dss".to_string(), privkey_type)); return Err(SSHKeyParseError::InconsistentKeyTypes("ssh-dss".to_string(), privkey_type));
} }
let privp = parse_openssh_number(inp)?; let privp = parse_openssh_number(inp)?;

View File

@@ -18,7 +18,9 @@ pub enum SSHKeyParseError
InconsistentKeyTypes(String,String), InconsistentKeyTypes(String,String),
InconsistentPublicKeyValue, InconsistentPublicKeyValue,
InvalidPrivateKeyValue, InvalidPrivateKeyValue,
InvalidPadding InvalidPadding,
InvalidPublicKeyType,
BrokenPublicKeyLine
} }
impl From<DecodeError> for SSHKeyParseError { impl From<DecodeError> for SSHKeyParseError {

View File

@@ -4,6 +4,7 @@ mod frame;
pub use self::errors::{SSHKeyParseError,SSHKeyRenderError}; pub use self::errors::{SSHKeyParseError,SSHKeyRenderError};
use base64::{decode,encode};
use self::frame::*; use self::frame::*;
use std::fs::File; use std::fs::File;
use std::io::{Cursor,Read,Write}; use std::io::{Cursor,Read,Write};
@@ -11,6 +12,8 @@ use std::path::Path;
use super::KeyPair; use super::KeyPair;
pub trait SSHKey: Sized + KeyPair { pub trait SSHKey: Sized + KeyPair {
fn valid_keytype(s: &str) -> bool;
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>; fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>;
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>; fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>;
@@ -55,6 +58,27 @@ pub fn decode_ssh<KP: SSHKey>(x: &str) -> Result<(KP, String),SSHKeyParseError>
Ok((KP::new(public, private), comment)) Ok((KP::new(public, private), comment))
} }
pub fn decode_ssh_pubkey<KP: SSHKey>(s: &str) -> Result<(KP::Public, String),SSHKeyParseError>
{
let mut splitter = s.split_whitespace();
match (splitter.next(), splitter.next(), splitter.next(), splitter.next()) {
(Some(keytype), Some(keymaterial), Some(comment), None) => {
if !KP::valid_keytype(keytype) {
return Err(SSHKeyParseError::InvalidPublicKeyType);
}
let bytes = decode(keymaterial)?;
let mut byte_cursor = Cursor::new(bytes);
let key = KP::parse_ssh_public_info(&mut byte_cursor)?;
Ok((key, comment.to_string()))
}
_ =>
Err(SSHKeyParseError::BrokenPublicKeyLine)
}
}
pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError> pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError>
where where
KP: SSHKey, KP: SSHKey,
@@ -66,6 +90,23 @@ pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError>
decode_ssh(&contents) decode_ssh(&contents)
} }
pub fn load_ssh_pubkeys<KP,P>(path: P) -> Result<Vec<(KP::Public, String)>,SSHKeyParseError>
where
KP: SSHKey,
P: AsRef<Path>
{
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let mut result = Vec::new();
for line in contents.lines() {
result.push( decode_ssh_pubkey::<KP>(line)? );
}
Ok(result)
}
pub fn encode_ssh<KP: SSHKey>(x: &KP, comment: &str) -> Result<String,SSHKeyRenderError> pub fn encode_ssh<KP: SSHKey>(x: &KP, comment: &str) -> Result<String,SSHKeyRenderError>
{ {
let mut pubkeybin = Vec::with_capacity(8192); let mut pubkeybin = Vec::with_capacity(8192);
@@ -101,7 +142,7 @@ pub fn write_ssh_keyfile<KP,P>(path: P, x: &KP, comment: &str) -> Result<(),SSHK
#[cfg(test)] #[cfg(test)]
use dsa::{DSAKeyPair,DSAPublicKey,DSAPrivateKey,L1024N160}; use dsa::{DSAKeyPair,DSAPublicKey,DSAPrivateKey,DSAPubKey,L1024N160};
#[cfg(test)] #[cfg(test)]
use sha2::Sha256; use sha2::Sha256;
@@ -138,6 +179,20 @@ fn read_dsa_examples() {
assert_eq!(keypair.public.y,keypair2.public.y,"failed to reparse key pair (y)"); assert_eq!(keypair.public.y,keypair2.public.y,"failed to reparse key pair (y)");
assert_eq!(keypair.private.x,keypair2.private.x,"failed to reparse key pair (x)"); assert_eq!(keypair.private.x,keypair2.private.x,"failed to reparse key pair (x)");
assert_eq!(comment,comment2,"failed to reparse comment"); assert_eq!(comment,comment2,"failed to reparse comment");
let ppath = format!("testdata/ssh/{}.pub",file);
match load_ssh_pubkeys::<DSAKeyPair<L1024N160>,String>(ppath) {
Err(e4) => assert!(false, format!("pubkey error: {:?}", e4)),
Ok(pubkeys) => {
let _ : Vec<(DSAPubKey<L1024N160>,String)> = pubkeys;
for (pubkey, comment3) in pubkeys {
assert_eq!(pubkey.params.p, keypair.public.params.p, "public key check (p)");
assert_eq!(pubkey.params.q, keypair.public.params.q, "public key check (q)");
assert_eq!(pubkey.params.g, keypair.public.params.g, "public key check (g)");
assert_eq!(pubkey.y, keypair.public.y, "public key check (y)");
assert_eq!(comment, comment3, "public key check comment")
}
}
}
} }
} }
} }