Skip to content

Commit

Permalink
Merge pull request #26 from wiktor-k/wiktor/migrate-to-rustcrypto
Browse files Browse the repository at this point in the history
Wiktor/Migrate to RustCrypto
  • Loading branch information
wiktor-k authored Apr 5, 2024
2 parents bf7523a + a1c0beb commit d0b92fd
Show file tree
Hide file tree
Showing 34 changed files with 1,432 additions and 1,477 deletions.
422 changes: 350 additions & 72 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ exclude = [".github"]

[dependencies]
byteorder = "1.4.3"
serde = {version = "1", features = ["derive"]}

async-trait = { version = "0.1.77", optional = true }
bytes = { version = "1.5.0", optional = true }
futures = { version = "0.3.30", optional = true }
log = { version = "0.4.6", optional = true }
tokio = { version = "1", optional = true, features = ["rt", "net"] }
tokio-util = { version = "0.7.1", optional = true, features = ["codec"] }
service-binding = { version = "^2" }
ssh-encoding = { version = "0.2.0" }
ssh-key = { version = "0.6.4", features = ["rsa", "alloc"] }
#uuid = { version = "1.8.0", features = ["v4"] }

[features]
default = ["agent"]
agent = ["futures", "log", "tokio", "tokio-util", "bytes", "async-trait"]
agent = ["futures", "log", "tokio", "tokio-util", "async-trait"]

