saorsa_core/adaptive/
identity.rs

1// Copyright 2024 Saorsa Labs Limited
2//
3// This software is dual-licensed under:
4// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
5// - Commercial License
6//
7// For AGPL-3.0 license, see LICENSE-AGPL-3.0
8// For commercial licensing, contact: saorsalabs@gmail.com
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under these licenses is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14//! Cryptographic identity system for the adaptive P2P network
15//!
16//! Implements Ed25519-based identity. Sybil-resistance is handled by
17//! higher-level mechanisms.
18
19use super::*;
20use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
21use serde::{Deserialize, Serialize};
22use sha2::{Digest, Sha256};
23use std::time::{SystemTime, UNIX_EPOCH};
24
25/// Node identity with cryptographic keys
26#[derive(Clone)]
27pub struct NodeIdentity {
28    /// Ed25519 signing key
29    signing_key: SigningKey,
30    /// Node ID derived from public key
31    node_id: NodeId,
32}
33
34/// Signed message structure
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct SignedMessage<T: Serialize> {
37    /// Message payload
38    pub payload: T,
39    /// Sender's node ID
40    pub sender_id: NodeId,
41    /// Unix timestamp
42    pub timestamp: u64,
43    /// Ed25519 signature
44    pub signature: Vec<u8>,
45}
46
47impl NodeIdentity {
48    /// Generate a new node identity
49    pub fn generate() -> Result<Self> {
50        let mut csprng = rand::thread_rng();
51        let signing_key = SigningKey::generate(&mut csprng);
52
53        let node_id = Self::compute_node_id(&signing_key.verifying_key());
54
55        Ok(Self {
56            signing_key,
57            node_id,
58        })
59    }
60
61    /// Create identity from existing signing key
62    pub fn from_signing_key(signing_key: SigningKey) -> Result<Self> {
63        let node_id = Self::compute_node_id(&signing_key.verifying_key());
64
65        Ok(Self {
66            signing_key,
67            node_id,
68        })
69    }
70
71    /// Compute node ID from public key (SHA-256 hash)
72    pub fn compute_node_id(public_key: &VerifyingKey) -> NodeId {
73        let mut hasher = Sha256::new();
74        hasher.update(public_key.as_bytes());
75        let result = hasher.finalize();
76
77        // Convert hash to UserId
78        let mut bytes = [0u8; 32];
79        bytes.copy_from_slice(&result);
80        crate::peer_record::UserId::from_bytes(bytes)
81    }
82
83    /// Sign a message
84    pub fn sign_message<T: Serialize + Clone>(&self, message: &T) -> Result<SignedMessage<T>> {
85        let timestamp = SystemTime::now()
86            .duration_since(UNIX_EPOCH)
87            .map_err(|e| AdaptiveNetworkError::Other(e.to_string()))?
88            .as_secs();
89
90        let payload_bytes =
91            bincode::serialize(message).map_err(AdaptiveNetworkError::Serialization)?;
92
93        // Create bytes to sign: payload || sender_id || timestamp
94        let mut bytes_to_sign = Vec::new();
95        bytes_to_sign.extend_from_slice(&payload_bytes);
96        bytes_to_sign.extend_from_slice(&self.node_id.hash);
97        bytes_to_sign.extend_from_slice(&timestamp.to_le_bytes());
98
99        let signature = self.signing_key.sign(&bytes_to_sign);
100
101        Ok(SignedMessage {
102            payload: message.clone(),
103            sender_id: self.node_id.clone(),
104            timestamp,
105            signature: signature.to_bytes().to_vec(),
106        })
107    }
108
109    /// Get node ID
110    pub fn node_id(&self) -> &NodeId {
111        &self.node_id
112    }
113
114    /// Get public key
115    pub fn public_key(&self) -> VerifyingKey {
116        self.signing_key.verifying_key()
117    }
118}
119
120impl<T: Serialize + for<'de> Deserialize<'de>> SignedMessage<T> {
121    /// Verify message signature
122    pub fn verify(&self, public_key: &VerifyingKey) -> Result<bool> {
123        let payload_bytes =
124            bincode::serialize(&self.payload).map_err(AdaptiveNetworkError::Serialization)?;
125
126        // Recreate bytes that were signed
127        let mut bytes_to_verify = Vec::new();
128        bytes_to_verify.extend_from_slice(&payload_bytes);
129        bytes_to_verify.extend_from_slice(&self.sender_id.hash);
130        bytes_to_verify.extend_from_slice(&self.timestamp.to_le_bytes());
131
132        let signature_bytes: [u8; 64] = self
133            .signature
134            .as_slice()
135            .try_into()
136            .map_err(|_| AdaptiveNetworkError::Other("Invalid signature length".to_string()))?;
137        let signature = Signature::from_bytes(&signature_bytes);
138
139        Ok(public_key.verify(&bytes_to_verify, &signature).is_ok())
140    }
141
142    /// Get message age in seconds
143    pub fn age(&self) -> Result<u64> {
144        let now = SystemTime::now()
145            .duration_since(UNIX_EPOCH)
146            .map_err(|e| AdaptiveNetworkError::Other(e.to_string()))?
147            .as_secs();
148
149        Ok(now.saturating_sub(self.timestamp))
150    }
151}
152
153/// Identity storage for persistence
154#[derive(Debug, Serialize, Deserialize)]
155pub struct StoredIdentity {
156    /// Secret key bytes
157    pub secret_key: Vec<u8>,
158    /// Public key bytes
159    pub public_key: Vec<u8>,
160    /// Node ID
161    pub node_id: NodeId,
162}
163
164impl StoredIdentity {
165    /// Create from NodeIdentity
166    pub fn from_identity(identity: &NodeIdentity) -> Self {
167        Self {
168            secret_key: identity.signing_key.to_bytes().to_vec(),
169            public_key: identity.signing_key.verifying_key().to_bytes().to_vec(),
170            node_id: identity.node_id.clone(),
171        }
172    }
173
174    /// Restore to NodeIdentity
175    pub fn to_identity(&self) -> Result<NodeIdentity> {
176        let secret_key_bytes: [u8; 32] =
177            self.secret_key.as_slice().try_into().map_err(|_| {
178                AdaptiveNetworkError::Other("Invalid secret key length".to_string())
179            })?;
180        let signing_key = SigningKey::from_bytes(&secret_key_bytes);
181
182        let public_key_bytes: [u8; 32] =
183            self.public_key.as_slice().try_into().map_err(|_| {
184                AdaptiveNetworkError::Other("Invalid public key length".to_string())
185            })?;
186        let public_key = VerifyingKey::from_bytes(&public_key_bytes)
187            .map_err(|e| AdaptiveNetworkError::Other(format!("Invalid public key: {e}")))?;
188
189        // Verify the stored public key matches the signing key
190        if signing_key.verifying_key().to_bytes() != public_key.to_bytes() {
191            return Err(AdaptiveNetworkError::Other(
192                "Public key doesn't match signing key".to_string(),
193            ));
194        }
195
196        // Verify the stored node ID matches
197        let computed_id = NodeIdentity::compute_node_id(&public_key);
198        if computed_id != self.node_id {
199            return Err(AdaptiveNetworkError::Other(
200                "Stored node ID doesn't match computed ID".to_string(),
201            ));
202        }
203
204        Ok(NodeIdentity {
205            signing_key,
206            node_id: self.node_id.clone(),
207        })
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use rand_core::RngCore;
215
216    #[test]
217    fn test_identity_generation() {
218        let identity = NodeIdentity::generate().unwrap();
219
220        // Verify node ID matches public key
221        let computed_id = NodeIdentity::compute_node_id(&identity.public_key());
222        assert_eq!(&computed_id, identity.node_id());
223
224        // PoW removed
225    }
226
227    #[test]
228    fn test_message_signing_and_verification() {
229        let identity = NodeIdentity::generate().unwrap();
230
231        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
232        struct TestMessage {
233            content: String,
234            value: u64,
235        }
236
237        let message = TestMessage {
238            content: "Hello, P2P!".to_string(),
239            value: 42,
240        };
241
242        // Sign message
243        let signed = identity.sign_message(&message).unwrap();
244
245        // Verify with correct public key
246        assert!(signed.verify(&identity.public_key()).unwrap());
247
248        // Verify with wrong public key should fail
249        let other_identity = NodeIdentity::generate().unwrap();
250        assert!(!signed.verify(&other_identity.public_key()).unwrap());
251    }
252
253    #[test]
254    fn test_proof_of_work_verification() {}
255
256    #[test]
257    fn test_identity_serialization() {
258        let identity = NodeIdentity::generate().unwrap();
259
260        // Store identity
261        let stored = StoredIdentity::from_identity(&identity);
262
263        // Restore identity
264        let restored = stored.to_identity().unwrap();
265
266        // Verify they match
267        assert_eq!(identity.node_id(), restored.node_id());
268        assert_eq!(
269            identity.public_key().to_bytes(),
270            restored.public_key().to_bytes()
271        );
272    }
273}