Improve ssh crate API

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-10-03 11:13:53 +02:00
parent 79c1372d33
commit c8bff35229
No known key found for this signature in database
3 changed files with 32 additions and 19 deletions

View File

@ -218,10 +218,12 @@ impl<S: ClientStream> AgentClient<S> {
if self.buf[0] == msg::IDENTITIES_ANSWER { if self.buf[0] == msg::IDENTITIES_ANSWER {
let mut r = self.buf.reader(1); let mut r = self.buf.reader(1);
let n = r.read_u32()?; let n = r.read_u32()?;
for _ in 0..n { for _ in 0..n {
let key = r.read_string()?; let key = r.read_string()?;
let _ = r.read_string()?; let _ = r.read_string()?;
let mut r = key.reader(0); let mut r = key.reader(0);
if let Some(pk) = K::read(&mut r).map_err(|err| Error::Public(Box::new(err)))? { if let Some(pk) = K::read(&mut r).map_err(|err| Error::Public(Box::new(err)))? {
keys.push(pk); keys.push(pk);
} }
@ -261,7 +263,7 @@ impl<S: ClientStream> AgentClient<S> {
// uint32 flags // uint32 flags
let mut pk = Vec::new().into(); let mut pk = Vec::new().into();
let n = public.write_blob(&mut pk); let n = public.write(&mut pk);
let total = 1 + n + 4 + data.len() + 4; let total = 1 + n + 4 + data.len() + 4;
debug_assert_eq!(n, pk.len()); debug_assert_eq!(n, pk.len());
@ -295,7 +297,7 @@ impl<S: ClientStream> AgentClient<S> {
K: Public, K: Public,
{ {
let mut pk = Vec::new().into(); let mut pk = Vec::new().into();
let n = public.write_blob(&mut pk); let n = public.write(&mut pk);
let total = 1 + n; let total = 1 + n;
debug_assert_eq!(n, pk.len()); debug_assert_eq!(n, pk.len());

View File

@ -1,20 +1,26 @@
use std::error::Error;
use crate::encoding::{Buffer, Cursor}; use crate::encoding::{Buffer, Cursor};
/// A public SSH key.
pub trait Public: Sized { pub trait Public: Sized {
type Error; type Error: Error + Send + Sync + 'static;
fn write_blob(&self, buf: &mut Buffer) -> usize; /// Write the public key to the given buffer, in SSH "blob" format.
fn write(&self, buf: &mut Buffer) -> usize;
/// Read the public key from the given reader.
fn read(reader: &mut Cursor) -> Result<Option<Self>, Self::Error>; fn read(reader: &mut Cursor) -> Result<Option<Self>, Self::Error>;
} }
/// A private SSH key.
pub trait Private: Sized { pub trait Private: Sized {
type Error; type Error: Error + Send + Sync + 'static;
fn read(reader: &mut Cursor) -> Result<Option<(Vec<u8>, Self)>, Self::Error>; /// Read a private key from the given reader.
fn read(reader: &mut Cursor) -> Result<Option<Self>, Self::Error>;
/// Write the key bytes to the supplied buffer.
fn write(&self, buf: &mut Buffer) -> Result<(), Self::Error>; fn write(&self, buf: &mut Buffer) -> Result<(), Self::Error>;
fn write_signature<T: AsRef<[u8]>>( /// Sign the data and write the signature to the given buffer.
&self, fn write_signature<T: AsRef<[u8]>>(&self, data: T, buf: &mut Buffer)
buf: &mut Buffer, -> Result<(), Self::Error>;
to_sign: T,
) -> Result<(), Self::Error>;
} }

View File

@ -86,7 +86,7 @@ pub enum PublicKeyError {
impl radicle_ssh::key::Public for PublicKey { impl radicle_ssh::key::Public for PublicKey {
type Error = PublicKeyError; type Error = PublicKeyError;
fn write_blob(&self, buf: &mut Zeroizing<Vec<u8>>) -> usize { fn write(&self, buf: &mut Zeroizing<Vec<u8>>) -> usize {
let mut n = 0; let mut n = 0;
let typ = b"ssh-ed25519"; let typ = b"ssh-ed25519";
let size = typ.len() + self.len() + mem::size_of::<u32>() * 2; let size = typ.len() + self.len() + mem::size_of::<u32>() * 2;
@ -124,19 +124,21 @@ impl From<crypto::SecretKey> for SecretKey {
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum SigningKeyError { pub enum SecretKeyError {
#[error(transparent)] #[error(transparent)]
Encoding(#[from] encoding::Error), Encoding(#[from] encoding::Error),
#[error(transparent)] #[error(transparent)]
Crypto(#[from] crypto::Error), Crypto(#[from] crypto::Error),
#[error(transparent)] #[error(transparent)]
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("public key does not match secret key")]
Mismatch,
} }
impl radicle_ssh::key::Private for SecretKey { impl radicle_ssh::key::Private for SecretKey {
type Error = SigningKeyError; type Error = SecretKeyError;
fn read(r: &mut encoding::Cursor) -> Result<Option<(Vec<u8>, Self)>, Self::Error> { fn read(r: &mut encoding::Cursor) -> Result<Option<Self>, Self::Error> {
match r.read_string()? { match r.read_string()? {
b"ssh-ed25519" => { b"ssh-ed25519" => {
let public = r.read_string()?; let public = r.read_string()?;
@ -144,7 +146,10 @@ impl radicle_ssh::key::Private for SecretKey {
let _comment = r.read_string()?; let _comment = r.read_string()?;
let key = crypto::SecretKey::from_slice(pair).unwrap(); let key = crypto::SecretKey::from_slice(pair).unwrap();
Ok(Some((public.to_vec(), SecretKey(key)))) if public != key.public_key().as_ref() {
return Err(SecretKeyError::Mismatch);
}
Ok(Some(SecretKey(key)))
} }
_ => Ok(None), _ => Ok(None),
} }
@ -164,11 +169,11 @@ impl radicle_ssh::key::Private for SecretKey {
fn write_signature<Bytes: AsRef<[u8]>>( fn write_signature<Bytes: AsRef<[u8]>>(
&self, &self,
data: Bytes,
buf: &mut Zeroizing<Vec<u8>>, buf: &mut Zeroizing<Vec<u8>>,
to_sign: Bytes,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let name = "ssh-ed25519"; let name = "ssh-ed25519";
let signature: [u8; 64] = *self.0.sign(to_sign.as_ref(), None); let signature: [u8; 64] = *self.0.sign(data.as_ref(), None);
buf.deref_mut() buf.deref_mut()
.write_u32::<BigEndian>((name.len() + signature.len() + 8) as u32)?; .write_u32::<BigEndian>((name.len() + signature.len() + 8) as u32)?;
@ -221,7 +226,7 @@ mod test {
SecretKey(sk).write(&mut buf).unwrap(); SecretKey(sk).write(&mut buf).unwrap();
let mut cursor = buf.reader(0); let mut cursor = buf.reader(0);
let (_, output) = SecretKey::read(&mut cursor).unwrap().unwrap(); let output = SecretKey::read(&mut cursor).unwrap().unwrap();
assert_eq!(sk, output.0); assert_eq!(sk, output.0);
} }