diff --git a/src/ssh/mod.rs b/src/ssh/mod.rs index aa77e74..131b0e7 100644 --- a/src/ssh/mod.rs +++ b/src/ssh/mod.rs @@ -1,6 +1,7 @@ mod dsa; mod errors; mod frame; +mod rsa; pub use self::errors::{SSHKeyParseError,SSHKeyRenderError}; @@ -141,9 +142,13 @@ pub fn write_ssh_keyfile(path: P, x: &KP, comment: &str) -> Result<(),SSHK +#[cfg(test)] +use cryptonum::unsigned::{U1024}; #[cfg(test)] use dsa::{DSAKeyPair,DSAPublicKey,L1024N160}; #[cfg(test)] +use rsa::{RSAKeyPair,RSAPublicKey,SIGNING_HASH_SHA256}; +#[cfg(test)] use sha2::Sha256; #[cfg(test)] @@ -200,4 +205,54 @@ fn read_dsa_examples() { } } } +} + +#[cfg(test)] +#[test] +fn read_rsa_examples() { + let test_files = ["rsa1024-1", "rsa1024-2", "rsa1024-3"]; + + for file in test_files.iter() { + let path = format!("testdata/ssh/{}",file); + let mkeypair = load_ssh_keyfile(path); + match mkeypair { + Err(e) => assert!(false, format!("reading error: {:?}", e)), + Ok((keypair, comment)) => { + let buffer = [0,1,2,3,4,6,2]; + let _ : RSAKeyPair = keypair; + let sig = keypair.private.sign(&SIGNING_HASH_SHA256, &buffer); + assert!(keypair.public.verify(&SIGNING_HASH_SHA256, &buffer, &sig)); + let buffer2 = [0,1,2,3,4,6,5]; + assert!(!keypair.public.verify(&SIGNING_HASH_SHA256, &buffer2, &sig)); + match encode_ssh(&keypair, &comment) { + Err(e2) => assert!(false, format!("render error: {:?}", e2)), + Ok(encodedstr) => { + match decode_ssh(&encodedstr) { + Err(e3) => assert!(false, format!("reparse error: {:?}", e3)), + Ok((keypair2,comment2)) => { + let _ : RSAKeyPair = keypair2; + assert_eq!(keypair.public.n,keypair2.public.n,"failed to reparse key pair (n)"); + assert_eq!(keypair.public.e,keypair2.public.e,"failed to reparse key pair (e)"); + assert_eq!(keypair.private.nu,keypair2.private.nu,"failed to reparse key pair (n)"); + assert_eq!(keypair.private.d,keypair2.private.d,"failed to reparse key pair (d)"); + assert_eq!(comment,comment2,"failed to reparse comment"); + let ppath = format!("testdata/ssh/{}.pub",file); + match load_ssh_pubkeys::,String>(ppath) { + Err(e4) => assert!(false, format!("pubkey error: {:?}", e4)), + Ok(pubkeys) => { + let _ : Vec<(RSAPublicKey,String)> = pubkeys; + for (pubkey, comment3) in pubkeys { + assert_eq!(pubkey.n, keypair.public.n, "public key check (n)"); + assert_eq!(pubkey.e, keypair.public.e, "public key check (e)"); + assert_eq!(comment, comment3, "public key check comment") + } + } + } + } + } + } + } + } + } + } } \ No newline at end of file diff --git a/src/ssh/rsa.rs b/src/ssh/rsa.rs new file mode 100644 index 0000000..4e3f9cd --- /dev/null +++ b/src/ssh/rsa.rs @@ -0,0 +1,79 @@ +use cryptonum::unsigned::*; +use rsa::{RSAKeyPair,RSAPublicKey,RSAPrivateKey}; +use std::io::{Read,Write}; +use ssh::errors::{SSHKeyParseError,SSHKeyRenderError}; +use ssh::frame::*; +use ssh::SSHKey; + +impl SSHKey for RSAKeyPair { + fn valid_keytype(s: &str) -> bool { + (s == "ssh-rsa") || (s == "rsa") + } + + fn parse_ssh_public_info(inp: &mut I) -> Result + { + let pubkey_type = parse_openssh_string(inp)?; + if !Self::valid_keytype(&pubkey_type) { + return Err(SSHKeyParseError::UnknownKeyType(pubkey_type)); + } + let e = parse_openssh_number(inp)?; + let n = parse_openssh_number(inp)?; + Ok(RSAPublicKey::::new(n, e)) + } + + fn parse_ssh_private_info(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError> + { + let check1 = parse_openssh_u32(inp)?; + let check2 = parse_openssh_u32(inp)?; + if check1 != check2 { + return Err(SSHKeyParseError::PrivateKeyCorruption); + } + let privkey_type = parse_openssh_string(inp)?; + if !Self::valid_keytype(&privkey_type) { + return Err(SSHKeyParseError::InconsistentKeyTypes("ssh-rsa".to_string(), privkey_type)); + } + let n = parse_openssh_number(inp)?; + let _e: U1024 = parse_openssh_number(inp)?; + let d = parse_openssh_number(inp)?; + let _iqmp: U1024 = parse_openssh_number(inp)?; + let _p: U1024 = parse_openssh_number(inp)?; + let _q: U1024 = parse_openssh_number(inp)?; + let comment = parse_openssh_string(inp)?; + for (idx,byte) in inp.bytes().enumerate() { + if ((idx+1) as u8) != byte? { + return Err(SSHKeyParseError::InvalidPadding); + } + } + + Ok((RSAPrivateKey::::new(n, d), comment)) + } + + fn render_ssh_public_info(&self, out: &mut O) -> Result<(),SSHKeyRenderError> + { + render_openssh_string(out, "ssh-rsa")?; + render_openssh_number(out, &self.public.e)?; + render_openssh_number(out, &self.public.n)?; + Ok(()) + } + + fn render_ssh_private_info(&self, out: &mut O, comment: &str) -> Result<(),SSHKeyRenderError> + { + render_openssh_u32(out, 0xDEADBEEF)?; // FIXME: Any reason for this to be random? + render_openssh_u32(out, 0xDEADBEEF)?; // ditto + render_openssh_string(out, "ssh-rsa")?; + render_openssh_number(out, &self.public.n)?; + render_openssh_number(out, &self.public.e)?; + render_openssh_number(out, &self.private.d)?; + render_openssh_number(out, &self.private.d)?; + render_openssh_number(out, &self.private.d)?; + render_openssh_number(out, &self.private.d)?; + render_openssh_string(out, comment)?; + // add some padding (not quite sure why) + let mut i = comment.len(); + while (i % 16) != 0 { + out.write(&[(i - comment.len() + 1) as u8])?; + i += 1; + } + Ok(()) + } +} \ No newline at end of file