Skip to content

Commit 237c8ff

Browse files
committed
API cleanup
1 parent 6562551 commit 237c8ff

5 files changed

Lines changed: 199 additions & 127 deletions

File tree

russh/src/auth.rs

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
2525
use crate::CryptoVec;
2626
use crate::helpers::NameList;
2727
use crate::keys::PrivateKeyWithHashAlg;
28+
use crate::keys::agent::AgentIdentity;
2829

2930
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3031
pub enum MethodKind {
@@ -157,28 +158,12 @@ impl AuthResult {
157158
pub trait Signer: Sized {
158159
type Error: From<crate::SendError>;
159160

160-
fn auth_publickey_sign(
161+
fn auth_sign(
161162
&mut self,
162-
key: &ssh_key::PublicKey,
163+
key: &AgentIdentity,
163164
hash_alg: Option<HashAlg>,
164165
to_sign: CryptoVec,
165166
) -> impl Future<Output = Result<CryptoVec, Self::Error>> + Send;
166-
167-
/// Sign authentication data using a certificate identity.
168-
///
169-
/// The default implementation returns an error, indicating certificate
170-
/// signing is not supported. Implementations that support certificate
171-
/// signing (e.g., SSH agent clients) should override this method.
172-
///
173-
/// For RSA certificates, you can specify the hash algorithm to use.
174-
fn auth_certificate_sign(
175-
&mut self,
176-
_cert: &Certificate,
177-
_hash_alg: Option<HashAlg>,
178-
_to_sign: CryptoVec,
179-
) -> impl Future<Output = Result<CryptoVec, Self::Error>> + Send {
180-
async { Err((crate::SendError {}).into()) }
181-
}
182167
}
183168

184169
#[derive(Debug, Error)]
@@ -196,9 +181,9 @@ impl<R: AsyncRead + AsyncWrite + Unpin + Send + 'static> Signer
196181
type Error = AgentAuthError;
197182

198183
#[allow(clippy::manual_async_fn)]
199-
fn auth_publickey_sign(
184+
fn auth_sign(
200185
&mut self,
201-
key: &ssh_key::PublicKey,
186+
key: &AgentIdentity,
202187
hash_alg: Option<HashAlg>,
203188
to_sign: CryptoVec,
204189
) -> impl Future<Output = Result<CryptoVec, Self::Error>> {
@@ -208,20 +193,6 @@ impl<R: AsyncRead + AsyncWrite + Unpin + Send + 'static> Signer
208193
.map_err(Into::into)
209194
}
210195
}
211-
212-
#[allow(clippy::manual_async_fn)]
213-
fn auth_certificate_sign(
214-
&mut self,
215-
cert: &Certificate,
216-
hash_alg: Option<HashAlg>,
217-
to_sign: CryptoVec,
218-
) -> impl Future<Output = Result<CryptoVec, Self::Error>> {
219-
async move {
220-
self.sign_request_cert(cert, hash_alg, to_sign)
221-
.await
222-
.map_err(Into::into)
223-
}
224-
}
225196
}
226197

227198
#[derive(Debug)]

russh/src/client/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ impl<H: Handler> Handle<H> {
485485
});
486486
}
487487
Some(Reply::SignRequest { key, data }) => {
488-
let data = signer.auth_publickey_sign(&key, hash_alg, data).await;
488+
let data = signer.auth_sign(&key.into(), hash_alg, data).await;
489489
let data = match data {
490490
Ok(data) => data,
491491
Err(e) => return Err(e),
@@ -542,8 +542,12 @@ impl<H: Handler> Handle<H> {
542542
partial_success,
543543
});
544544
}
545-
Some(Reply::SignRequestCert { cert, hash_alg, data }) => {
546-
let data = signer.auth_certificate_sign(&cert, hash_alg, data).await;
545+
Some(Reply::SignRequestCert {
546+
cert,
547+
hash_alg,
548+
data,
549+
}) => {
550+
let data = signer.auth_sign(&cert.into(), hash_alg, data).await;
547551
let data = match data {
548552
Ok(data) => data,
549553
Err(e) => return Err(e),

russh/src/keys/agent/client.rs

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use ssh_key::{Algorithm, Certificate, HashAlg, PrivateKey, PublicKey, Signature}
88
use tokio;
99
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
1010

11-
use super::{msg, AgentIdentity, Constraint};
12-
use crate::helpers::EncodedExt;
13-
use crate::keys::{key, Error};
11+
use super::{AgentIdentity, Constraint, msg};
1412
use crate::CryptoVec;
13+
use crate::helpers::EncodedExt;
14+
use crate::keys::{Error, key};
1515

1616
pub trait AgentStream: AsyncRead + AsyncWrite {}
1717

@@ -254,43 +254,8 @@ impl<S: AgentStream + Unpin> AgentClient<S> {
254254
Ok(())
255255
}
256256

257-
/// Ask the agent for a list of the currently registered public keys.
258-
///
259-
/// Note: Certificates held by the agent will be returned as their underlying public
260-
/// key only, without the certificate data. Use
261-
/// [`request_identities_full`](Self::request_identities_full) to retrieve full
262-
/// certificate information.
263-
pub async fn request_identities(&mut self) -> Result<Vec<PublicKey>, Error> {
264-
self.buf.clear();
265-
self.buf.resize(4);
266-
msg::REQUEST_IDENTITIES.encode(&mut self.buf)?;
267-
let len = self.buf.len() - 4;
268-
BigEndian::write_u32(&mut self.buf[..], len as u32);
269-
270-
self.read_response().await?;
271-
debug!("identities: {:?}", &self.buf[..]);
272-
let mut keys = Vec::new();
273-
274-
#[allow(clippy::indexing_slicing)] // static length
275-
if let Some((&msg::IDENTITIES_ANSWER, mut r)) = self.buf.split_first() {
276-
let n = u32::decode(&mut r)?;
277-
for _ in 0..n {
278-
let key_blob = Bytes::decode(&mut r)?;
279-
let comment = String::decode(&mut r)?;
280-
let mut key = key::parse_public_key(&key_blob)?;
281-
key.set_comment(comment);
282-
keys.push(key);
283-
}
284-
}
285-
286-
Ok(keys)
287-
}
288-
289257
/// Ask the agent for a list of identities, including certificates.
290-
///
291-
/// Unlike [`request_identities`](Self::request_identities) which only returns public keys,
292-
/// this method correctly parses OpenSSH certificates held by the agent.
293-
pub async fn request_identities_full(&mut self) -> Result<Vec<AgentIdentity>, Error> {
258+
pub async fn request_identities(&mut self) -> Result<Vec<AgentIdentity>, Error> {
294259
self.buf.clear();
295260
self.buf.resize(4);
296261
msg::REQUEST_IDENTITIES.encode(&mut self.buf)?;
@@ -313,7 +278,7 @@ impl<S: AgentStream + Unpin> AgentClient<S> {
313278
// This avoids parsing the blob twice for regular keys.
314279
let identity = if Self::is_certificate_blob(&key_blob) {
315280
match Certificate::decode(&mut key_blob.as_ref()) {
316-
Ok(cert) => AgentIdentity::Certificate { cert, comment },
281+
Ok(cert) => AgentIdentity::Certificate { certificate: cert, comment },
317282
Err(_) => {
318283
// Fallback to public key if certificate parsing fails
319284
let key = key::parse_public_key(&key_blob)?;
@@ -352,6 +317,20 @@ impl<S: AgentStream + Unpin> AgentClient<S> {
352317

353318
/// Ask the agent to sign the supplied piece of data.
354319
pub async fn sign_request(
320+
&mut self,
321+
identity: &AgentIdentity,
322+
hash_alg: Option<HashAlg>,
323+
data: CryptoVec,
324+
) -> Result<CryptoVec, Error> {
325+
match identity {
326+
AgentIdentity::PublicKey { key, .. } => self.sign_request_pk(key, hash_alg, data).await,
327+
AgentIdentity::Certificate { certificate, .. } => {
328+
self.sign_request_cert(certificate, hash_alg, data).await
329+
}
330+
}
331+
}
332+
333+
async fn sign_request_pk(
355334
&mut self,
356335
public: &PublicKey,
357336
hash_alg: Option<HashAlg>,
@@ -381,7 +360,7 @@ impl<S: AgentStream + Unpin> AgentClient<S> {
381360
/// allowing the agent to match it to the correct private key.
382361
///
383362
/// For RSA certificates, you can specify the hash algorithm to use.
384-
pub async fn sign_request_cert(
363+
async fn sign_request_cert(
385364
&mut self,
386365
cert: &Certificate,
387366
hash_alg: Option<HashAlg>,

russh/src/keys/agent/mod.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,39 @@ pub enum AgentIdentity {
3434
/// An OpenSSH certificate
3535
Certificate {
3636
/// The certificate (contains public key plus CA signature, principals, validity, etc.)
37-
cert: Certificate,
37+
certificate: Certificate,
3838
/// Comment associated with this identity
3939
comment: String,
4040
},
4141
}
4242

43+
impl From<PublicKey> for AgentIdentity {
44+
fn from(key: PublicKey) -> Self {
45+
Self::PublicKey {
46+
key,
47+
comment: String::new(),
48+
}
49+
}
50+
}
51+
52+
impl From<Certificate> for AgentIdentity {
53+
fn from(certificate: Certificate) -> Self {
54+
Self::Certificate {
55+
certificate,
56+
comment: String::new(),
57+
}
58+
}
59+
}
60+
4361
impl AgentIdentity {
4462
/// Returns the underlying public key.
4563
/// For certificates, extracts the public key from the certificate.
4664
/// Returns a borrowed reference for plain keys, or an owned value for certificates.
4765
pub fn public_key(&self) -> Cow<'_, PublicKey> {
4866
match self {
4967
Self::PublicKey { key, .. } => Cow::Borrowed(key),
50-
Self::Certificate { cert, .. } => {
51-
Cow::Owned(PublicKey::new(cert.public_key().clone(), ""))
68+
Self::Certificate { certificate, .. } => {
69+
Cow::Owned(PublicKey::new(certificate.public_key().clone(), ""))
5270
}
5371
}
5472
}
@@ -66,7 +84,7 @@ impl AgentIdentity {
6684
mod tests {
6785
use super::*;
6886
use ssh_key::rand_core::OsRng;
69-
use ssh_key::{certificate, PrivateKey};
87+
use ssh_key::{PrivateKey, certificate};
7088

7189
fn create_test_certificate() -> Certificate {
7290
use std::time::{SystemTime, UNIX_EPOCH};
@@ -128,7 +146,7 @@ mod tests {
128146
let comment = "test-cert-comment".to_string();
129147

130148
let identity = AgentIdentity::Certificate {
131-
cert: cert.clone(),
149+
certificate: cert.clone(),
132150
comment: comment.clone(),
133151
};
134152

0 commit comments

Comments
 (0)