[[example]]
name = "key_storage"
Expand All @@ -41,3 +41,9 @@ rand = "0.8.5"
rsa = { version = "0.9.6", features = ["sha2", "sha1"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
sha1 = { version = "0.10.5", default-features = false, features = ["oid"] }
testresult = "0.4.0"
hex-literal = "0.4.1"
ssh-key = { version = "0.6.4", features = ["p256"] }
p256 = { version = "0.13.2" }
const-str = "0.5.7"
rstest = "0.18.2"
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ssh_agent_lib::agent::NamedPipeListener;
use ssh_agent_lib::agent::{Session, Agent};
use ssh_agent_lib::proto::message::Message;
use ssh_key::{Algorithm, Signature};
#[derive(Default)]
struct MyAgent;
Expand All @@ -31,7 +32,10 @@ impl Session for MyAgent {
Message::SignRequest(request) => {
// get the signature by signing `request.data`
let signature = vec![];
Ok(Message::SignResponse(signature))
Ok(Message::SignResponse(Signature::new(
Algorithm::new("algorithm")?,
signature,
)?))
},
_ => Ok(Message::Failure),
}
Expand Down
95 changes: 71 additions & 24 deletions examples/key_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ use async_trait::async_trait;
use log::info;
#[cfg(windows)]
use ssh_agent_lib::agent::NamedPipeListener;
use ssh_agent_lib::proto::extension::SessionBind;
#[cfg(not(windows))]
use tokio::net::UnixListener;

use ssh_agent_lib::agent::{Agent, Session};
use ssh_agent_lib::proto::message::{self, Message, SignRequest};
use ssh_agent_lib::proto::private_key::PrivateKey;
use ssh_agent_lib::proto::public_key::PublicKey;
use ssh_agent_lib::proto::signature::{self, Signature};
use ssh_agent_lib::proto::{from_bytes, to_bytes};
use ssh_agent_lib::proto::{signature, AddIdentityConstrained, KeyConstraint};
use ssh_key::{
private::{KeypairData, PrivateKey},
public::PublicKey,
Algorithm, Signature,
};

use std::error::Error;
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -68,21 +71,18 @@ impl KeyStorage {
}

fn sign(&self, sign_request: &SignRequest) -> Result<Signature, Box<dyn Error>> {
let pubkey: PublicKey = from_bytes(&sign_request.pubkey_blob)?;
let pubkey: PublicKey = sign_request.pubkey.clone().try_into()?;

if let Some(identity) = self.identity_from_pubkey(&pubkey) {
match identity.privkey {
PrivateKey::Rsa(ref key) => {
match identity.privkey.key_data() {
KeypairData::Rsa(ref key) => {
let algorithm;

let private_key = rsa::RsaPrivateKey::from_components(
BigUint::from_bytes_be(&key.n),
BigUint::from_bytes_be(&key.e),
BigUint::from_bytes_be(&key.d),
vec![
BigUint::from_bytes_be(&key.p),
BigUint::from_bytes_be(&key.q),
],
BigUint::from_bytes_be(&key.public.n.as_bytes()),
BigUint::from_bytes_be(&key.public.e.as_bytes()),
BigUint::from_bytes_be(&key.private.d.as_bytes()),
vec![],
)?;
let mut rng = rand::thread_rng();
let data = &sign_request.data;
Expand All @@ -97,11 +97,10 @@ impl KeyStorage {
algorithm = "ssh-rsa";
SigningKey::<Sha1>::new(private_key).sign_with_rng(&mut rng, data)
};

Ok(Signature {
algorithm: algorithm.to_string(),
blob: signature.to_bytes().to_vec(),
})
Ok(Signature::new(
Algorithm::new(algorithm)?,
signature.to_bytes().to_vec(),
)?)
}
_ => Err(From::from("Signature for key type not implemented")),
}
Expand All @@ -117,29 +116,76 @@ impl KeyStorage {
let mut identities = vec![];
for identity in self.identities.lock().unwrap().iter() {
identities.push(message::Identity {
pubkey_blob: to_bytes(&identity.pubkey)?,
pubkey: identity.pubkey.key_data().clone(),
comment: identity.comment.clone(),
})
}
Ok(Message::IdentitiesAnswer(identities))
}
Message::RemoveIdentity(identity) => {
let pubkey: PublicKey = from_bytes(&identity.pubkey_blob)?;
let pubkey: PublicKey = identity.pubkey.try_into()?;
self.identity_remove(&pubkey)?;
Ok(Message::Success)
}
Message::AddIdentity(identity) => {
let privkey = PrivateKey::try_from(identity.privkey).unwrap();
self.identity_add(Identity {
pubkey: PublicKey::from(&privkey),
privkey,
comment: identity.comment,
});
Ok(Message::Success)
}
Message::AddIdConstrained(AddIdentityConstrained {
identity,
constraints,
}) => {
eprintln!("Would use these constraints: {constraints:#?}");
for constraint in constraints {
if let KeyConstraint::Extension(name, mut details) = constraint {
if name == "[email protected]" {
if let Ok(destination_constraint) = details.parse::<SessionBind>() {
eprintln!("Destination constraint: {destination_constraint:?}");
}
}
}
}
let privkey = PrivateKey::try_from(identity.privkey).unwrap();
self.identity_add(Identity {
pubkey: PublicKey::from(&identity.privkey),
privkey: identity.privkey,
pubkey: PublicKey::from(&privkey),
privkey,
comment: identity.comment,
});
Ok(Message::Success)
}
Message::SignRequest(request) => {
let signature = to_bytes(&self.sign(&request)?)?;
let signature = self.sign(&request)?;
Ok(Message::SignResponse(signature))
}
Message::AddSmartcardKey(key) => {
println!("Adding smartcard key: {key:?}");
Ok(Message::Success)
}
Message::AddSmartcardKeyConstrained(key) => {
println!("Adding smartcard key with constraints: {key:?}");
Ok(Message::Success)
}
Message::Lock(pwd) => {
println!("Locked with password: {pwd:?}");
Ok(Message::Success)
}
Message::Unlock(pwd) => {
println!("Unlocked with password: {pwd:?}");
Ok(Message::Success)
}
Message::Extension(mut extension) => {
eprintln!("Extension: {extension:?}");
if extension.name == "[email protected]" {
let bind = extension.details.parse::<SessionBind>()?;
eprintln!("Bind: {bind:?}");
}
Ok(Message::Success)
}
_ => Err(From::from(format!("Unknown message: {:?}", request))),
};
info!("Response {:?}", response);
Expand Down Expand Up @@ -177,6 +223,7 @@ impl Agent for KeyStorageAgent {
#[tokio::main]
#[cfg(not(windows))]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let socket = "ssh-agent.sock";
let _ = std::fs::remove_file(socket); // remove the socket if exists

Expand Down
18 changes: 13 additions & 5 deletions src/agent.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use async_trait::async_trait;
use byteorder::{BigEndian, ReadBytesExt};
use bytes::{Buf, BufMut, BytesMut};
use futures::{SinkExt, TryStreamExt};
use log::{error, info};
use ssh_encoding::{Decode, Encode};
use tokio::io::{AsyncRead, AsyncWrite};
#[cfg(windows)]
use tokio::net::windows::named_pipe::{NamedPipeServer, ServerOptions};
use tokio::net::{TcpListener, TcpStream};
#[cfg(unix)]
use tokio::net::{UnixListener, UnixStream};
use tokio_util::bytes::{Buf, BufMut, BytesMut};
use tokio_util::codec::{Decoder, Encoder, Framed};

use std::fmt;
Expand All @@ -17,8 +18,7 @@ use std::marker::Unpin;
use std::mem::size_of;

use super::error::AgentError;
use super::proto::message::Message;
use super::proto::{from_bytes, to_bytes};
use super::proto::{message::Message, ProtoError};

#[derive(Debug)]
pub struct MessageCodec;
Expand All @@ -40,7 +40,7 @@ impl Decoder for MessageCodec {
return Ok(None);
}

let message: Message = from_bytes(bytes)?;
let message: Message = Message::decode(&mut bytes)?;
src.advance(size_of::<u32>() + length);
Ok(Some(message))
}
Expand All @@ -50,8 +50,14 @@ impl Encoder<Message> for MessageCodec {
type Error = AgentError;

fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> {
let bytes = to_bytes(&to_bytes(&item)?)?;
let mut bytes = Vec::new();

let len = item.encoded_len().unwrap() as u32;
len.encode(&mut bytes).map_err(ProtoError::SshEncoding)?;

item.encode(&mut bytes).map_err(ProtoError::SshEncoding)?;
dst.put(&*bytes);

Ok(())
}
}
Expand Down Expand Up @@ -122,13 +128,15 @@ pub trait Session: 'static + Sync + Send + Sized {
{
loop {
if let Some(incoming_message) = adapter.try_next().await? {
log::debug!("Request: {incoming_message:?}");
let response = match self.handle(incoming_message).await {
Ok(message) => message,
Err(e) => {
error!("Error handling message: {:?}", e);
Message::Failure
}
};
log::debug!("Response: {response:?}");

adapter.send(response).await?;
} else {
Expand Down
11 changes: 10 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::proto::error::ProtoError;
use std::io;

use crate::proto::ProtoError;

#[derive(Debug)]
pub enum AgentError {
//Ssh(ssh_key::Error),
Proto(ProtoError),
IO(io::Error),
}
Expand All @@ -13,6 +15,12 @@ impl From<ProtoError> for AgentError {
}
}

//impl From<ssh_key::Error> for AgentError {
// fn from(e: ssh_key::Error) -> AgentError {
// AgentError::Ssh(e)
// }
//}

impl From<io::Error> for AgentError {
fn from(e: io::Error) -> AgentError {
AgentError::IO(e)
Expand All @@ -22,6 +30,7 @@ impl From<io::Error> for AgentError {
impl std::fmt::Display for AgentError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
//AgentError::Ssh(e) => write!(f, "Agent: Ssh key error: {e}"),
AgentError::Proto(proto) => write!(f, "Agent: Protocol error: {}", proto),
AgentError::IO(error) => write!(f, "Agent: I/O error: {}", error),
}
Expand Down
10 changes: 10 additions & 0 deletions src/proto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod error;
pub mod extension;
pub mod message;
pub mod signature;

pub use self::error::*;
pub use self::message::*;
pub use self::signature::*;

pub type MpInt = Vec<u8>;
Loading

0 comments on commit d0b92fd

Please sign in to comment.