auths_core/crypto/
signer.rs1use crate::config::current_algorithm;
4use crate::crypto::encryption::{decrypt_bytes, encrypt_bytes};
5use crate::error::AgentError;
6use auths_crypto::{CurveType, SecureSeed, TypedSeed};
7use auths_verifier::DevicePublicKey;
8use ssh_agent_lib::ssh_key::{Algorithm as SshAlgorithm, EcdsaCurve};
9use zeroize::Zeroizing;
10
11pub trait SignerKey: Send + Sync + 'static {
13 fn public_key_bytes(&self) -> Vec<u8>;
15
16 fn kind(&self) -> SshAlgorithm;
18
19 fn sign(&self, message: &[u8]) -> Result<Vec<u8>, AgentError>;
21}
22
23pub struct SeedSignerKey {
28 seed: SecureSeed,
29 public_key: DevicePublicKey,
30 curve: CurveType,
31}
32
33impl SeedSignerKey {
34 pub fn new(seed: SecureSeed, public_key: [u8; 32]) -> Self {
38 #[allow(clippy::expect_used)] let dpk = DevicePublicKey::try_new(CurveType::Ed25519, &public_key)
40 .expect("Ed25519 public key is always 32 bytes");
41 Self {
42 seed,
43 public_key: dpk,
44 curve: CurveType::Ed25519,
45 }
46 }
47
48 pub fn new_typed(seed: SecureSeed, public_key: DevicePublicKey) -> Self {
50 let curve = public_key.curve();
51 Self {
52 seed,
53 public_key,
54 curve,
55 }
56 }
57
58 pub fn from_seed(seed: SecureSeed) -> Result<Self, AgentError> {
62 let pk_vec = auths_crypto::typed_public_key(&TypedSeed::Ed25519(*seed.as_bytes()))
63 .map_err(|e| {
64 AgentError::CryptoError(format!("Failed to derive public key from seed: {}", e))
65 })?;
66 #[allow(clippy::expect_used)] let public_key: [u8; 32] = pk_vec.try_into().expect("Ed25519 public key is 32 bytes");
68 Ok(Self::new(seed, public_key))
69 }
70
71 pub fn from_typed_seed(seed: TypedSeed) -> Result<Self, AgentError> {
73 let curve = seed.curve();
74 let pk_bytes = auths_crypto::typed_public_key(&seed)
75 .map_err(|e| AgentError::CryptoError(format!("Failed to derive pk: {e}")))?;
76 let dpk = DevicePublicKey::try_new(curve, &pk_bytes)
77 .map_err(|e| AgentError::CryptoError(format!("Invalid derived pk: {e}")))?;
78 Ok(Self {
79 seed: seed.to_secure_seed(),
80 public_key: dpk,
81 curve,
82 })
83 }
84
85 pub fn curve(&self) -> CurveType {
87 self.curve
88 }
89
90 pub fn typed_public_key(&self) -> &DevicePublicKey {
92 &self.public_key
93 }
94}
95
96impl SignerKey for SeedSignerKey {
97 fn public_key_bytes(&self) -> Vec<u8> {
98 self.public_key.as_bytes().to_vec()
99 }
100
101 fn kind(&self) -> SshAlgorithm {
102 match self.curve {
103 CurveType::Ed25519 => SshAlgorithm::Ed25519,
104 CurveType::P256 => SshAlgorithm::Ecdsa {
105 curve: EcdsaCurve::NistP256,
106 },
107 }
108 }
109
110 fn sign(&self, message: &[u8]) -> Result<Vec<u8>, AgentError> {
111 let typed = match self.curve {
112 CurveType::Ed25519 => TypedSeed::Ed25519(*self.seed.as_bytes()),
113 CurveType::P256 => TypedSeed::P256(*self.seed.as_bytes()),
114 };
115 auths_crypto::typed_sign(&typed, message)
116 .map_err(|e| AgentError::CryptoError(format!("{} signing failed: {e}", self.curve)))
117 }
118}
119
120pub fn extract_seed_from_key_bytes(bytes: &[u8]) -> Result<SecureSeed, AgentError> {
124 let parsed = auths_crypto::parse_key_material(bytes)
125 .map_err(|e| AgentError::KeyDeserializationError(e.to_string()))?;
126 Ok(parsed.seed.to_secure_seed())
127}
128
129pub fn load_seed_and_pubkey(
135 bytes: &[u8],
136) -> Result<(SecureSeed, Vec<u8>, auths_crypto::CurveType), AgentError> {
137 let parsed = auths_crypto::parse_key_material(bytes)
138 .map_err(|e| AgentError::KeyDeserializationError(e.to_string()))?;
139 let curve = parsed.seed.curve();
140 Ok((parsed.seed.to_secure_seed(), parsed.public_key, curve))
141}
142
143pub fn encrypt_keypair(raw: &[u8], passphrase: &str) -> Result<Vec<u8>, AgentError> {
145 encrypt_bytes(raw, passphrase, current_algorithm())
146}
147
148pub fn decrypt_keypair(
150 encrypted: &[u8],
151 passphrase: &str,
152) -> Result<Zeroizing<Vec<u8>>, AgentError> {
153 Ok(Zeroizing::new(decrypt_bytes(encrypted, passphrase)?))
154}