1use crate::error::{Result, SignError};
4use crate::hash::DocumentHash;
5use crate::keys::{KeyPair, PublicKey};
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fs;
10use std::path::Path;
11
12pub const FORMAT_VERSION: &str = "1.0";
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct DocumentSignature {
18 pub version: String,
20
21 pub document_hash: String,
23
24 pub signatures: Vec<SignatureEntry>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct SignatureEntry {
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub signer_id: Option<String>,
34
35 pub public_key: String,
37
38 pub signature: String,
40
41 pub timestamp: DateTime<Utc>,
43
44 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
46 pub metadata: HashMap<String, String>,
47}
48
49impl DocumentSignature {
50 pub fn new(document_hash: DocumentHash) -> Self {
52 Self {
53 version: FORMAT_VERSION.to_string(),
54 document_hash: document_hash.to_base64(),
55 signatures: Vec::new(),
56 }
57 }
58
59 pub fn get_hash(&self) -> Result<DocumentHash> {
61 DocumentHash::from_base64(&self.document_hash)
62 }
63
64 pub fn add_signature(
66 &mut self,
67 keypair: &KeyPair,
68 signer_id: Option<String>,
69 ) -> Result<()> {
70 self.add_signature_with_metadata(keypair, signer_id, HashMap::new())
71 }
72
73 pub fn add_signature_with_metadata(
75 &mut self,
76 keypair: &KeyPair,
77 signer_id: Option<String>,
78 metadata: HashMap<String, String>,
79 ) -> Result<()> {
80 use base64::Engine;
81 let engine = base64::engine::general_purpose::STANDARD;
82
83 let hash = self.get_hash()?;
85
86 let signature_bytes = keypair.sign(hash.as_bytes());
88
89 let entry = SignatureEntry {
90 signer_id,
91 public_key: keypair.public_key().to_base64(),
92 signature: engine.encode(signature_bytes),
93 timestamp: Utc::now(),
94 metadata,
95 };
96
97 self.signatures.push(entry);
98 Ok(())
99 }
100
101 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
103 let json = serde_json::to_string_pretty(self)?;
104 fs::write(path, json)?;
105 Ok(())
106 }
107
108 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
110 let content = fs::read_to_string(path)?;
111 let sig: Self = serde_json::from_str(&content)?;
112 Ok(sig)
113 }
114
115 pub fn from_json(json: &str) -> Result<Self> {
117 let sig: Self = serde_json::from_str(json)?;
118 Ok(sig)
119 }
120
121 pub fn to_json(&self) -> Result<String> {
123 let json = serde_json::to_string_pretty(self)?;
124 Ok(json)
125 }
126
127 pub fn signature_count(&self) -> usize {
129 self.signatures.len()
130 }
131
132 pub fn has_signatures(&self) -> bool {
134 !self.signatures.is_empty()
135 }
136}
137
138impl SignatureEntry {
139 pub fn get_public_key(&self) -> Result<PublicKey> {
141 PublicKey::from_base64(&self.public_key)
142 }
143
144 pub fn get_signature_bytes(&self) -> Result<[u8; 64]> {
146 use base64::Engine;
147 let engine = base64::engine::general_purpose::STANDARD;
148
149 let bytes = engine.decode(&self.signature)?;
150 if bytes.len() != 64 {
151 return Err(SignError::InvalidFormat(format!(
152 "Invalid signature length: expected 64, got {}",
153 bytes.len()
154 )));
155 }
156
157 let mut arr = [0u8; 64];
158 arr.copy_from_slice(&bytes);
159 Ok(arr)
160 }
161
162 pub fn verify(&self, document_hash: &DocumentHash) -> Result<()> {
164 let public_key = self.get_public_key()?;
165 let signature = self.get_signature_bytes()?;
166 public_key.verify(document_hash.as_bytes(), &signature)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::hash::hash_bytes;
174
175 #[test]
176 fn test_create_and_sign() {
177 let keypair = KeyPair::generate();
178 let data = b"Test document content";
179 let hash = hash_bytes(data);
180
181 let mut doc_sig = DocumentSignature::new(hash);
182 doc_sig.add_signature(&keypair, Some("alice@example.com".to_string())).unwrap();
183
184 assert_eq!(doc_sig.signature_count(), 1);
185 assert!(doc_sig.has_signatures());
186 }
187
188 #[test]
189 fn test_multiple_signatures() {
190 let keypair1 = KeyPair::generate();
191 let keypair2 = KeyPair::generate();
192 let data = b"Test document content";
193 let hash = hash_bytes(data);
194
195 let mut doc_sig = DocumentSignature::new(hash);
196 doc_sig.add_signature(&keypair1, Some("alice@example.com".to_string())).unwrap();
197 doc_sig.add_signature(&keypair2, Some("bob@example.com".to_string())).unwrap();
198
199 assert_eq!(doc_sig.signature_count(), 2);
200 }
201
202 #[test]
203 fn test_json_roundtrip() {
204 let keypair = KeyPair::generate();
205 let data = b"Test document content";
206 let hash = hash_bytes(data);
207
208 let mut doc_sig = DocumentSignature::new(hash);
209 doc_sig.add_signature(&keypair, Some("alice@example.com".to_string())).unwrap();
210
211 let json = doc_sig.to_json().unwrap();
212 let restored = DocumentSignature::from_json(&json).unwrap();
213
214 assert_eq!(doc_sig.document_hash, restored.document_hash);
215 assert_eq!(doc_sig.signature_count(), restored.signature_count());
216 }
217
218 #[test]
219 fn test_verify_signature() {
220 let keypair = KeyPair::generate();
221 let data = b"Test document content";
222 let hash = hash_bytes(data);
223
224 let mut doc_sig = DocumentSignature::new(hash.clone());
225 doc_sig.add_signature(&keypair, None).unwrap();
226
227 let result = doc_sig.signatures[0].verify(&hash);
229 assert!(result.is_ok());
230
231 let wrong_hash = hash_bytes(b"Different content");
233 let result = doc_sig.signatures[0].verify(&wrong_hash);
234 assert!(result.is_err());
235 }
236}
237