Skip to main content

ave_identity/
signature.rs

1//! Signed values with metadata.
2//!
3//! These types keep the signature, signer, timestamp and content hash together
4//! so callers can store or transmit a self-describing signed payload.
5
6use crate::{
7    error::CryptoError,
8    hash::{BLAKE3_HASHER, DigestIdentifier, Hash as HashTrait},
9    keys::{DSA, PublicKey, SignatureIdentifier},
10    timestamp::TimeStamp,
11};
12
13use borsh::{BorshDeserialize, BorshSerialize};
14use serde::{Deserialize, Serialize};
15use std::hash::Hash;
16use subtle::ConstantTimeEq;
17
18/// Signature metadata plus the signature bytes.
19#[derive(
20    Debug,
21    Clone,
22    Serialize,
23    Deserialize,
24    Eq,
25    BorshSerialize,
26    BorshDeserialize,
27    PartialEq,
28    Hash,
29    Ord,
30    PartialOrd,
31)]
32pub struct Signature {
33    /// Public key of the signer.
34    pub signer: PublicKey,
35    /// Creation timestamp.
36    pub timestamp: TimeStamp,
37    /// Hash of the serialized content and timestamp.
38    pub content_hash: DigestIdentifier,
39    /// Signature bytes.
40    pub value: SignatureIdentifier,
41}
42
43impl Signature {
44    /// Signs `content`.
45    ///
46    /// The method serializes `(content, timestamp)` with Borsh, hashes the
47    /// result with Blake3 and signs that digest.
48    pub fn new<T: BorshSerialize>(
49        content: &T,
50        signer: &dyn DSA,
51    ) -> Result<Self, CryptoError> {
52        let timestamp = TimeStamp::now();
53
54        // Serialize content + timestamp together for signing
55        // This single serialization is used for both hashing and later verification
56        let signing_payload = (content, &timestamp);
57        let payload_bytes = borsh::to_vec(&signing_payload)
58            .map_err(|e| CryptoError::SerializationError(e.to_string()))?;
59
60        // Hash the payload
61        let content_hash = BLAKE3_HASHER.hash(&payload_bytes);
62
63        // Sign the hash (not the full payload for efficiency)
64        let signature_bytes =
65            signer.sign(content_hash.hash_bytes()).map_err(|_| {
66                CryptoError::SigningError(
67                    "Failed to create signature".to_string(),
68                )
69            })?;
70
71        // Create public key wrapper with the algorithm from the signer
72        let public_key =
73            PublicKey::new(signer.algorithm(), signer.public_key_bytes())?;
74
75        Ok(Self {
76            signer: public_key,
77            timestamp,
78            content_hash,
79            value: signature_bytes,
80        })
81    }
82
83    /// Verifies this signature against `content`.
84    pub fn verify<T: BorshSerialize>(
85        &self,
86        content: &T,
87    ) -> Result<(), CryptoError> {
88        // Recreate the signing payload
89        let signing_payload = (content, &self.timestamp);
90        let payload_bytes = borsh::to_vec(&signing_payload)
91            .map_err(|e| CryptoError::SerializationError(e.to_string()))?;
92
93        // Hash the payload
94        let computed_hash = BLAKE3_HASHER.hash(&payload_bytes);
95
96        // Verify hash matches using constant-time comparison
97        let hash_matches = computed_hash
98            .hash_bytes()
99            .ct_eq(self.content_hash.hash_bytes());
100        if hash_matches.unwrap_u8() != 1 {
101            return Err(CryptoError::HashError(
102                "Content hash mismatch".to_string(),
103            ));
104        }
105
106        // Verify the signature
107        self.signer
108            .verify(self.content_hash.hash_bytes(), &self.value)
109    }
110}
111
112/// Content bundled together with its signature metadata.
113#[derive(
114    Debug,
115    Clone,
116    Serialize,
117    Deserialize,
118    Eq,
119    PartialEq,
120    BorshSerialize,
121    BorshDeserialize,
122    Hash,
123)]
124pub struct Signed<T>
125where
126    T: BorshSerialize + BorshDeserialize + Clone,
127{
128    /// Signed content.
129    content: T,
130    /// Signature metadata for `content`.
131    signature: Signature,
132}
133
134impl<T: BorshSerialize + BorshDeserialize + Clone> Signed<T> {
135    /// Signs `content` and stores the resulting metadata alongside it.
136    pub fn new(content: T, signer: &dyn DSA) -> Result<Self, CryptoError> {
137        let signature = Signature::new(&content, signer)?;
138        Ok(Self { content, signature })
139    }
140
141    /// Creates a signed value from pre-existing parts.
142    pub const fn from_parts(content: T, signature: Signature) -> Self {
143        Self { content, signature }
144    }
145
146    /// Verifies the stored content against the stored signature.
147    pub fn verify(&self) -> Result<(), CryptoError> {
148        self.signature.verify(&self.content)
149    }
150
151    /// Returns the signature metadata.
152    pub const fn signature(&self) -> &Signature {
153        &self.signature
154    }
155
156    /// Returns the signed content.
157    pub const fn content(&self) -> &T {
158        &self.content
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use crate::keys::Ed25519Signer;
166
167    #[derive(
168        Debug,
169        Clone,
170        PartialEq,
171        Eq,
172        BorshSerialize,
173        BorshDeserialize,
174        Serialize,
175        Deserialize,
176    )]
177    struct TestData {
178        value: String,
179        number: u64,
180    }
181
182    #[test]
183    fn test_signature_creation_and_verification() {
184        let signer = Ed25519Signer::generate().unwrap();
185        let data = TestData {
186            value: "test message".to_string(),
187            number: 42,
188        };
189
190        let signature = Signature::new(&data, &signer).unwrap();
191        assert!(signature.verify(&data).is_ok());
192    }
193
194    #[test]
195    fn test_signature_fails_with_wrong_content() {
196        let signer = Ed25519Signer::generate().unwrap();
197        let data1 = TestData {
198            value: "message 1".to_string(),
199            number: 1,
200        };
201        let data2 = TestData {
202            value: "message 2".to_string(),
203            number: 2,
204        };
205
206        let signature = Signature::new(&data1, &signer).unwrap();
207        assert!(signature.verify(&data2).is_err());
208    }
209
210    #[test]
211    fn test_signed_creation() {
212        let signer = Ed25519Signer::generate().unwrap();
213        let data = TestData {
214            value: "signed data".to_string(),
215            number: 123,
216        };
217
218        let signed = Signed::new(data.clone(), &signer).unwrap();
219        assert_eq!(signed.content, data);
220        assert!(signed.verify().is_ok());
221    }
222
223    #[test]
224    fn test_signed_verification() {
225        let signer = Ed25519Signer::generate().unwrap();
226        let data = TestData {
227            value: "verify me".to_string(),
228            number: 999,
229        };
230
231        let signed = Signed::new(data, &signer).unwrap();
232        assert!(signed.verify().is_ok());
233    }
234
235    #[test]
236    fn test_signed_from_parts() {
237        let signer = Ed25519Signer::generate().unwrap();
238        let data = TestData {
239            value: "test".to_string(),
240            number: 1,
241        };
242
243        let signature = Signature::new(&data, &signer).unwrap();
244        let signed = Signed::from_parts(data.clone(), signature);
245
246        assert_eq!(signed.content().value, "test");
247        assert!(signed.verify().is_ok());
248    }
249
250    #[test]
251    fn test_signer_accessor() {
252        let signer = Ed25519Signer::generate().unwrap();
253        let data = TestData {
254            value: "test".to_string(),
255            number: 1,
256        };
257
258        let signed = Signed::new(data, &signer).unwrap();
259        let public_key = signed.signature().signer.clone();
260
261        // Verify the public key matches
262        let expected_pubkey = PublicKey::new(
263            crate::keys::DSAlgorithm::Ed25519,
264            signer.public_key_bytes(),
265        )
266        .unwrap();
267
268        assert_eq!(public_key, expected_pubkey);
269    }
270
271    #[test]
272    fn test_timestamp_is_set() {
273        let signer = Ed25519Signer::generate().unwrap();
274        let data = TestData {
275            value: "test".to_string(),
276            number: 1,
277        };
278
279        let before = TimeStamp::now();
280        let signed = Signed::new(data, &signer).unwrap();
281        let after = TimeStamp::now();
282
283        let ts = signed.signature().timestamp;
284        assert!(ts >= before);
285        assert!(ts <= after);
286    }
287
288    #[test]
289    fn test_signature_serialization() {
290        let signer = Ed25519Signer::generate().unwrap();
291        let data = TestData {
292            value: "serialize me".to_string(),
293            number: 456,
294        };
295
296        let signature = Signature::new(&data, &signer).unwrap();
297
298        // Serialize to JSON
299        let json = serde_json::to_string(&signature).unwrap();
300
301        // Deserialize back
302        let deserialized: Signature = serde_json::from_str(&json).unwrap();
303
304        // Should still verify
305        assert!(deserialized.verify(&data).is_ok());
306    }
307
308    #[test]
309    fn test_signed_serialization() {
310        let signer = Ed25519Signer::generate().unwrap();
311        let data = TestData {
312            value: "roundtrip".to_string(),
313            number: 789,
314        };
315
316        let signed = Signed::new(data, &signer).unwrap();
317
318        // Serialize to JSON
319        let json = serde_json::to_string(&signed).unwrap();
320
321        // Deserialize back
322        let deserialized: Signed<TestData> =
323            serde_json::from_str(&json).unwrap();
324
325        // Should still verify
326        assert!(deserialized.verify().is_ok());
327        assert_eq!(deserialized.content().value, "roundtrip");
328    }
329}