From 157d6081b8b7401c945ad3d52f503db954f97593 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:14:45 +0200 Subject: [PATCH] fix sign --- bin/ssh-agentctl/src/main.rs | 41 ++++++++++++++++++++++++++++----- lib/ssh-agent-client/src/lib.rs | 1 + lib/ssh-transport/src/parse.rs | 5 ++-- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/bin/ssh-agentctl/src/main.rs b/bin/ssh-agentctl/src/main.rs index b6f7c38..31bce81 100644 --- a/bin/ssh-agentctl/src/main.rs +++ b/bin/ssh-agentctl/src/main.rs @@ -2,8 +2,9 @@ use std::{io::Write, path::PathBuf}; use clap::Parser; use eyre::{bail, Context}; +use sha2::Digest; use ssh_agent_client::{IdentityAnswer, SocketAgentConnection}; -use ssh_transport::key::PublicKey; +use ssh_transport::{key::PublicKey, parse::Writer}; #[derive(clap::Parser, Debug)] struct Args { @@ -28,8 +29,11 @@ enum Subcommand { /// Sign a blob, SSH_AGENTC_SIGN_REQUEST Sign { /// The key-id of the key, obtained with list-identities --key-id - #[arg(short, long = "key")] + #[arg(short, long)] key: Option, + /// The domain of the signature, for example 'file' + #[arg(short, long)] + namespace: String, file: PathBuf, }, /// Temporarily lock the agent with a passphrase, SSH_AGENTC_LOCK @@ -64,7 +68,15 @@ async fn main() -> eyre::Result<()> { Subcommand::ListIdentities { key_id } => { list_ids(&mut agent, key_id).await?; } - Subcommand::Sign { file, key } => { + Subcommand::Sign { + file, + key, + namespace, + } => { + if namespace.is_empty() { + bail!("namespace must not be empty"); + } + let file = std::fs::read(&file) .wrap_err_with(|| format!("reading file {}", file.display()))?; @@ -107,10 +119,27 @@ async fn main() -> eyre::Result<()> { } }; - let signature = agent.sign(&key.key_blob, &file, 0).await?; + // - // TODO: https://github.com/openssh/openssh-portable/blob/a76a6b85108e3032c8175611ecc5746e7131f876/PROTOCOL.sshsig - let signature = pem::encode(&pem::Pem::new("SSH SIGNATURE", signature)); + let mut sign_data = Writer::new(); + sign_data.raw(b"SSHSIG"); + sign_data.string(&namespace); + sign_data.string([]); + sign_data.string(b"sha512"); + sign_data.string(sha2::Sha512::digest(file)); + + let signature = agent.sign(&key.key_blob, &sign_data.finish(), 0).await?; + + let mut sig = Writer::new(); + sig.raw(b"SSHSIG"); + sig.u32(1); // version + sig.string(&key.key_blob); // publickey + sig.string(namespace.as_bytes()); + sig.string(b""); // reserved + sig.string(b"sha512"); // hash algorithm + sig.string(&signature); + + let signature = pem::encode(&pem::Pem::new("SSH SIGNATURE", sig.finish())); std::io::stdout().write_all(signature.as_bytes())?; } Subcommand::Lock => { diff --git a/lib/ssh-agent-client/src/lib.rs b/lib/ssh-agent-client/src/lib.rs index d20757a..23f04b7 100644 --- a/lib/ssh-agent-client/src/lib.rs +++ b/lib/ssh-agent-client/src/lib.rs @@ -113,6 +113,7 @@ pub enum ExtensionResponse { /// A single identity in SSH_AGENT_IDENTITIES_ANSWER. #[derive(Debug)] pub struct IdentityAnswer { + /// The public key in the SSH wire encoding. pub key_blob: Vec, pub comment: String, } diff --git a/lib/ssh-transport/src/parse.rs b/lib/ssh-transport/src/parse.rs index b65aaad..6bb2fba 100644 --- a/lib/ssh-transport/src/parse.rs +++ b/lib/ssh-transport/src/parse.rs @@ -107,7 +107,7 @@ impl<'a> Parser<'a> { } } -/// A simplified `byteorder` clone that emits client errors when the data is too short. +/// A writer for the SSH wire format. pub struct Writer(Vec); impl Writer { @@ -149,7 +149,8 @@ impl Writer { self.raw(bytes); } - pub fn string(&mut self, data: &[u8]) { + pub fn string(&mut self, data: impl AsRef<[u8]>) { + let data = data.as_ref(); self.u32(data.len() as u32); self.raw(data); }