shield_core/
signatures.rs1use ring::hmac;
6use ring::rand::{SecureRandom, SystemRandom};
7use std::num::NonZeroU32;
8use std::time::{SystemTime, UNIX_EPOCH};
9use subtle::ConstantTimeEq;
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12use crate::error::{Result, ShieldError};
13
14#[derive(Zeroize, ZeroizeOnDrop)]
18pub struct SymmetricSignature {
19 signing_key: [u8; 32],
20 verification_key: [u8; 32],
21}
22
23impl SymmetricSignature {
24 #[must_use]
26 pub fn new(signing_key: [u8; 32]) -> Self {
27 let verification_key = {
28 let mut data = Vec::with_capacity(7 + 32);
29 data.extend_from_slice(b"verify:");
30 data.extend_from_slice(&signing_key);
31 let hash = ring::digest::digest(&ring::digest::SHA256, &data);
32 let mut key = [0u8; 32];
33 key.copy_from_slice(hash.as_ref());
34 key
35 };
36
37 Self {
38 signing_key,
39 verification_key,
40 }
41 }
42
43 pub fn generate() -> Result<Self> {
45 let rng = SystemRandom::new();
46 let mut key = [0u8; 32];
47 rng.fill(&mut key).map_err(|_| ShieldError::RandomFailed)?;
48 Ok(Self::new(key))
49 }
50
51 #[must_use]
53 pub fn from_password(password: &str, identity: &str) -> Self {
54 let salt_data = format!("sign:{identity}");
55 let salt = ring::digest::digest(&ring::digest::SHA256, salt_data.as_bytes());
56
57 let mut key = [0u8; 32];
58 ring::pbkdf2::derive(
59 ring::pbkdf2::PBKDF2_HMAC_SHA256,
60 NonZeroU32::new(100_000).unwrap(),
61 salt.as_ref(),
62 password.as_bytes(),
63 &mut key,
64 );
65
66 Self::new(key)
67 }
68
69 #[must_use]
71 pub fn sign(&self, message: &[u8], include_timestamp: bool) -> Vec<u8> {
72 if include_timestamp {
73 let timestamp = SystemTime::now()
74 .duration_since(UNIX_EPOCH)
75 .unwrap()
76 .as_secs();
77
78 let mut sig_data = Vec::with_capacity(8 + message.len());
79 sig_data.extend_from_slice(×tamp.to_le_bytes());
80 sig_data.extend_from_slice(message);
81
82 let key = hmac::Key::new(hmac::HMAC_SHA256, &self.signing_key);
83 let tag = hmac::sign(&key, &sig_data);
84
85 let mut result = Vec::with_capacity(8 + 32);
86 result.extend_from_slice(×tamp.to_le_bytes());
87 result.extend_from_slice(tag.as_ref());
88 result
89 } else {
90 let key = hmac::Key::new(hmac::HMAC_SHA256, &self.signing_key);
91 let tag = hmac::sign(&key, message);
92 tag.as_ref().to_vec()
93 }
94 }
95
96 #[must_use]
98 pub fn verify(
99 &self,
100 message: &[u8],
101 signature: &[u8],
102 verification_key: &[u8; 32],
103 max_age: u64,
104 ) -> bool {
105 if verification_key.ct_eq(&self.verification_key).unwrap_u8() != 1 {
106 return false;
107 }
108
109 if signature.len() == 40 {
110 let timestamp = u64::from_le_bytes(signature[..8].try_into().unwrap());
112 let sig = &signature[8..];
113
114 if max_age > 0 {
115 let now = SystemTime::now()
116 .duration_since(UNIX_EPOCH)
117 .unwrap()
118 .as_secs();
119 if now.abs_diff(timestamp) > max_age {
120 return false;
121 }
122 }
123
124 let mut sig_data = Vec::with_capacity(8 + message.len());
125 sig_data.extend_from_slice(×tamp.to_le_bytes());
126 sig_data.extend_from_slice(message);
127
128 let key = hmac::Key::new(hmac::HMAC_SHA256, &self.signing_key);
129 let expected = hmac::sign(&key, &sig_data);
130 sig.ct_eq(expected.as_ref()).unwrap_u8() == 1
131 } else if signature.len() == 32 {
132 let key = hmac::Key::new(hmac::HMAC_SHA256, &self.signing_key);
133 let expected = hmac::sign(&key, message);
134 signature.ct_eq(expected.as_ref()).unwrap_u8() == 1
135 } else {
136 false
137 }
138 }
139
140 #[must_use]
142 pub fn verification_key(&self) -> &[u8; 32] {
143 &self.verification_key
144 }
145
146 #[must_use]
148 pub fn fingerprint(&self) -> String {
149 let hash = ring::digest::digest(&ring::digest::SHA256, &self.verification_key);
150 hex::encode(&hash.as_ref()[..8])
151 }
152}
153
154#[derive(Zeroize, ZeroizeOnDrop)]
158pub struct LamportSignature {
159 private_key: Vec<([u8; 32], [u8; 32])>,
160 #[zeroize(skip)]
161 public_key: Vec<u8>,
162 #[zeroize(skip)]
163 used: bool,
164}
165
166impl LamportSignature {
167 const BITS: usize = 256;
168
169 pub fn generate() -> Result<Self> {
171 let rng = SystemRandom::new();
172 let mut private_key = Vec::with_capacity(Self::BITS);
173 let mut public_key = Vec::with_capacity(Self::BITS * 64);
174
175 for _ in 0..Self::BITS {
176 let mut key0 = [0u8; 32];
177 let mut key1 = [0u8; 32];
178 rng.fill(&mut key0).map_err(|_| ShieldError::RandomFailed)?;
179 rng.fill(&mut key1).map_err(|_| ShieldError::RandomFailed)?;
180
181 let hash0 = ring::digest::digest(&ring::digest::SHA256, &key0);
182 let hash1 = ring::digest::digest(&ring::digest::SHA256, &key1);
183
184 public_key.extend_from_slice(hash0.as_ref());
185 public_key.extend_from_slice(hash1.as_ref());
186 private_key.push((key0, key1));
187 }
188
189 Ok(Self {
190 private_key,
191 public_key,
192 used: false,
193 })
194 }
195
196 pub fn sign(&mut self, message: &[u8]) -> Result<Vec<u8>> {
198 if self.used {
199 return Err(ShieldError::LamportKeyUsed);
200 }
201 self.used = true;
202
203 let msg_hash = ring::digest::digest(&ring::digest::SHA256, message);
204 let hash_bytes = msg_hash.as_ref();
205 let mut signature = Vec::with_capacity(Self::BITS * 32);
206
207 for i in 0..Self::BITS {
208 let byte_idx = i / 8;
209 let bit_idx = i % 8;
210 let bit = (hash_bytes[byte_idx] >> bit_idx) & 1;
211
212 let (key0, key1) = &self.private_key[i];
213 if bit == 1 {
214 signature.extend_from_slice(key1);
215 } else {
216 signature.extend_from_slice(key0);
217 }
218 }
219
220 Ok(signature)
221 }
222
223 #[must_use]
225 pub fn verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
226 if signature.len() != 256 * 32 || public_key.len() != 256 * 64 {
227 return false;
228 }
229
230 let msg_hash = ring::digest::digest(&ring::digest::SHA256, message);
231 let hash_bytes = msg_hash.as_ref();
232
233 for i in 0..256 {
234 let byte_idx = i / 8;
235 let bit_idx = i % 8;
236 let bit = (hash_bytes[byte_idx] >> bit_idx) & 1;
237
238 let revealed = &signature[i * 32..(i + 1) * 32];
239 let hashed = ring::digest::digest(&ring::digest::SHA256, revealed);
240
241 let expected = if bit == 1 {
242 &public_key[i * 64 + 32..i * 64 + 64]
243 } else {
244 &public_key[i * 64..i * 64 + 32]
245 };
246
247 if hashed.as_ref().ct_eq(expected).unwrap_u8() != 1 {
248 return false;
249 }
250 }
251
252 true
253 }
254
255 #[must_use]
257 pub fn is_used(&self) -> bool {
258 self.used
259 }
260
261 #[must_use]
263 pub fn public_key(&self) -> &[u8] {
264 &self.public_key
265 }
266
267 #[must_use]
269 pub fn fingerprint(&self) -> String {
270 let hash = ring::digest::digest(&ring::digest::SHA256, &self.public_key);
271 hex::encode(&hash.as_ref()[..8])
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_symmetric_sign_verify() {
281 let signer = SymmetricSignature::generate().unwrap();
282 let message = b"Hello, World!";
283 let signature = signer.sign(message, true);
284 assert!(signer.verify(message, &signature, signer.verification_key(), 300));
285 }
286
287 #[test]
288 fn test_symmetric_wrong_key() {
289 let signer1 = SymmetricSignature::generate().unwrap();
290 let signer2 = SymmetricSignature::generate().unwrap();
291 let message = b"test";
292 let signature = signer1.sign(message, true);
293 assert!(!signer2.verify(message, &signature, signer2.verification_key(), 300));
294 }
295
296 #[test]
297 fn test_symmetric_from_password() {
298 let signer1 = SymmetricSignature::from_password("password", "user@example.com");
299 let signer2 = SymmetricSignature::from_password("password", "user@example.com");
300 assert_eq!(signer1.verification_key(), signer2.verification_key());
301 }
302
303 #[test]
304 fn test_lamport_sign_verify() {
305 let mut lamport = LamportSignature::generate().unwrap();
306 let message = b"Test message";
307 let signature = lamport.sign(message).unwrap();
308 assert!(LamportSignature::verify(message, &signature, lamport.public_key()));
309 }
310
311 #[test]
312 fn test_lamport_one_time() {
313 let mut lamport = LamportSignature::generate().unwrap();
314 lamport.sign(b"first").unwrap();
315 assert!(lamport.sign(b"second").is_err());
316 }
317
318 #[test]
319 fn test_lamport_is_used() {
320 let mut lamport = LamportSignature::generate().unwrap();
321 assert!(!lamport.is_used());
322 lamport.sign(b"message").unwrap();
323 assert!(lamport.is_used());
324 }
325}