Skip to main content

auths_core/crypto/
signer.rs

1//! Signing key types.
2
3use crate::config::current_algorithm;
4use crate::crypto::encryption::{decrypt_bytes, encrypt_bytes_argon2};
5use crate::crypto::provider_bridge;
6use crate::error::AgentError;
7use auths_crypto::SecureSeed;
8use ssh_agent_lib::ssh_key::Algorithm as SshAlgorithm;
9use zeroize::Zeroizing;
10
11/// A trait implemented by key types that can sign messages and return their public key.
12pub trait SignerKey: Send + Sync + 'static {
13    /// Returns the public key bytes of this key.
14    fn public_key_bytes(&self) -> Vec<u8>;
15
16    /// Returns the key kind (i.e., SSH algorithm) of this key.
17    fn kind(&self) -> SshAlgorithm;
18
19    /// Signs a message and returns the signature bytes.
20    fn sign(&self, message: &[u8]) -> Result<Vec<u8>, AgentError>;
21}
22
23/// SignerKey implementation backed by a SecureSeed, routing through CryptoProvider.
24pub struct SeedSignerKey {
25    seed: SecureSeed,
26    public_key: [u8; 32],
27}
28
29impl SeedSignerKey {
30    /// Create a `SignerKey` from a seed and pre-computed public key.
31    pub fn new(seed: SecureSeed, public_key: [u8; 32]) -> Self {
32        Self { seed, public_key }
33    }
34
35    /// Create a `SignerKey` by deriving the public key from the seed.
36    pub fn from_seed(seed: SecureSeed) -> Result<Self, AgentError> {
37        let public_key =
38            provider_bridge::ed25519_public_key_from_seed_sync(&seed).map_err(|e| {
39                AgentError::CryptoError(format!("Failed to derive public key from seed: {}", e))
40            })?;
41        Ok(Self { seed, public_key })
42    }
43}
44
45impl SignerKey for SeedSignerKey {
46    fn public_key_bytes(&self) -> Vec<u8> {
47        self.public_key.to_vec()
48    }
49
50    fn kind(&self) -> SshAlgorithm {
51        SshAlgorithm::Ed25519
52    }
53
54    fn sign(&self, message: &[u8]) -> Result<Vec<u8>, AgentError> {
55        provider_bridge::sign_ed25519_sync(&self.seed, message)
56            .map_err(|e| AgentError::CryptoError(format!("Ed25519 signing failed: {}", e)))
57    }
58}
59
60/// Extract a SecureSeed from key bytes in various formats.
61///
62/// Delegates to [`auths_crypto::parse_ed25519_seed`].
63pub fn extract_seed_from_key_bytes(bytes: &[u8]) -> Result<SecureSeed, AgentError> {
64    auths_crypto::parse_ed25519_seed(bytes)
65        .map_err(|e| AgentError::KeyDeserializationError(format!("{}", e)))
66}
67
68/// Extract a SecureSeed and public key from key bytes.
69///
70/// For PKCS#8 v2 the public key is extracted from the DER. For other formats,
71/// the public key is derived from the seed via CryptoProvider.
72pub fn load_seed_and_pubkey(bytes: &[u8]) -> Result<(SecureSeed, [u8; 32]), AgentError> {
73    let (seed, maybe_pk) = auths_crypto::parse_ed25519_key_material(bytes)
74        .map_err(|e| AgentError::KeyDeserializationError(format!("{}", e)))?;
75
76    let pubkey = match maybe_pk {
77        Some(pk) => pk,
78        None => provider_bridge::ed25519_public_key_from_seed_sync(&seed).map_err(|e| {
79            AgentError::CryptoError(format!("Failed to derive public key from seed: {}", e))
80        })?,
81    };
82
83    Ok((seed, pubkey))
84}
85
86/// Encrypts a raw serialized keypair using the configured encryption algorithm and passphrase.
87pub fn encrypt_keypair(raw: &[u8], passphrase: &str) -> Result<Vec<u8>, AgentError> {
88    encrypt_bytes_argon2(raw, passphrase, current_algorithm())
89}
90
91/// Decrypts a previously encrypted keypair using the configured encryption algorithm and passphrase.
92pub fn decrypt_keypair(
93    encrypted: &[u8],
94    passphrase: &str,
95) -> Result<Zeroizing<Vec<u8>>, AgentError> {
96    Ok(Zeroizing::new(decrypt_bytes(encrypted, passphrase)?))
97}