use ssh_encoding::{self, CheckedSum, Decode, Encode, Reader, Writer};
use ssh_key::Error as KeyError;
use crate::proto::{AddIdentity, Error, Extension, Result, SmartcardKey, Unparsed};
#[derive(Clone, PartialEq, Debug)]
pub enum KeyConstraint {
Lifetime(u32),
Confirm,
Extension(Extension),
}
impl Decode for KeyConstraint {
type Error = Error;
fn decode(reader: &mut impl Reader) -> Result<Self> {
let constraint_type = u8::decode(reader)?;
Ok(match constraint_type {
1 => KeyConstraint::Lifetime(u32::decode(reader)?),
2 => KeyConstraint::Confirm,
255 => {
let name = String::decode(reader)?;
let details: Vec<u8> = Vec::decode(reader)?;
KeyConstraint::Extension(Extension {
name,
details: Unparsed::from(details),
})
}
_ => return Err(KeyError::AlgorithmUnknown)?, })
}
}
impl Encode for KeyConstraint {
fn encoded_len(&self) -> ssh_encoding::Result<usize> {
let base = u8::MAX.encoded_len()?;
match self {
Self::Lifetime(lifetime) => base
.checked_add(lifetime.encoded_len()?)
.ok_or(ssh_encoding::Error::Length),
Self::Confirm => Ok(base),
Self::Extension(extension) => [
base,
extension.name.encoded_len()?,
extension.details.encoded_len_prefixed()?,
]
.checked_sum(),
}
}
fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
match self {
Self::Lifetime(lifetime) => {
1u8.encode(writer)?;
lifetime.encode(writer)
}
Self::Confirm => 2u8.encode(writer),
Self::Extension(extension) => {
255u8.encode(writer)?;
extension.name.encode(writer)?;
extension.details.encode_prefixed(writer)
}
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AddIdentityConstrained {
pub identity: AddIdentity,
pub constraints: Vec<KeyConstraint>,
}
impl Decode for AddIdentityConstrained {
type Error = Error;
fn decode(reader: &mut impl Reader) -> Result<Self> {
let identity = AddIdentity::decode(reader)?;
let mut constraints = vec![];
while !reader.is_finished() {
constraints.push(KeyConstraint::decode(reader)?);
}
Ok(Self {
identity,
constraints,
})
}
}
impl Encode for AddIdentityConstrained {
fn encoded_len(&self) -> ssh_encoding::Result<usize> {
self.constraints
.iter()
.try_fold(self.identity.encoded_len()?, |acc, e| {
let constraint_len = e.encoded_len()?;
usize::checked_add(acc, constraint_len).ok_or(ssh_encoding::Error::Length)
})
}
fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
self.identity.encode(writer)?;
for constraint in &self.constraints {
constraint.encode(writer)?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AddSmartcardKeyConstrained {
pub key: SmartcardKey,
pub constraints: Vec<KeyConstraint>,
}
impl Decode for AddSmartcardKeyConstrained {
type Error = Error;
fn decode(reader: &mut impl Reader) -> Result<Self> {
let key = SmartcardKey::decode(reader)?;
let mut constraints = vec![];
while !reader.is_finished() {
constraints.push(KeyConstraint::decode(reader)?);
}
Ok(Self { key, constraints })
}
}
impl Encode for AddSmartcardKeyConstrained {
fn encoded_len(&self) -> ssh_encoding::Result<usize> {
self.constraints
.iter()
.try_fold(self.key.encoded_len()?, |acc, e| {
let constraint_len = e.encoded_len()?;
usize::checked_add(acc, constraint_len).ok_or(ssh_encoding::Error::Length)
})
}
fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
self.key.encode(writer)?;
for constraint in &self.constraints {
constraint.encode(writer)?;
}
Ok(())
}
}