rust_sign/
keys.rs

1//! Key generation and management for Ed25519 signing.
2
3use crate::error::{Result, SignError};
4use ed25519_dalek::{SigningKey, VerifyingKey, Signer as DalekSigner};
5use rand::rngs::OsRng;
6use std::fs;
7use std::path::Path;
8
9/// An Ed25519 keypair for signing documents.
10#[derive(Debug)]
11pub struct KeyPair {
12    signing_key: SigningKey,
13}
14
15impl KeyPair {
16    /// Generate a new random keypair.
17    pub fn generate() -> Self {
18        let signing_key = SigningKey::generate(&mut OsRng);
19        Self { signing_key }
20    }
21
22    /// Create a keypair from raw secret key bytes (32 bytes).
23    pub fn from_bytes(secret_bytes: &[u8; 32]) -> Self {
24        let signing_key = SigningKey::from_bytes(secret_bytes);
25        Self { signing_key }
26    }
27
28    /// Get the secret key bytes.
29    pub fn secret_bytes(&self) -> [u8; 32] {
30        self.signing_key.to_bytes()
31    }
32
33    /// Get the public key.
34    pub fn public_key(&self) -> PublicKey {
35        PublicKey {
36            verifying_key: self.signing_key.verifying_key(),
37        }
38    }
39
40    /// Sign a message and return the signature bytes.
41    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
42        let signature = self.signing_key.sign(message);
43        signature.to_bytes()
44    }
45
46    /// Save the keypair to a file.
47    /// 
48    /// The file format is:
49    /// - Line 1: "RUST-SIGN PRIVATE KEY"
50    /// - Line 2: Base64-encoded secret key
51    /// - Line 3: Base64-encoded public key
52    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
53        use base64::Engine;
54        let engine = base64::engine::general_purpose::STANDARD;
55        
56        let secret_b64 = engine.encode(self.signing_key.to_bytes());
57        let public_b64 = engine.encode(self.public_key().as_bytes());
58        
59        let content = format!(
60            "RUST-SIGN PRIVATE KEY\n{}\n{}\n",
61            secret_b64, public_b64
62        );
63        
64        fs::write(path, content)?;
65        Ok(())
66    }
67
68    /// Load a keypair from a file.
69    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
70        use base64::Engine;
71        let engine = base64::engine::general_purpose::STANDARD;
72        
73        let content = fs::read_to_string(path)?;
74        let lines: Vec<&str> = content.lines().collect();
75        
76        if lines.len() < 2 || lines[0] != "RUST-SIGN PRIVATE KEY" {
77            return Err(SignError::InvalidKey(
78                "Invalid key file format".to_string(),
79            ));
80        }
81        
82        let secret_bytes = engine.decode(lines[1])?;
83        if secret_bytes.len() != 32 {
84            return Err(SignError::InvalidKey(format!(
85                "Invalid secret key length: expected 32, got {}",
86                secret_bytes.len()
87            )));
88        }
89        
90        let mut arr = [0u8; 32];
91        arr.copy_from_slice(&secret_bytes);
92        Ok(Self::from_bytes(&arr))
93    }
94}
95
96/// An Ed25519 public key for verifying signatures.
97#[derive(Debug, Clone)]
98pub struct PublicKey {
99    verifying_key: VerifyingKey,
100}
101
102impl PublicKey {
103    /// Create a public key from raw bytes (32 bytes).
104    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
105        if bytes.len() != 32 {
106            return Err(SignError::InvalidKey(format!(
107                "Invalid public key length: expected 32, got {}",
108                bytes.len()
109            )));
110        }
111        
112        let mut arr = [0u8; 32];
113        arr.copy_from_slice(bytes);
114        let verifying_key = VerifyingKey::from_bytes(&arr)?;
115        Ok(Self { verifying_key })
116    }
117
118    /// Get the raw bytes of the public key.
119    pub fn as_bytes(&self) -> [u8; 32] {
120        self.verifying_key.to_bytes()
121    }
122
123    /// Encode the public key as base64.
124    pub fn to_base64(&self) -> String {
125        use base64::Engine;
126        base64::engine::general_purpose::STANDARD.encode(self.as_bytes())
127    }
128
129    /// Decode a public key from base64.
130    pub fn from_base64(s: &str) -> Result<Self> {
131        use base64::Engine;
132        let bytes = base64::engine::general_purpose::STANDARD.decode(s)?;
133        Self::from_bytes(&bytes)
134    }
135
136    /// Verify a signature on a message.
137    pub fn verify(&self, message: &[u8], signature: &[u8; 64]) -> Result<()> {
138        use ed25519_dalek::Signature;
139        let sig = Signature::from_bytes(signature);
140        self.verifying_key.verify_strict(message, &sig)?;
141        Ok(())
142    }
143
144    /// Save the public key to a file.
145    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
146        use base64::Engine;
147        let engine = base64::engine::general_purpose::STANDARD;
148        
149        let public_b64 = engine.encode(self.as_bytes());
150        let content = format!("RUST-SIGN PUBLIC KEY\n{}\n", public_b64);
151        
152        fs::write(path, content)?;
153        Ok(())
154    }
155
156    /// Load a public key from a file.
157    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
158        use base64::Engine;
159        let engine = base64::engine::general_purpose::STANDARD;
160        
161        let content = fs::read_to_string(path)?;
162        let lines: Vec<&str> = content.lines().collect();
163        
164        if lines.len() < 2 || lines[0] != "RUST-SIGN PUBLIC KEY" {
165            return Err(SignError::InvalidKey(
166                "Invalid public key file format".to_string(),
167            ));
168        }
169        
170        let public_bytes = engine.decode(lines[1])?;
171        Self::from_bytes(&public_bytes)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_keypair_generation() {
181        let keypair = KeyPair::generate();
182        let public_key = keypair.public_key();
183        
184        // Verify we can sign and verify
185        let message = b"Test message";
186        let signature = keypair.sign(message);
187        
188        assert!(public_key.verify(message, &signature).is_ok());
189    }
190
191    #[test]
192    fn test_keypair_roundtrip() {
193        let keypair = KeyPair::generate();
194        let secret_bytes = keypair.secret_bytes();
195        
196        let restored = KeyPair::from_bytes(&secret_bytes);
197        assert_eq!(keypair.secret_bytes(), restored.secret_bytes());
198    }
199
200    #[test]
201    fn test_invalid_signature_fails() {
202        let keypair = KeyPair::generate();
203        let other_keypair = KeyPair::generate();
204        
205        let message = b"Test message";
206        let signature = other_keypair.sign(message);
207        
208        // Signature from different key should fail
209        assert!(keypair.public_key().verify(message, &signature).is_err());
210    }
211
212    #[test]
213    fn test_public_key_base64_roundtrip() {
214        let keypair = KeyPair::generate();
215        let public_key = keypair.public_key();
216        
217        let encoded = public_key.to_base64();
218        let decoded = PublicKey::from_base64(&encoded).unwrap();
219        
220        assert_eq!(public_key.as_bytes(), decoded.as_bytes());
221    }
222}
223