feat: sshrw
This commit is contained in:
@@ -6,3 +6,5 @@ license = "MIT"
|
|||||||
description = "OpenSSH Utils"
|
description = "OpenSSH Utils"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
base64 = "0.22.1"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|||||||
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
mod sshrw;
|
||||||
183
src/sshrw.rs
Normal file
183
src/sshrw.rs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
use base64::Engine;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::{Cursor, Read};
|
||||||
|
|
||||||
|
const ED25519_PK_SZ: u32 = 32;
|
||||||
|
|
||||||
|
pub type SshResult<T> = Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
|
pub struct SshReader {
|
||||||
|
buffer_len: usize,
|
||||||
|
read_len: usize,
|
||||||
|
buffer: Cursor<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SshReader {
|
||||||
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer_len: bytes.len(),
|
||||||
|
read_len: 0,
|
||||||
|
buffer: Cursor::new(bytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bytes(&mut self, len: u32) -> SshResult<Vec<u8>> {
|
||||||
|
let mut buff = vec![0_u8; len as usize];
|
||||||
|
Cursor::read_exact(&mut self.buffer, &mut buff)?;
|
||||||
|
self.read_len += len as usize;
|
||||||
|
Ok(buff)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_u32(&mut self) -> SshResult<u32> {
|
||||||
|
let mut i = [0_u8; 4];
|
||||||
|
i.copy_from_slice(&self.read_bytes(4)?);
|
||||||
|
Ok(u32::from_be_bytes(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_string(&mut self) -> SshResult<Vec<u8>> {
|
||||||
|
let len = self.read_u32()?;
|
||||||
|
if len == 0 {
|
||||||
|
Ok(vec![])
|
||||||
|
} else {
|
||||||
|
self.read_bytes(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_left(&mut self) -> SshResult<Vec<u8>> {
|
||||||
|
self.read_bytes(self.left_bytes() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_bytes(&self) -> usize {
|
||||||
|
println!("{} .. {}", self.read_len, self.buffer_len);
|
||||||
|
assert!(self.read_len <= self.buffer_len);
|
||||||
|
self.buffer_len - self.read_len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SshWriter {
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SshWriter {
|
||||||
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_bytes(&mut self, bytes: &[u8]) {
|
||||||
|
self.buffer.extend_from_slice(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_u32(&mut self, i: u32) {
|
||||||
|
self.write_bytes(&i.to_be_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_string(&mut self, bytes: &[u8]) {
|
||||||
|
self.write_u32(bytes.len() as u32);
|
||||||
|
self.write_bytes(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rsa() {
|
||||||
|
use base64::engine::general_purpose::STANDARD;
|
||||||
|
let id_rsa_pub = "AAAAB3NzaC1yc2EAAAADAQABAAABgQC82oVSC64hu5H3bpdyZGNS9w7iJ/nF8iB6AYfpl\
|
||||||
|
XdAMSZpDqHsGLfjj88nlJgb6mJCR0v1NhMNDRAcZ4VO/wLnLfZG384tgpbEuuBJsa21YYavcdPIWKnlhluNR\
|
||||||
|
0NPGqMka6yFErJ97iPEaTJZdK0R1icOY5GNk71nKGDQP068l/GhH0CRwzZE2gAsfA3klndb1N0wSFaNUGrVb\
|
||||||
|
aRfGb/KXQLJGJUP6Fn0JnAARNMojhYogJOYfF6jyqnfyn5NZxE9zL/ykCHZCKXZzfRKzNZMJ5NcDDhqkfoT7\
|
||||||
|
aitryTP31BCQXynlkmG30gFL6f955H/hBMOVU1JgXR48dzGZwlEoJxeb35RGBzBruCESFkU4FzKjFcQxYbtg\
|
||||||
|
XC/5zmN05vbpmZ3ZQO9CwR3ERDpkDNvMelxdxF529Ewi3ioHl+3fJVMjnZ18BUGkOn0bfVHIovX9+Jp6mhwo\
|
||||||
|
k/QQmZ2Nu29OYSWITbWcoeszsC+AmpFa9zhF/TG+iwmRFKlmodyCWU=";
|
||||||
|
let mut ssh_reader = SshReader::new(STANDARD.decode(id_rsa_pub).unwrap());
|
||||||
|
let algorithm = ssh_reader.read_string().unwrap();
|
||||||
|
assert_eq!(b"ssh-rsa", algorithm.as_slice());
|
||||||
|
|
||||||
|
let id_rsa = "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";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ecdsa() {
|
||||||
|
use base64::engine::general_purpose::STANDARD;
|
||||||
|
let id_ecdsa_pub = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLsD16oP\
|
||||||
|
M9hkSeHxHrJ1+FMK2xNTd+LF17A/WW3YYTBCz8tQvhtgOQDiSk/9Lnc+vFlMF7LN0dwgwl4zIg07h0M=";
|
||||||
|
let mut ssh_reader = SshReader::new(STANDARD.decode(id_ecdsa_pub).unwrap());
|
||||||
|
let algorithm = ssh_reader.read_string().unwrap();
|
||||||
|
assert_eq!(b"ecdsa-sha2-nistp256", algorithm.as_slice());
|
||||||
|
|
||||||
|
let ecc_key_blob = ssh_reader.read_left().unwrap();
|
||||||
|
let mut ecc_key_blob_reader = SshReader::new(ecc_key_blob);
|
||||||
|
let ssh_algorithm = ecc_key_blob_reader.read_string().unwrap();
|
||||||
|
let ecc_key_point = ecc_key_blob_reader.read_string().unwrap();
|
||||||
|
assert_eq!(b"nistp256", ssh_algorithm.as_slice());
|
||||||
|
assert_eq!(
|
||||||
|
"04bb03d7aa0f33d86449e1f11eb275f8530adb135377e2c5d7b03f596dd8613042cfcb50be1b603900e24a4\
|
||||||
|
ffd2e773ebc594c17b2cdd1dc20c25e33220d3b8743",
|
||||||
|
hex::encode(ecc_key_point));
|
||||||
|
|
||||||
|
assert_eq!(0, ssh_reader.left_bytes());
|
||||||
|
|
||||||
|
let id_ecdsa = "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS\
|
||||||
|
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQS7A9eqDzPYZEnh8R6ydfhTCtsTU3fi\
|
||||||
|
xdewP1lt2GEwQs/LUL4bYDkA4kpP/S53PrxZTBeyzdHcIMJeMyINO4dDAAAAuK3kG36t5B\"
|
||||||
|
t+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLsD16oPM9hkSeHx\"
|
||||||
|
HrJ1+FMK2xNTd+LF17A/WW3YYTBCz8tQvhtgOQDiSk/9Lnc+vFlMF7LN0dwgwl4zIg07h0\"
|
||||||
|
MAAAAhAIC5cYUvZZw5X4LGn2hZR+l7kdiMKsTly/luHG7VNN4WAAAAHWhhdHRlcmppYW5n\"
|
||||||
|
QEhhdHRlckppYW5nX21hY09TAQI=";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ed25519() {
|
||||||
|
use base64::engine::general_purpose::STANDARD;
|
||||||
|
let id_ed25519_pub = "AAAAC3NzaC1lZDI1NTE5AAAAIEwcL5ke2mqRDXMWM34VW432M7fjASfvAUUTu7A7QqzW";
|
||||||
|
let mut ssh_reader = SshReader::new(STANDARD.decode(id_ed25519_pub).unwrap());
|
||||||
|
let algorithm = ssh_reader.read_string().unwrap();
|
||||||
|
assert_eq!(b"ssh-ed25519", algorithm.as_slice());
|
||||||
|
|
||||||
|
let pub_key = ssh_reader.read_string().unwrap();
|
||||||
|
assert_eq!("4c1c2f991eda6a910d7316337e155b8df633b7e30127ef014513bbb03b42acd6",
|
||||||
|
hex::encode(&pub_key));
|
||||||
|
|
||||||
|
assert_eq!(0, ssh_reader.left_bytes());
|
||||||
|
|
||||||
|
let id_ed25519 = "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\
|
||||||
|
QyNTUxOQAAACBMHC+ZHtpqkQ1zFjN+FVuN9jO34wEn7wFFE7uwO0Ks1gAAAKCwNsNtsDbD\
|
||||||
|
bQAAAAtzc2gtZWQyNTUxOQAAACBMHC+ZHtpqkQ1zFjN+FVuN9jO34wEn7wFFE7uwO0Ks1g\
|
||||||
|
AAAEBG1hWO0trqejMtL8JlEUkm2w4+Wqvl4e4PkjC0Ee6fD0wcL5ke2mqRDXMWM34VW432\
|
||||||
|
M7fjASfvAUUTu7A7QqzWAAAAHWhhdHRlcmppYW5nQEhhdHRlckppYW5nX21hY09T";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user