Skip to content

Comments

Allow external signer to be implemented on the host side#621

Open
j-baker wants to merge 2 commits intoEugeny:mainfrom
j-baker:jbaker/external-signer
Open

Allow external signer to be implemented on the host side#621
j-baker wants to merge 2 commits intoEugeny:mainfrom
j-baker:jbaker/external-signer

Conversation

@j-baker
Copy link

@j-baker j-baker commented Jan 10, 2026

Fixes #618.

The only difference I made is that I didn't make this take a Signer because that takes an &mut. Instead I added a new trait which is a bit more strongly typed and added a wrapper so that type is compatible.

Copy link
Owner

@Eugeny Eugeny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR! There are some issues with hash alg handling but once that's fixed it should be good to go

}

/// Creates a new key pair from an Arc-wrapped signer.
pub fn from_arc(signer: Arc<dyn PrivateKeySigner>) -> Self {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed as there's already a corresponding From<>

fn from(signer: Arc<dyn PrivateKeySigner>) -> Self {
KeyPair {
signer,
hash_alg: None,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hash_alg is never used anywhere except .algorithm() which itself is never used.

Host keys do not need a hash specification since russh assumes an RSA key can use any hash and automatically chooses the hash corresponding to the negotiated key algo (this PR also breaks that, see below).

Essentially, hash_alg needs to remain a parameter in PrivateKeySigner since the hash is only known by the time negotiation has already happened.

/// let signer = SignerWrapper::new(agent, public_key, None);
/// let keypair = KeyPair::new(signer);
/// ```
pub struct SignerWrapper<S> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since PrivateKeySigner is essentially a superset of Signer, I'd prefer the old trait to be completely replaced by PrivateKeySigner. You can keep it &self too and leave the responsibility of syncing to the user.

pub async fn sign(
&self,
data: &[u8],
_signature_hash_alg: Option<HashAlg>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signature_hash_alg should be used (see above)

)
.map_err(Into::into)?;
let signature = key
.sign(&hash, signature_hash_alg)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, signature_hash_alg is the hash choice I meant above


/// Returns the base algorithm without hash algorithm override.
/// This is useful for key compatibility checking.
pub fn base_algorithm(&self) -> Algorithm {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this to exist since non-RSA algos are already equal to themselves (for is_key_compatible_with_algo) - Algorithm ignores hash_alg for non-RSA keys

}

/// Returns whether this is an RSA key.
pub fn is_rsa(&self) -> bool {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a dedicated fn here

@j-baker j-baker force-pushed the jbaker/external-signer branch from 003cd47 to 185d068 Compare February 22, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Trait for private key management

2 participants