1use crate::error::AuthError;
2use ed25519_dalek::{
3 Signature, Signer, SigningKey as EdSigningKey, VerifyingKey as EdVerifyingKey,
4};
5use std::fs;
6use std::path::Path;
7
8#[derive(Debug, Clone)]
10pub struct SigningKey {
11 inner: EdSigningKey,
12}
13
14impl SigningKey {
15 pub fn generate() -> Self {
17 use rand::rngs::OsRng;
18 use rand::RngCore;
19 let mut bytes = [0u8; 32];
20 OsRng.fill_bytes(&mut bytes);
21 Self {
22 inner: EdSigningKey::from_bytes(&bytes),
23 }
24 }
25
26 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
28 Self {
29 inner: EdSigningKey::from_bytes(bytes),
30 }
31 }
32
33 pub fn verifying_key(&self) -> VerifyingKey {
35 VerifyingKey {
36 inner: self.inner.verifying_key(),
37 }
38 }
39
40 pub fn key_id(&self) -> String {
42 self.verifying_key().key_id()
43 }
44
45 pub fn sign(&self, message: &[u8]) -> Signature {
47 self.inner.sign(message)
48 }
49
50 pub fn to_bytes(&self) -> [u8; 32] {
52 self.inner.to_bytes()
53 }
54
55 pub fn to_keypair_bytes(&self) -> [u8; 64] {
57 self.inner.to_keypair_bytes()
58 }
59
60 pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result<Self, AuthError> {
62 let key = EdSigningKey::from_keypair_bytes(bytes)
63 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid keypair: {:?}", e)))?;
64 Ok(Self { inner: key })
65 }
66
67 pub fn to_pkcs8_der(&self) -> Result<Vec<u8>, AuthError> {
69 use ed25519_dalek::pkcs8::EncodePrivateKey;
70 self.inner
71 .to_pkcs8_der()
72 .map(|der| der.as_bytes().to_vec())
73 .map_err(|e| AuthError::InvalidKeyFormat(format!("PKCS#8 encoding failed: {:?}", e)))
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct VerifyingKey {
80 pub(crate) inner: EdVerifyingKey,
81}
82
83impl VerifyingKey {
84 pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, AuthError> {
86 let key = EdVerifyingKey::from_bytes(bytes)
87 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid public key: {:?}", e)))?;
88 Ok(Self { inner: key })
89 }
90
91 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), AuthError> {
97 self.inner
98 .verify_strict(message, signature)
99 .map_err(|e| AuthError::InvalidKeyFormat(format!("Verification failed: {:?}", e)))
100 }
101
102 pub fn to_bytes(&self) -> [u8; 32] {
104 self.inner.to_bytes()
105 }
106
107 pub fn key_id(&self) -> String {
109 let hex = self
110 .to_bytes()
111 .into_iter()
112 .map(|byte| format!("{byte:02x}"))
113 .collect::<String>();
114 hex[..16].to_string()
115 }
116
117 pub fn to_spki_der(&self) -> Result<Vec<u8>, AuthError> {
119 use ed25519_dalek::pkcs8::EncodePublicKey;
120 self.inner
121 .to_public_key_der()
122 .map(|der| der.as_bytes().to_vec())
123 .map_err(|e| AuthError::InvalidKeyFormat(format!("SPKI encoding failed: {:?}", e)))
124 }
125}
126
127pub struct KeyLoader;
129
130impl KeyLoader {
131 pub fn signing_key_from_env(var_name: &str) -> Result<SigningKey, AuthError> {
133 let b64 = std::env::var(var_name).map_err(|_| {
134 AuthError::KeyLoadingFailed(format!("Environment variable {} not set", var_name))
135 })?;
136 let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64)
137 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid base64: {}", e)))?;
138 let key_bytes: [u8; 32] = bytes
139 .try_into()
140 .map_err(|_| AuthError::InvalidKeyFormat("Invalid key length".to_string()))?;
141 Ok(SigningKey::from_bytes(&key_bytes))
142 }
143
144 pub fn verifying_key_from_env(var_name: &str) -> Result<VerifyingKey, AuthError> {
146 let b64 = std::env::var(var_name).map_err(|_| {
147 AuthError::KeyLoadingFailed(format!("Environment variable {} not set", var_name))
148 })?;
149 let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64)
150 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid base64: {}", e)))?;
151 let key_bytes: [u8; 32] = bytes
152 .try_into()
153 .map_err(|_| AuthError::InvalidKeyFormat("Invalid key length".to_string()))?;
154 VerifyingKey::from_bytes(&key_bytes)
155 }
156
157 pub fn generate_and_save_keys(
159 signing_key_path: impl AsRef<Path>,
160 verifying_key_path: impl AsRef<Path>,
161 ) -> Result<(SigningKey, VerifyingKey), AuthError> {
162 let signing_key = SigningKey::generate();
163 let verifying_key = signing_key.verifying_key();
164
165 let signing_b64 = base64::Engine::encode(
167 &base64::engine::general_purpose::STANDARD,
168 signing_key.to_bytes(),
169 );
170 fs::write(signing_key_path, signing_b64)?;
171
172 let verifying_b64 = base64::Engine::encode(
174 &base64::engine::general_purpose::STANDARD,
175 verifying_key.to_bytes(),
176 );
177 fs::write(verifying_key_path, verifying_b64)?;
178
179 Ok((signing_key, verifying_key))
180 }
181
182 pub fn signing_key_from_file(path: impl AsRef<Path>) -> Result<SigningKey, AuthError> {
184 let b64 = fs::read_to_string(path)?;
185 let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64)
186 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid base64: {}", e)))?;
187 let key_bytes: [u8; 32] = bytes
188 .try_into()
189 .map_err(|_| AuthError::InvalidKeyFormat("Invalid key length".to_string()))?;
190 Ok(SigningKey::from_bytes(&key_bytes))
191 }
192
193 pub fn verifying_key_from_file(path: impl AsRef<Path>) -> Result<VerifyingKey, AuthError> {
195 let b64 = fs::read_to_string(path)?;
196 let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &b64)
197 .map_err(|e| AuthError::InvalidKeyFormat(format!("Invalid base64: {}", e)))?;
198 let key_bytes: [u8; 32] = bytes
199 .try_into()
200 .map_err(|_| AuthError::InvalidKeyFormat("Invalid key length".to_string()))?;
201 VerifyingKey::from_bytes(&key_bytes)
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_generate_and_sign() {
211 let signing_key = SigningKey::generate();
212 let message = b"test message";
213 let signature = signing_key.sign(message);
214
215 let verifying_key = signing_key.verifying_key();
216 assert!(verifying_key.verify(message, &signature).is_ok());
217 }
218
219 #[test]
220 fn test_bytes_roundtrip() {
221 let signing_key = SigningKey::generate();
222 let bytes = signing_key.to_bytes();
223
224 let loaded = SigningKey::from_bytes(&bytes);
225 assert_eq!(
226 signing_key.verifying_key().to_bytes(),
227 loaded.verifying_key().to_bytes()
228 );
229 }
230
231 #[test]
232 fn test_keypair_bytes_roundtrip() {
233 let signing_key = SigningKey::generate();
234 let keypair_bytes = signing_key.to_keypair_bytes();
235
236 let loaded = SigningKey::from_keypair_bytes(&keypair_bytes).unwrap();
237 assert_eq!(
238 signing_key.verifying_key().to_bytes(),
239 loaded.verifying_key().to_bytes()
240 );
241 }
242}