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