feat: sshrw

This commit is contained in:
2024-08-28 00:05:37 +08:00
parent 0b03e296bc
commit 9f4dc1ef09
3 changed files with 186 additions and 0 deletions

View File

@@ -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
View File

@@ -0,0 +1 @@
mod sshrw;

183
src/sshrw.rs Normal file
View 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";
}