hive_btle/security/
registry.rs

1// Copyright (c) 2025-2026 (r)evolve - Revolve Team LLC
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Identity Registry - Trust On First Use (TOFU) identity tracking
17//!
18//! Tracks the binding between node IDs and their public keys. On first contact,
19//! the public key is recorded. On subsequent contacts, if the public key differs,
20//! the identity is rejected as a potential impersonation attempt.
21//!
22//! # Example
23//!
24//! ```
25//! use hive_btle::security::{DeviceIdentity, IdentityRegistry, RegistryResult};
26//!
27//! let mut registry = IdentityRegistry::new();
28//!
29//! // First contact - identity is registered
30//! let alice = DeviceIdentity::generate();
31//! let attestation = alice.create_attestation(0);
32//! assert!(matches!(
33//!     registry.verify_or_register(&attestation),
34//!     RegistryResult::Registered
35//! ));
36//!
37//! // Same identity - verification succeeds
38//! assert!(matches!(
39//!     registry.verify_or_register(&attestation),
40//!     RegistryResult::Verified
41//! ));
42//!
43//! // Different key claiming same node_id - rejected!
44//! // (This would require crafting a fake attestation, which would fail signature check first)
45//! ```
46
47#[cfg(not(feature = "std"))]
48use alloc::vec::Vec;
49use hashbrown::HashMap;
50
51use super::identity::IdentityAttestation;
52use crate::NodeId;
53
54/// Result of identity verification
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum RegistryResult {
57    /// Identity was newly registered (first contact)
58    Registered,
59
60    /// Identity was verified against existing record
61    Verified,
62
63    /// Signature verification failed
64    InvalidSignature,
65
66    /// Public key doesn't match previously registered key (impersonation attempt!)
67    KeyMismatch {
68        /// The node_id that was claimed
69        node_id: NodeId,
70    },
71}
72
73impl RegistryResult {
74    /// Returns true if the identity is trusted (registered or verified)
75    pub fn is_trusted(&self) -> bool {
76        matches!(self, Self::Registered | Self::Verified)
77    }
78
79    /// Returns true if this is a security violation
80    pub fn is_violation(&self) -> bool {
81        matches!(self, Self::InvalidSignature | Self::KeyMismatch { .. })
82    }
83}
84
85/// Record of a known identity
86#[derive(Debug, Clone)]
87pub struct IdentityRecord {
88    /// The public key for this node
89    pub public_key: [u8; 32],
90
91    /// When this identity was first seen (milliseconds since epoch)
92    pub first_seen_ms: u64,
93
94    /// When this identity was last verified (milliseconds since epoch)
95    pub last_seen_ms: u64,
96
97    /// Number of successful verifications
98    pub verification_count: u32,
99}
100
101/// TOFU Identity Registry
102///
103/// Maintains a mapping of node IDs to their public keys, implementing
104/// Trust On First Use semantics.
105#[derive(Debug, Clone)]
106pub struct IdentityRegistry {
107    /// Known identities: node_id → identity record
108    known: HashMap<NodeId, IdentityRecord>,
109
110    /// Maximum number of identities to track (prevents memory exhaustion)
111    max_identities: usize,
112}
113
114impl Default for IdentityRegistry {
115    fn default() -> Self {
116        Self::new()
117    }
118}
119
120impl IdentityRegistry {
121    /// Default maximum identities (suitable for most deployments)
122    pub const DEFAULT_MAX_IDENTITIES: usize = 256;
123
124    /// Create a new empty registry
125    pub fn new() -> Self {
126        Self {
127            known: HashMap::new(),
128            max_identities: Self::DEFAULT_MAX_IDENTITIES,
129        }
130    }
131
132    /// Create a registry with custom capacity limit
133    pub fn with_capacity(max_identities: usize) -> Self {
134        Self {
135            known: HashMap::with_capacity(max_identities.min(64)),
136            max_identities,
137        }
138    }
139
140    /// Verify an identity attestation or register it if new
141    ///
142    /// This is the main TOFU operation:
143    /// 1. Verify the attestation signature
144    /// 2. If node_id is new, register the public key
145    /// 3. If node_id is known, verify the public key matches
146    pub fn verify_or_register(&mut self, attestation: &IdentityAttestation) -> RegistryResult {
147        self.verify_or_register_at(attestation, attestation.timestamp_ms)
148    }
149
150    /// Verify or register with explicit timestamp (for testing)
151    pub fn verify_or_register_at(
152        &mut self,
153        attestation: &IdentityAttestation,
154        now_ms: u64,
155    ) -> RegistryResult {
156        // First, verify the cryptographic signature
157        if !attestation.verify() {
158            return RegistryResult::InvalidSignature;
159        }
160
161        let node_id = attestation.node_id;
162
163        // Check if we already know this node
164        if let Some(record) = self.known.get_mut(&node_id) {
165            // Known node - verify public key matches
166            if record.public_key == attestation.public_key {
167                // Same key - update last seen and count
168                record.last_seen_ms = now_ms;
169                record.verification_count = record.verification_count.saturating_add(1);
170                RegistryResult::Verified
171            } else {
172                // Different key! Potential impersonation
173                RegistryResult::KeyMismatch { node_id }
174            }
175        } else {
176            // New node - register if we have capacity
177            if self.known.len() >= self.max_identities {
178                // At capacity - could implement LRU eviction here
179                // For now, still register (HashMap will handle it)
180                // In production, might want to evict oldest or implement proper LRU
181            }
182
183            self.known.insert(
184                node_id,
185                IdentityRecord {
186                    public_key: attestation.public_key,
187                    first_seen_ms: now_ms,
188                    last_seen_ms: now_ms,
189                    verification_count: 1,
190                },
191            );
192            RegistryResult::Registered
193        }
194    }
195
196    /// Check if a node_id is known without modifying the registry
197    pub fn is_known(&self, node_id: NodeId) -> bool {
198        self.known.contains_key(&node_id)
199    }
200
201    /// Get the public key for a known node
202    pub fn get_public_key(&self, node_id: NodeId) -> Option<&[u8; 32]> {
203        self.known.get(&node_id).map(|r| &r.public_key)
204    }
205
206    /// Get the full identity record for a node
207    pub fn get_record(&self, node_id: NodeId) -> Option<&IdentityRecord> {
208        self.known.get(&node_id)
209    }
210
211    /// Get the number of known identities
212    pub fn len(&self) -> usize {
213        self.known.len()
214    }
215
216    /// Check if the registry is empty
217    pub fn is_empty(&self) -> bool {
218        self.known.is_empty()
219    }
220
221    /// Remove an identity from the registry
222    ///
223    /// Use with caution - this allows re-registration with a different key.
224    pub fn remove(&mut self, node_id: NodeId) -> Option<IdentityRecord> {
225        self.known.remove(&node_id)
226    }
227
228    /// Clear all known identities
229    ///
230    /// Use with extreme caution - this resets all TOFU trust.
231    pub fn clear(&mut self) {
232        self.known.clear();
233    }
234
235    /// Get all known node IDs
236    pub fn known_nodes(&self) -> Vec<NodeId> {
237        self.known.keys().copied().collect()
238    }
239
240    /// Pre-register a known identity (for out-of-band key exchange)
241    ///
242    /// This allows registering an identity without an attestation,
243    /// useful when keys are exchanged through a secure side channel.
244    pub fn pre_register(&mut self, node_id: NodeId, public_key: [u8; 32], now_ms: u64) {
245        self.known.insert(
246            node_id,
247            IdentityRecord {
248                public_key,
249                first_seen_ms: now_ms,
250                last_seen_ms: now_ms,
251                verification_count: 0,
252            },
253        );
254    }
255
256    /// Encode registry for persistence
257    ///
258    /// Format per entry:
259    /// - node_id (4 bytes)
260    /// - public_key (32 bytes)
261    /// - first_seen_ms (8 bytes)
262    /// - last_seen_ms (8 bytes)
263    /// - verification_count (4 bytes)
264    ///
265    /// Total: 56 bytes per entry
266    pub fn encode(&self) -> Vec<u8> {
267        let mut buf = Vec::with_capacity(4 + self.known.len() * 56);
268
269        // Number of entries
270        buf.extend_from_slice(&(self.known.len() as u32).to_le_bytes());
271
272        for (node_id, record) in &self.known {
273            buf.extend_from_slice(&node_id.as_u32().to_le_bytes());
274            buf.extend_from_slice(&record.public_key);
275            buf.extend_from_slice(&record.first_seen_ms.to_le_bytes());
276            buf.extend_from_slice(&record.last_seen_ms.to_le_bytes());
277            buf.extend_from_slice(&record.verification_count.to_le_bytes());
278        }
279
280        buf
281    }
282
283    /// Decode registry from bytes
284    pub fn decode(data: &[u8]) -> Option<Self> {
285        if data.len() < 4 {
286            return None;
287        }
288
289        let count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
290
291        if data.len() < 4 + count * 56 {
292            return None;
293        }
294
295        let mut registry = Self::new();
296        let mut offset = 4;
297
298        for _ in 0..count {
299            let node_id = NodeId::new(u32::from_le_bytes([
300                data[offset],
301                data[offset + 1],
302                data[offset + 2],
303                data[offset + 3],
304            ]));
305            offset += 4;
306
307            let mut public_key = [0u8; 32];
308            public_key.copy_from_slice(&data[offset..offset + 32]);
309            offset += 32;
310
311            let first_seen_ms = u64::from_le_bytes([
312                data[offset],
313                data[offset + 1],
314                data[offset + 2],
315                data[offset + 3],
316                data[offset + 4],
317                data[offset + 5],
318                data[offset + 6],
319                data[offset + 7],
320            ]);
321            offset += 8;
322
323            let last_seen_ms = u64::from_le_bytes([
324                data[offset],
325                data[offset + 1],
326                data[offset + 2],
327                data[offset + 3],
328                data[offset + 4],
329                data[offset + 5],
330                data[offset + 6],
331                data[offset + 7],
332            ]);
333            offset += 8;
334
335            let verification_count = u32::from_le_bytes([
336                data[offset],
337                data[offset + 1],
338                data[offset + 2],
339                data[offset + 3],
340            ]);
341            offset += 4;
342
343            registry.known.insert(
344                node_id,
345                IdentityRecord {
346                    public_key,
347                    first_seen_ms,
348                    last_seen_ms,
349                    verification_count,
350                },
351            );
352        }
353
354        Some(registry)
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361    use crate::security::DeviceIdentity;
362
363    #[test]
364    fn test_register_new_identity() {
365        let mut registry = IdentityRegistry::new();
366        let identity = DeviceIdentity::generate();
367        let attestation = identity.create_attestation(0);
368
369        let result = registry.verify_or_register(&attestation);
370        assert_eq!(result, RegistryResult::Registered);
371        assert!(result.is_trusted());
372        assert!(!result.is_violation());
373        assert_eq!(registry.len(), 1);
374    }
375
376    #[test]
377    fn test_verify_known_identity() {
378        let mut registry = IdentityRegistry::new();
379        let identity = DeviceIdentity::generate();
380        let attestation = identity.create_attestation(0);
381
382        // First registration
383        registry.verify_or_register(&attestation);
384
385        // Second verification
386        let result = registry.verify_or_register(&attestation);
387        assert_eq!(result, RegistryResult::Verified);
388        assert!(result.is_trusted());
389    }
390
391    #[test]
392    fn test_key_mismatch_detection() {
393        let mut registry = IdentityRegistry::new();
394
395        // Register first identity
396        let identity1 = DeviceIdentity::generate();
397        let attestation1 = identity1.create_attestation(0);
398        registry.verify_or_register(&attestation1);
399
400        // Try to register different identity with same node_id
401        // (In reality, this would fail signature verification because
402        // the attacker can't sign for a node_id derived from a different key)
403        // But we can test the key mismatch path by pre-registering
404
405        let identity2 = DeviceIdentity::generate();
406        let node_id = identity1.node_id();
407
408        // Manually create a conflicting record
409        registry.known.insert(
410            node_id,
411            IdentityRecord {
412                public_key: [0xAA; 32], // Different key
413                first_seen_ms: 0,
414                last_seen_ms: 0,
415                verification_count: 1,
416            },
417        );
418
419        // Now verification should detect mismatch
420        let result = registry.verify_or_register(&attestation1);
421        assert!(matches!(result, RegistryResult::KeyMismatch { .. }));
422        assert!(result.is_violation());
423    }
424
425    #[test]
426    fn test_invalid_signature_detection() {
427        let mut registry = IdentityRegistry::new();
428
429        // Create a tampered attestation
430        let identity = DeviceIdentity::generate();
431        let mut attestation = identity.create_attestation(0);
432        attestation.signature[0] ^= 0xFF; // Corrupt signature
433
434        let result = registry.verify_or_register(&attestation);
435        assert_eq!(result, RegistryResult::InvalidSignature);
436        assert!(result.is_violation());
437    }
438
439    #[test]
440    fn test_verification_count_increment() {
441        let mut registry = IdentityRegistry::new();
442        let identity = DeviceIdentity::generate();
443        let attestation = identity.create_attestation(0);
444        let node_id = identity.node_id();
445
446        // Multiple verifications
447        registry.verify_or_register(&attestation);
448        registry.verify_or_register(&attestation);
449        registry.verify_or_register(&attestation);
450
451        let record = registry.get_record(node_id).unwrap();
452        assert_eq!(record.verification_count, 3);
453    }
454
455    #[test]
456    fn test_pre_register() {
457        let mut registry = IdentityRegistry::new();
458        let identity = DeviceIdentity::generate();
459        let node_id = identity.node_id();
460        let public_key = identity.public_key();
461
462        // Pre-register without attestation
463        registry.pre_register(node_id, public_key, 1000);
464
465        assert!(registry.is_known(node_id));
466        assert_eq!(registry.get_public_key(node_id), Some(&public_key));
467
468        // Now attestation should verify
469        let attestation = identity.create_attestation(0);
470        let result = registry.verify_or_register(&attestation);
471        assert_eq!(result, RegistryResult::Verified);
472    }
473
474    #[test]
475    fn test_encode_decode_roundtrip() {
476        let mut registry = IdentityRegistry::new();
477
478        // Register a few identities
479        for _ in 0..5 {
480            let identity = DeviceIdentity::generate();
481            let attestation = identity.create_attestation(0);
482            registry.verify_or_register(&attestation);
483        }
484
485        let encoded = registry.encode();
486        let decoded = IdentityRegistry::decode(&encoded).unwrap();
487
488        assert_eq!(decoded.len(), registry.len());
489        for node_id in registry.known_nodes() {
490            assert!(decoded.is_known(node_id));
491            assert_eq!(
492                decoded.get_public_key(node_id),
493                registry.get_public_key(node_id)
494            );
495        }
496    }
497
498    #[test]
499    fn test_remove_identity() {
500        let mut registry = IdentityRegistry::new();
501        let identity = DeviceIdentity::generate();
502        let attestation = identity.create_attestation(0);
503        let node_id = identity.node_id();
504
505        registry.verify_or_register(&attestation);
506        assert!(registry.is_known(node_id));
507
508        registry.remove(node_id);
509        assert!(!registry.is_known(node_id));
510
511        // Can re-register after removal
512        let result = registry.verify_or_register(&attestation);
513        assert_eq!(result, RegistryResult::Registered);
514    }
515
516    #[test]
517    fn test_known_nodes() {
518        let mut registry = IdentityRegistry::new();
519        let mut expected_nodes = Vec::new();
520
521        for _ in 0..3 {
522            let identity = DeviceIdentity::generate();
523            let attestation = identity.create_attestation(0);
524            expected_nodes.push(identity.node_id());
525            registry.verify_or_register(&attestation);
526        }
527
528        let known = registry.known_nodes();
529        assert_eq!(known.len(), 3);
530        for node_id in expected_nodes {
531            assert!(known.contains(&node_id));
532        }
533    }
534}