1use crate::NQuin;
7use std::collections::HashMap;
8
9pub type WebizenId = u64;
11
12pub const TAG_WEBIZEN: u64 = 0x8;
14
15pub const WEBIZEN_ID_MIN: u64 = 0x8000_0000_0000_0000;
17pub const WEBIZEN_ID_MAX: u64 = 0x8FFF_FFFF_FFFF_FFFF;
18
19#[repr(C)]
21#[derive(Debug, Clone)]
22pub struct WebizenIdentity {
23 pub webizen_id: WebizenId,
25 pub webid_hash: u64,
27 pub public_key: [u64; 4],
29 pub did_hash: u64,
31 pub created_at: u64,
33 pub updated_at: u64,
35}
36
37impl WebizenIdentity {
38 pub fn new(webid_hash: u64, public_key: [u64; 4]) -> Self {
40 let webizen_id = (TAG_WEBIZEN << 60) | (webid_hash & 0x0FFF_FFFF_FFFF_FFFF);
41 Self {
42 webizen_id,
43 webid_hash,
44 public_key,
45 did_hash: 0,
46 created_at: 0,
47 updated_at: 0,
48 }
49 }
50
51 pub fn with_did(mut self, did_hash: u64) -> Self {
53 self.did_hash = did_hash;
54 self
55 }
56
57 pub fn is_webizen_id(id: u64) -> bool {
59 (id & 0xF000_0000_0000_0000) == (TAG_WEBIZEN << 60)
60 }
61}
62
63pub struct WebizenRegistry {
65 webizen_slots: HashMap<WebizenId, WebizenIdentity>,
67 webid_to_webizen: HashMap<u64, WebizenId>,
69 signature_cache: HashMap<(WebizenId, Vec<u8>), bool>,
71}
72
73impl WebizenRegistry {
74 pub fn new() -> Self {
76 Self {
77 webizen_slots: HashMap::new(),
78 webid_to_webizen: HashMap::new(),
79 signature_cache: HashMap::new(),
80 }
81 }
82
83 pub fn register_webizen(&mut self, identity: WebizenIdentity) -> Result<(), String> {
85 let webizen_id = identity.webizen_id;
86
87 if !WebizenIdentity::is_webizen_id(webizen_id) {
89 return Err("ID not in Webizen range".to_string());
90 }
91
92 self.webizen_slots.insert(webizen_id, identity.clone());
94 self.webid_to_webizen
95 .insert(identity.webid_hash, webizen_id);
96
97 Ok(())
98 }
99
100 pub fn get_webizen(&self, webizen_id: WebizenId) -> Option<&WebizenIdentity> {
102 self.webizen_slots.get(&webizen_id)
103 }
104
105 pub fn get_webizen_by_webid(&self, webid_hash: u64) -> Option<WebizenId> {
107 self.webid_to_webizen.get(&webid_hash).copied()
108 }
109
110 pub fn verify_signature(
114 &mut self,
115 webizen_id: WebizenId,
116 message: &[u8],
117 signature: &[u8],
118 ) -> Result<bool, String> {
119 let identity = self
120 .webizen_slots
121 .get(&webizen_id)
122 .ok_or_else(|| "Webizen not found".to_string())?;
123
124 let mut cache_input = message.to_vec();
127 cache_input.extend_from_slice(signature);
128 let cache_key = (webizen_id, cache_input);
129 if let Some(&cached) = self.signature_cache.get(&cache_key) {
130 return Ok(cached);
131 }
132
133 let mut key_bytes = [0u8; 32];
135 for (i, &chunk) in identity.public_key.iter().enumerate() {
136 key_bytes[i * 8..(i + 1) * 8].copy_from_slice(&chunk.to_le_bytes());
137 }
138
139 use ed25519_dalek::{Signature, VerifyingKey};
141 let vk =
142 VerifyingKey::from_bytes(&key_bytes).map_err(|e| format!("Invalid public key: {e}"))?;
143
144 let sig_arr: [u8; 64] = signature
146 .try_into()
147 .map_err(|_| format!("Signature must be 64 bytes, got {}", signature.len()))?;
148 let sig = Signature::from_bytes(&sig_arr);
149
150 let verified = vk.verify_strict(message, &sig).is_ok();
151
152 self.signature_cache.insert(cache_key, verified);
154
155 Ok(verified)
156 }
157
158 pub fn verify_quin_signature(
160 &mut self,
161 quin: &NQuin,
162 signature: &[u8],
163 ) -> Result<bool, String> {
164 if !WebizenIdentity::is_webizen_id(quin.subject) {
166 return Err("Subject is not a Webizen".to_string());
167 }
168
169 let message = self.serialize_quin_for_signature(quin);
171
172 self.verify_signature(quin.subject, &message, signature)
173 }
174
175 fn serialize_quin_for_signature(&self, quin: &NQuin) -> Vec<u8> {
177 let mut bytes = Vec::with_capacity(40);
180 bytes.extend_from_slice(&quin.subject.to_le_bytes());
181 bytes.extend_from_slice(&quin.predicate.to_le_bytes());
182 bytes.extend_from_slice(&quin.object.to_le_bytes());
183 bytes.extend_from_slice(&quin.context.to_le_bytes());
184 bytes.extend_from_slice(&quin.metadata.to_le_bytes());
185 bytes
186 }
187}
188
189impl Default for WebizenRegistry {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195#[repr(C)]
198#[derive(Debug, Clone)]
199pub struct WebizenSignatureStorage {
200 pub block_id: u64,
202 pub signature_count: u32,
204 pub reserved: u32,
206}
207
208impl WebizenSignatureStorage {
209 pub fn signature_to_quins(
211 webizen_id: WebizenId,
212 quin_hash: u64,
213 signature: &[u8; 64],
214 ) -> Vec<NQuin> {
215 const WEBIZEN_METADATA_FLAG: u64 = 0x1 << 63; let sig_parts: [u64; 8] = unsafe { std::mem::transmute(*signature) };
219
220 let mut quins = Vec::with_capacity(8);
222 for (i, part) in sig_parts.iter().enumerate() {
223 quins.push(NQuin {
224 subject: webizen_id,
225 predicate: quin_hash,
226 object: *part,
227 context: i as u64, metadata: WEBIZEN_METADATA_FLAG,
229 parity: 0,
230 });
231 }
232
233 quins
234 }
235
236 pub fn quins_to_signature(quins: &[NQuin]) -> Option<[u8; 64]> {
238 const WEBIZEN_METADATA_FLAG: u64 = 0x1 << 63;
239
240 let sig_quins: Vec<_> = quins
242 .iter()
243 .filter(|quin| quin.metadata & WEBIZEN_METADATA_FLAG != 0)
244 .collect();
245
246 if sig_quins.len() != 8 {
247 return None;
248 }
249
250 let mut sorted_quins = sig_quins.clone();
252 sorted_quins.sort_by_key(|q| q.context);
253
254 let mut sig_parts: [u64; 8] = [0; 8];
256 for (i, quin) in sorted_quins.iter().enumerate() {
257 sig_parts[i] = quin.object;
258 }
259
260 Some(unsafe { std::mem::transmute(sig_parts) })
261 }
262}
263
264#[cfg(test)]
265mod webizen_tests {
266 use super::*;
267
268 #[test]
269 fn test_webizen_id_range() {
270 assert!(WebizenIdentity::is_webizen_id(WEBIZEN_ID_MIN));
271 assert!(WebizenIdentity::is_webizen_id(WEBIZEN_ID_MAX));
272 assert!(!WebizenIdentity::is_webizen_id(0x123456789ABCDEF0));
273 }
274
275 #[test]
276 fn test_webizen_identity_creation() {
277 let public_key = [1u64, 2, 3, 4];
278 let identity = WebizenIdentity::new(0x123456789ABCDEF0, public_key);
279
280 assert!(WebizenIdentity::is_webizen_id(identity.webizen_id));
281 assert_eq!(identity.webid_hash, 0x123456789ABCDEF0);
282 }
283
284 #[test]
285 fn test_registry_registration() {
286 let mut registry = WebizenRegistry::new();
287 let public_key = [1u64, 2, 3, 4];
288 let identity = WebizenIdentity::new(0x123456789ABCDEF0, public_key);
289 let id = identity.webizen_id;
290
291 assert!(registry.register_webizen(identity).is_ok());
292 assert!(registry.get_webizen(id).is_some());
293 }
294
295 #[test]
296 fn test_registry_webid_lookup() {
297 let mut registry = WebizenRegistry::new();
298 let public_key = [1u64, 2, 3, 4];
299 let identity = WebizenIdentity::new(0x123456789ABCDEF0, public_key);
300 let webizen_id = identity.webizen_id;
301
302 registry.register_webizen(identity).unwrap();
303 assert_eq!(
304 registry.get_webizen_by_webid(0x123456789ABCDEF0),
305 Some(webizen_id)
306 );
307 }
308
309 #[test]
310 fn test_signature_storage() {
311 let webizen_id = TAG_WEBIZEN | 0x123456789ABCDEF0;
312 let quin_hash = 0x9876543210FEDCBA;
313 let signature = [1u8; 64];
314
315 let quins = WebizenSignatureStorage::signature_to_quins(webizen_id, quin_hash, &signature);
316 assert_eq!(quins.len(), 8);
317
318 let signature_back = WebizenSignatureStorage::quins_to_signature(&quins);
319 assert!(signature_back.is_some());
320 assert_eq!(signature_back.unwrap(), signature);
321 }
322
323 #[test]
324 fn test_verify_signature_roundtrip() {
325 use ed25519_dalek::{Signer, SigningKey};
326
327 let mut registry = WebizenRegistry::new();
328
329 let signing_key = SigningKey::from_bytes(&[7u8; 32]);
331 let vk_bytes = signing_key.verifying_key().to_bytes();
332
333 let mut public_key = [0u64; 4];
336 for (i, slot) in public_key.iter_mut().enumerate() {
337 let mut chunk = [0u8; 8];
338 chunk.copy_from_slice(&vk_bytes[i * 8..(i + 1) * 8]);
339 *slot = u64::from_le_bytes(chunk);
340 }
341
342 let identity = WebizenIdentity::new(0x123456789ABCDEF0, public_key);
343 let id = identity.webizen_id;
344 registry.register_webizen(identity).unwrap();
345
346 let message = b"test message";
347 let good_sig = signing_key.sign(message).to_bytes();
348
349 assert_eq!(registry.verify_signature(id, message, &good_sig), Ok(true));
351
352 let other_sig = signing_key.sign(b"other message").to_bytes();
354 assert_eq!(
355 registry.verify_signature(id, message, &other_sig),
356 Ok(false)
357 );
358
359 assert!(registry
361 .verify_signature(id, message, b"too short")
362 .is_err());
363 }
364}