Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ thiserror = "2.0.12"

[dev-dependencies]
rand = "0.9.1"
anyhow = "1.0.98"

[target.'cfg(windows)'.dependencies]
interprocess = "2.2.3"
56 changes: 18 additions & 38 deletions examples/verify.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,38 @@
use bytes::BytesMut;
use rand::{rng, RngCore};
use signature::Verifier;
use ssh_agent_client_rs::{Client, Identity, Result};
use ssh_key::public::KeyData;
use ssh_key::{Certificate, PublicKey, Signature};
use ssh_agent_client_rs::Client;
use ssh_key::{Certificate, PublicKey};
use std::env;
use std::fs::read_to_string;
use std::path::Path;

/// This example tests whether the ssh-agent referenced by the path
/// in the SSH_AUTH_SOCK environment variable holds a usable private
/// key that corresponds to PUBLIC_KEY much like the command
/// `ssh-add -K PUBLIC_KEY`
/// It can use either a public key or a certificate as an argument.
/// If the second argument is "certificate" it will use a certificate,
fn main() -> Result<()> {
let path_or_certificate = env::args().nth(1).expect("argument PUBLIC_KEY missing");
// Let's add a second optional argument that tells us if we want to use a certificate
// or a public key. If the argument is "certificate" it will not expect a path to a public key
// but the OpenSSH string representation of a certificate as given by ssh-add -L
// otherwise we will use a public key.
let key_type = env::args()
.nth(2)
.unwrap_or_else(|| String::from("public_key"));

let identity: &Identity = &match key_type.as_str() {
"certificate" => {
println!("Using a certificate");
Certificate::from_openssh(path_or_certificate.as_str())
.expect("failed to parse certificate from argument")
.into()
}
"public_key" => {
println!("Using a public key");
let key_bytes = read_to_string(Path::new(&path_or_certificate))?;
PublicKey::from_openssh(key_bytes.as_str())?.into()
}
_ => panic!("Unknown key type: {}", key_type),
};
/// `ssh-add -T PUBLIC_KEY_PATH`
fn main() -> anyhow::Result<()> {
let path = env::args()
.nth(1)
.expect("argument PUBLIC_KEY_PATH missing");

let identity = read_to_string(Path::new(&path))?;
let key_type = identity.split(" ").next().expect("Invalid key format");
let agent_path = env::var("SSH_AUTH_SOCK").expect("SSH_AUTH_SOCK is not set");
let mut client = Client::connect(Path::new(agent_path.as_str()))?;

let mut data = BytesMut::zeroed(32);
rng().fill_bytes(&mut data);
let data = data.freeze();

let sig = client.sign_with_ref(identity, &data)?;
verify_signature(identity.into(), &data, &sig)?;
if key_type.contains("-cert-") {
let cert = Certificate::from_openssh(&identity)?;
let sig = client.sign(&cert, &data)?;
cert.public_key().verify(&data, &sig)?;
} else {
let key = PublicKey::from_openssh(&identity)?;
let sig = client.sign(&key, &data)?;
key.key_data().verify(&data, &sig)?;
}
Ok(())
}

fn verify_signature(key: &KeyData, data: &[u8], sig: &Signature) -> Result<()> {
return Ok(key
.verify(data.as_ref(), &sig)
.expect("verification failed"));
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ impl<'a> From<Certificate> for Identity<'a> {
}
}

impl<'a> From<&'a Certificate> for Identity<'a> {
fn from(value: &'a Certificate) -> Self {
Identity::Certificate(Box::new(Cow::Borrowed(value)))
}
}

impl<'a> From<&'a Identity<'a>> for &'a KeyData {
fn from(value: &'a Identity) -> Self {
match value {
Expand Down