Add RSA 1024 SSH key support, as a basic attempt.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
mod dsa;
|
mod dsa;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
mod rsa;
|
||||||
|
|
||||||
pub use self::errors::{SSHKeyParseError,SSHKeyRenderError};
|
pub use self::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||||
|
|
||||||
@@ -141,9 +142,13 @@ pub fn write_ssh_keyfile<KP,P>(path: P, x: &KP, comment: &str) -> Result<(),SSHK
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use cryptonum::unsigned::{U1024};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use dsa::{DSAKeyPair,DSAPublicKey,L1024N160};
|
use dsa::{DSAKeyPair,DSAPublicKey,L1024N160};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
use rsa::{RSAKeyPair,RSAPublicKey,SIGNING_HASH_SHA256};
|
||||||
|
#[cfg(test)]
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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<U1024> = 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<U1024> = 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::<RSAKeyPair<U1024>,String>(ppath) {
|
||||||
|
Err(e4) => assert!(false, format!("pubkey error: {:?}", e4)),
|
||||||
|
Ok(pubkeys) => {
|
||||||
|
let _ : Vec<(RSAPublicKey<U1024>,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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
79
src/ssh/rsa.rs
Normal file
79
src/ssh/rsa.rs
Normal file
@@ -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<U1024> {
|
||||||
|
fn valid_keytype(s: &str) -> bool {
|
||||||
|
(s == "ssh-rsa") || (s == "rsa")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
|
||||||
|
{
|
||||||
|
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::<U1024>::new(n, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ssh_private_info<I: Read>(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::<U1024>::new(n, d), comment))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_ssh_public_info<O: Write>(&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<O: Write>(&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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user