1use crate::{Error, Result};
9use alloc::{string::String, vec::Vec};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[repr(u8)]
15pub enum SigningAlgorithm {
16 Ed25519 = 0x01,
18 EcdsaP256 = 0x02,
20 RsaPss2048 = 0x03,
22}
23
24impl SigningAlgorithm {
25 pub fn from_byte(byte: u8) -> Result<Self> {
27 match byte {
28 0x01 => Ok(SigningAlgorithm::Ed25519),
29 0x02 => Ok(SigningAlgorithm::EcdsaP256),
30 0x03 => Ok(SigningAlgorithm::RsaPss2048),
31 _ => Err(Error::UnsupportedAlgorithm(byte)),
32 }
33 }
34
35 pub fn name(&self) -> &'static str {
37 match self {
38 SigningAlgorithm::Ed25519 => "ed25519",
39 SigningAlgorithm::EcdsaP256 => "ecdsa-p256",
40 SigningAlgorithm::RsaPss2048 => "rsa-pss-2048",
41 }
42 }
43
44 pub fn is_supported(&self) -> bool {
46 matches!(
47 self,
48 SigningAlgorithm::Ed25519 | SigningAlgorithm::EcdsaP256
49 )
50 }
51}
52
53#[derive(Clone)]
55pub struct KeyPair {
56 pub public_key_pem: String,
58 pub private_key_pem: String,
60}
61
62impl core::fmt::Debug for KeyPair {
63 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64 f.debug_struct("KeyPair")
65 .field("public_key_pem", &self.public_key_pem)
66 .field("private_key_pem", &"[REDACTED]")
67 .finish()
68 }
69}
70
71impl Drop for KeyPair {
72 fn drop(&mut self) {
73 use zeroize::Zeroize;
74 self.private_key_pem.zeroize();
75 }
76}
77
78#[cfg(feature = "signing")]
80pub fn generate_key_pair(algorithm: SigningAlgorithm) -> Result<KeyPair> {
81 match algorithm {
82 SigningAlgorithm::Ed25519 => generate_ed25519_key_pair_pem(),
83 SigningAlgorithm::EcdsaP256 => generate_ecdsa_p256_key_pair(),
84 SigningAlgorithm::RsaPss2048 => generate_rsa_pss_key_pair(),
85 }
86}
87
88#[cfg(feature = "signing")]
89fn generate_ed25519_key_pair_pem() -> Result<KeyPair> {
90 use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
91 use ed25519_dalek::SigningKey;
92 use rand::rngs::OsRng;
93
94 let signing_key = SigningKey::generate(&mut OsRng);
95 let verifying_key = signing_key.verifying_key();
96
97 let private_pem = signing_key
98 .to_pkcs8_pem(Default::default())
99 .map_err(|e| Error::KeyGenerationFailed(e.to_string()))?
100 .to_string();
101 let public_pem = verifying_key
102 .to_public_key_pem(Default::default())
103 .map_err(|e| Error::KeyGenerationFailed(e.to_string()))?;
104
105 Ok(KeyPair {
106 public_key_pem: public_pem,
107 private_key_pem: private_pem,
108 })
109}
110
111#[cfg(feature = "signing")]
117pub fn generate_ed25519_key_pair() -> Result<(Vec<u8>, Vec<u8>)> {
118 use ed25519_dalek::SigningKey;
119 use rand::rngs::OsRng;
120
121 let signing_key = SigningKey::generate(&mut OsRng);
122 let verifying_key = signing_key.verifying_key();
123
124 Ok((
125 verifying_key.as_bytes().to_vec(),
126 signing_key.as_bytes().to_vec(),
127 ))
128}
129
130#[cfg(feature = "signing")]
131fn generate_ecdsa_p256_key_pair() -> Result<KeyPair> {
132 use p256::ecdsa::SigningKey;
133 use p256::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
134 use rand::rngs::OsRng;
135
136 let signing_key = SigningKey::random(&mut OsRng);
137 let verifying_key = signing_key.verifying_key();
138
139 let private_pem = signing_key
140 .to_pkcs8_pem(LineEnding::LF)
141 .map_err(|e| Error::KeyGenerationFailed(e.to_string()))?
142 .to_string();
143 let public_pem = verifying_key
144 .to_public_key_pem(LineEnding::LF)
145 .map_err(|e| Error::KeyGenerationFailed(e.to_string()))?;
146
147 Ok(KeyPair {
148 public_key_pem: public_pem,
149 private_key_pem: private_pem,
150 })
151}
152
153#[cfg(feature = "signing")]
158fn generate_rsa_pss_key_pair() -> Result<KeyPair> {
159 Err(Error::KeyGenerationFailed(
160 "RSA-PSS key generation is not implemented".to_string(),
161 ))
162}
163
164#[cfg(feature = "signing")]
166pub fn sign(data: &[u8], private_key_pem: &str, algorithm: SigningAlgorithm) -> Result<Vec<u8>> {
167 match algorithm {
168 SigningAlgorithm::Ed25519 => sign_ed25519_pem(data, private_key_pem),
169 SigningAlgorithm::EcdsaP256 => sign_ecdsa_p256(data, private_key_pem),
170 SigningAlgorithm::RsaPss2048 => sign_rsa_pss(data, private_key_pem),
171 }
172}
173
174#[cfg(feature = "signing")]
175fn sign_ed25519_pem(data: &[u8], private_key_pem: &str) -> Result<Vec<u8>> {
176 use ed25519_dalek::pkcs8::DecodePrivateKey;
177 use ed25519_dalek::{Signer, SigningKey};
178
179 let signing_key = SigningKey::from_pkcs8_pem(private_key_pem)
180 .map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
181
182 let signature = signing_key.sign(data);
183 Ok(signature.to_bytes().to_vec())
184}
185
186#[cfg(feature = "signing")]
195pub fn sign_ed25519(data: &[u8], private_key: &[u8]) -> Result<Vec<u8>> {
196 use ed25519_dalek::{Signer, SigningKey};
197
198 if private_key.len() != 32 {
199 return Err(Error::InvalidKeyLength {
200 expected: 32,
201 actual: private_key.len(),
202 });
203 }
204
205 let signing_key = SigningKey::from_bytes(
206 &private_key
207 .try_into()
208 .map_err(|_| Error::InvalidKeyFormat("Failed to convert key bytes".to_string()))?,
209 );
210
211 let signature = signing_key.sign(data);
212 Ok(signature.to_bytes().to_vec())
213}
214
215#[cfg(feature = "signing")]
216fn sign_ecdsa_p256(data: &[u8], private_key_pem: &str) -> Result<Vec<u8>> {
217 use p256::ecdsa::{signature::Signer, Signature, SigningKey};
218 use p256::pkcs8::DecodePrivateKey;
219
220 let signing_key = SigningKey::from_pkcs8_pem(private_key_pem)
221 .map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
222 let signature: Signature = signing_key.sign(data);
223 Ok(signature.to_der().as_bytes().to_vec())
224}
225
226#[cfg(feature = "signing")]
227fn sign_rsa_pss(_data: &[u8], _private_key_pem: &str) -> Result<Vec<u8>> {
228 Err(Error::SigningFailed(
229 "RSA-PSS is a reserved identifier and is not supported".to_string(),
230 ))
231}
232
233#[cfg(feature = "signing")]
235pub fn verify(
236 data: &[u8],
237 signature: &[u8],
238 public_key_pem: &str,
239 algorithm: SigningAlgorithm,
240) -> Result<bool> {
241 match algorithm {
242 SigningAlgorithm::Ed25519 => verify_ed25519_pem(data, signature, public_key_pem),
243 SigningAlgorithm::EcdsaP256 => verify_ecdsa_p256(data, signature, public_key_pem),
244 SigningAlgorithm::RsaPss2048 => verify_rsa_pss(data, signature, public_key_pem),
245 }
246}
247
248#[cfg(feature = "signing")]
249fn verify_ed25519_pem(data: &[u8], signature: &[u8], public_key_pem: &str) -> Result<bool> {
250 use ed25519_dalek::pkcs8::DecodePublicKey;
251 use ed25519_dalek::{Signature, VerifyingKey};
252
253 if signature.len() != 64 {
254 return Err(Error::InvalidKeyLength {
255 expected: 64,
256 actual: signature.len(),
257 });
258 }
259
260 let verifying_key = VerifyingKey::from_public_key_pem(public_key_pem)
261 .map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
262
263 let sig =
264 Signature::from_bytes(&signature.try_into().map_err(|_| {
265 Error::InvalidKeyFormat("Failed to convert signature bytes".to_string())
266 })?);
267
268 verifying_key
269 .verify_strict(data, &sig)
270 .map(|_| true)
271 .map_err(|_| Error::SignatureVerificationFailed)
272}
273
274#[cfg(feature = "signing")]
284pub fn verify_ed25519(data: &[u8], signature: &[u8], public_key: &[u8]) -> Result<bool> {
285 use ed25519_dalek::{Signature, VerifyingKey};
286
287 if public_key.len() != 32 {
288 return Err(Error::InvalidKeyLength {
289 expected: 32,
290 actual: public_key.len(),
291 });
292 }
293
294 if signature.len() != 64 {
295 return Err(Error::InvalidKeyLength {
296 expected: 64,
297 actual: signature.len(),
298 });
299 }
300
301 let verifying_key = VerifyingKey::from_bytes(
302 &public_key
303 .try_into()
304 .map_err(|_| Error::InvalidKeyFormat("Failed to convert key bytes".to_string()))?,
305 )
306 .map_err(|_| Error::InvalidKeyFormat("Invalid public key".to_string()))?;
307
308 let sig =
309 Signature::from_bytes(&signature.try_into().map_err(|_| {
310 Error::InvalidKeyFormat("Failed to convert signature bytes".to_string())
311 })?);
312
313 verifying_key
314 .verify_strict(data, &sig)
315 .map(|_| true)
316 .map_err(|_| Error::SignatureVerificationFailed)
317}
318
319#[cfg(feature = "signing")]
320fn verify_ecdsa_p256(data: &[u8], signature: &[u8], public_key_pem: &str) -> Result<bool> {
321 use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
322 use p256::pkcs8::DecodePublicKey;
323
324 let verifying_key = VerifyingKey::from_public_key_pem(public_key_pem)
325 .map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
326 let signature =
327 Signature::from_der(signature).map_err(|_| Error::SignatureVerificationFailed)?;
328 verifying_key
329 .verify(data, &signature)
330 .map(|_| true)
331 .map_err(|_| Error::SignatureVerificationFailed)
332}
333
334#[cfg(feature = "signing")]
335fn verify_rsa_pss(_data: &[u8], _signature: &[u8], _public_key_pem: &str) -> Result<bool> {
336 Err(Error::SigningFailed(
337 "RSA-PSS is a reserved identifier and is not supported".to_string(),
338 ))
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_algorithm_from_byte() {
347 assert_eq!(
348 SigningAlgorithm::from_byte(0x01).unwrap(),
349 SigningAlgorithm::Ed25519
350 );
351 assert_eq!(
352 SigningAlgorithm::from_byte(0x02).unwrap(),
353 SigningAlgorithm::EcdsaP256
354 );
355 assert_eq!(
356 SigningAlgorithm::from_byte(0x03).unwrap(),
357 SigningAlgorithm::RsaPss2048
358 );
359 assert!(SigningAlgorithm::from_byte(0xFF).is_err());
360 }
361
362 #[test]
363 fn test_algorithm_name() {
364 assert_eq!(SigningAlgorithm::Ed25519.name(), "ed25519");
365 assert_eq!(SigningAlgorithm::EcdsaP256.name(), "ecdsa-p256");
366 assert_eq!(SigningAlgorithm::RsaPss2048.name(), "rsa-pss-2048");
367 assert!(SigningAlgorithm::Ed25519.is_supported());
368 assert!(SigningAlgorithm::EcdsaP256.is_supported());
369 assert!(!SigningAlgorithm::RsaPss2048.is_supported());
370 }
371
372 #[test]
373 fn test_standard_pem_roundtrips_and_signatures() {
374 use ed25519_dalek::pkcs8::{
375 DecodePrivateKey as EdDecodePrivateKey, DecodePublicKey as EdDecodePublicKey,
376 };
377 let message = b"standard signing envelope";
378 for algorithm in [SigningAlgorithm::Ed25519, SigningAlgorithm::EcdsaP256] {
379 let pair = generate_key_pair(algorithm).unwrap();
380 assert!(pair
381 .private_key_pem
382 .starts_with("-----BEGIN PRIVATE KEY-----"));
383 assert!(pair
384 .public_key_pem
385 .starts_with("-----BEGIN PUBLIC KEY-----"));
386 let debug = format!("{pair:?}");
387 assert!(debug.contains("[REDACTED]"));
388 assert!(!debug.contains(&pair.private_key_pem));
389
390 match algorithm {
391 SigningAlgorithm::Ed25519 => {
392 ed25519_dalek::SigningKey::from_pkcs8_pem(&pair.private_key_pem).unwrap();
393 ed25519_dalek::VerifyingKey::from_public_key_pem(&pair.public_key_pem).unwrap();
394 }
395 SigningAlgorithm::EcdsaP256 => {
396 p256::ecdsa::SigningKey::from_pkcs8_pem(&pair.private_key_pem).unwrap();
397 p256::ecdsa::VerifyingKey::from_public_key_pem(&pair.public_key_pem).unwrap();
398 }
399 SigningAlgorithm::RsaPss2048 => unreachable!(),
400 }
401
402 let signature = sign(message, &pair.private_key_pem, algorithm).unwrap();
403 assert!(verify(message, &signature, &pair.public_key_pem, algorithm).unwrap());
404 assert!(verify(b"tampered", &signature, &pair.public_key_pem, algorithm).is_err());
405 }
406
407 assert!(generate_key_pair(SigningAlgorithm::RsaPss2048).is_err());
408 }
409}