use crate::Error;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use cryptovec::CryptoVec;
#[doc(hidden)]
pub trait Bytes {
fn bytes(&self) -> &[u8];
}
impl<A: AsRef<str>> Bytes for A {
fn bytes(&self) -> &[u8] {
self.as_ref().as_bytes()
}
}
pub trait Encoding {
fn extend_ssh_string(&mut self, s: &[u8]);
fn extend_ssh_string_blank(&mut self, s: usize) -> &mut [u8];
fn extend_ssh_mpint(&mut self, s: &[u8]);
fn extend_list<A: Bytes, I: Iterator<Item = A>>(&mut self, list: I);
fn write_empty_list(&mut self);
}
pub fn mpint_len(s: &[u8]) -> usize {
let mut i = 0;
while i < s.len() && s[i] == 0 {
i += 1
}
(if s[i] & 0x80 != 0 { 5 } else { 4 }) + s.len() - i
}
impl Encoding for Vec<u8> {
fn extend_ssh_string(&mut self, s: &[u8]) {
self.write_u32::<BigEndian>(s.len() as u32).unwrap();
self.extend(s);
}
fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
self.write_u32::<BigEndian>(len as u32).unwrap();
let current = self.len();
self.resize(current + len, 0u8);
&mut self[current..]
}
fn extend_ssh_mpint(&mut self, s: &[u8]) {
let mut i = 0;
while i < s.len() && s[i] == 0 {
i += 1
}
if s[i] & 0x80 != 0 {
self.write_u32::<BigEndian>((s.len() - i + 1) as u32)
.unwrap();
self.push(0)
} else {
self.write_u32::<BigEndian>((s.len() - i) as u32).unwrap();
}
self.extend(&s[i..]);
}
fn extend_list<A: Bytes, I: Iterator<Item = A>>(&mut self, list: I) {
let len0 = self.len();
self.extend(&[0, 0, 0, 0]);
let mut first = true;
for i in list {
if !first {
self.push(b',')
} else {
first = false;
}
self.extend(i.bytes())
}
let len = (self.len() - len0 - 4) as u32;
BigEndian::write_u32(&mut self[len0..], len);
}
fn write_empty_list(&mut self) {
self.extend(&[0, 0, 0, 0]);
}
}
impl Encoding for CryptoVec {
fn extend_ssh_string(&mut self, s: &[u8]) {
self.push_u32_be(s.len() as u32);
self.extend(s);
}
fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
self.push_u32_be(len as u32);
let current = self.len();
self.resize(current + len);
&mut self[current..]
}
fn extend_ssh_mpint(&mut self, s: &[u8]) {
let mut i = 0;
while i < s.len() && s[i] == 0 {
i += 1
}
if s[i] & 0x80 != 0 {
self.push_u32_be((s.len() - i + 1) as u32);
self.push(0)
} else {
self.push_u32_be((s.len() - i) as u32);
}
self.extend(&s[i..]);
}
fn extend_list<A: Bytes, I: Iterator<Item = A>>(&mut self, list: I) {
let len0 = self.len();
self.extend(&[0, 0, 0, 0]);
let mut first = true;
for i in list {
if !first {
self.push(b',')
} else {
first = false;
}
self.extend(i.bytes())
}
let len = (self.len() - len0 - 4) as u32;
BigEndian::write_u32(&mut self[len0..], len);
}
fn write_empty_list(&mut self) {
self.extend(&[0, 0, 0, 0]);
}
}
pub trait Reader {
fn reader<'a>(&'a self, starting_at: usize) -> Position<'a>;
}
impl Reader for CryptoVec {
fn reader<'a>(&'a self, starting_at: usize) -> Position<'a> {
Position {
s: &self,
position: starting_at,
}
}
}
impl Reader for [u8] {
fn reader<'a>(&'a self, starting_at: usize) -> Position<'a> {
Position {
s: self,
position: starting_at,
}
}
}
#[derive(Debug)]
pub struct Position<'a> {
s: &'a [u8],
#[doc(hidden)]
pub position: usize,
}
impl<'a> Position<'a> {
pub fn read_string(&mut self) -> Result<&'a [u8], Error> {
let len = self.read_u32()? as usize;
if self.position + len <= self.s.len() {
let result = &self.s[self.position..(self.position + len)];
self.position += len;
Ok(result)
} else {
Err(Error::IndexOutOfBounds)
}
}
pub fn read_u32(&mut self) -> Result<u32, Error> {
if self.position + 4 <= self.s.len() {
let u = BigEndian::read_u32(&self.s[self.position..]);
self.position += 4;
Ok(u)
} else {
Err(Error::IndexOutOfBounds)
}
}
pub fn read_byte(&mut self) -> Result<u8, Error> {
if self.position + 1 <= self.s.len() {
let u = self.s[self.position];
self.position += 1;
Ok(u)
} else {
Err(Error::IndexOutOfBounds)
}
}
pub fn read_mpint(&mut self) -> Result<&'a [u8], Error> {
let len = self.read_u32()? as usize;
if self.position + len <= self.s.len() {
let result = &self.s[self.position..(self.position + len)];
self.position += len;
Ok(result)
} else {
Err(Error::IndexOutOfBounds)
}
}
}