core_identity/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 P47H Team <https://p47h.com>
3
4//! # identity
5//!
6//! Cryptographic identities for p47h based on Ed25519.
7//!
8//! This crate provides secure, auditable, and memory-safe identity management
9//! using industry-standard Ed25519 signatures with proper key hygiene.
10//!
11//! ## Features
12//!
13//! - **Ed25519 Signatures**: NIST-compliant digital signatures
14//! - **Memory Safety**: Automatic zeroization of private keys
15//! - **Blake3 Hashing**: Fast public key hashing for lookups
16//! - **libp2p Integration**: Seamless conversion to libp2p keypairs
17//!
18//! ## Example
19//!
20//! ```
21//! use core_identity::Identity;
22//!
23//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
24//! // Generate a new identity
25//! let mut rng = rand::thread_rng();
26//! let identity = Identity::generate(&mut rng)?;
27//!
28//! // Sign a message
29//! let message = b"Hello, network!";
30//! let signature = identity.sign(message);
31//!
32//! // Verify the signature
33//! use core_identity::verify_signature;
34//! verify_signature(&identity.verifying_key(), message, &signature)?;
35//! # Ok(())
36//! # }
37//! ```
38
39mod error;
40pub mod hash;
41
42pub use error::{IdentityError, Result};
43
44use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
45use secrecy::Secret;
46use serde::{Deserialize, Serialize};
47use zeroize::ZeroizeOnDrop;
48
49use rand_core::{CryptoRng, RngCore};
50
51/// Cryptographic identity based on Ed25519
52///
53/// Each node in the network has a unique identity represented by a key pair.
54/// The public key is the node's identity, the private key never leaves this module.
55///
56/// ## Security
57///
58/// - Private keys are automatically zeroized on drop
59/// - Uses OS-level CSPRNG for key generation
60/// - Blake3 hashing for fast public key comparisons
61///
62/// ## Example
63///
64/// ```
65/// use core_identity::Identity;
66///
67/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
68/// let mut rng = rand::thread_rng();
69/// let identity = Identity::generate(&mut rng)?;
70/// let public_key = identity.verifying_key();
71/// let public_key_hash = identity.public_key_hash();
72///
73/// assert_eq!(public_key_hash.len(), 32);
74/// # Ok(())
75/// # }
76/// ```
77#[derive(ZeroizeOnDrop)]
78pub struct Identity {
79    keypair: SigningKey,
80}
81impl Identity {
82    /// Generates a new identity using a provided CSPRNG.
83    ///
84    /// # Example
85    ///
86    /// ```
87    /// use core_identity::Identity;
88    ///
89    /// let mut rng = rand::thread_rng();
90    /// let identity = Identity::generate(&mut rng).expect("Failed to generate identity");
91    /// assert_eq!(identity.public_key_hash().len(), 32);
92    /// ```
93    ///
94    /// # Errors
95    ///
96    /// This function is infallible in practice, but returns `Result`
97    /// for consistency with the API.
98    pub fn generate<R>(csprng: &mut R) -> Result<Self>
99    where
100        R: RngCore + CryptoRng,
101    {
102        Ok(Self {
103            keypair: SigningKey::generate(csprng),
104        })
105    }
106
107    /// Creates an identity from a 32-byte seed
108    ///
109    /// Useful for testing or deriving identities deterministically
110    ///
111    /// # Example
112    ///
113    /// ```
114    /// use core_identity::Identity;
115    ///
116    /// let seed = [42u8; 32];
117    /// let identity1 = Identity::from_seed(&seed).unwrap();
118    /// let identity2 = Identity::from_seed(&seed).unwrap();
119    ///
120    /// // Same seed produces same identity
121    /// assert_eq!(
122    ///     identity1.verifying_key().as_bytes(),
123    ///     identity2.verifying_key().as_bytes()
124    /// );
125    /// ```
126    pub fn from_seed(seed: &[u8; 32]) -> Result<Self> {
127        let keypair = SigningKey::from_bytes(seed);
128
129        Ok(Self { keypair })
130    }
131
132    /// Creates an identity from raw signing key bytes
133    ///
134    /// # Security
135    ///
136    /// This method should only be used for deserialization from secure storage
137    /// (e.g., encrypted keystore).
138    ///
139    /// # Errors
140    ///
141    /// Currently infallible, but returns `Result` for API consistency.
142    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
143        let keypair = SigningKey::from_bytes(bytes);
144        Ok(Self { keypair })
145    }
146
147    /// Returns the public key
148    ///
149    /// This is the identity shared with other nodes
150    ///
151    /// # Example
152    ///
153    /// ```
154    /// use core_identity::Identity;
155    ///
156    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
157    /// let mut rng = rand::thread_rng();
158    /// let identity = Identity::generate(&mut rng)?;
159    /// let public_key = identity.verifying_key();
160    ///
161    /// // Public key is 32 bytes
162    /// assert_eq!(public_key.as_bytes().len(), 32);
163    /// # Ok(())
164    /// # }
165    /// ```
166    pub fn verifying_key(&self) -> VerifyingKey {
167        self.keypair.verifying_key()
168    }
169
170    /// Returns the hash of the public key
171    ///
172    /// Uses the configured hash provider (default: Blake3) for efficient lookups.
173    pub fn public_key_hash(&self) -> [u8; 32] {
174        let bytes = self.keypair.verifying_key().to_bytes();
175        hash::hash(&bytes)
176    }
177
178    /// Signs a message with the private key
179    ///
180    /// # Arguments
181    ///
182    /// * `message` - The bytes to sign
183    ///
184    /// # Returns
185    ///
186    /// A signature that can be verified with the public key
187    ///
188    /// # Example
189    ///
190    /// ```
191    /// use core_identity::{Identity, verify_signature};
192    ///
193    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
194    /// let mut rng = rand::thread_rng();
195    /// let identity = Identity::generate(&mut rng)?;
196    /// let message = b"Important message";
197    ///
198    /// let signature = identity.sign(message);
199    /// verify_signature(&identity.verifying_key(), message, &signature)?;
200    /// # Ok(())
201    /// # }
202    /// ```
203    pub fn sign(&self, message: &[u8]) -> Signature {
204        self.keypair.sign(message)
205    }
206
207    /// Returns the signing key bytes for serialization
208    ///
209    /// # Security
210    ///
211    /// This method returns a `Secret<Vec<u8>>` that prevents accidental exposure:
212    /// - Cannot be printed with Debug/Display
213    /// - Will not appear in logs automatically
214    /// - Requires explicit `.expose_secret()` to access
215    ///
216    /// Should only be used for:
217    /// - Secure serialization (encrypted keystore)
218    /// - Conversion to libp2p keypair
219    /// - Low-level cryptographic operations
220    ///
221    /// WARNING: Use `.expose_secret()` only when absolutely necessary.
222    /// The exposed bytes should be:
223    /// - Encrypted immediately if stored
224    /// - Zeroized after use
225    /// - Never logged or transmitted unencrypted
226    ///
227    /// # Example
228    ///
229    /// ```
230    /// use core_identity::Identity;
231    /// use secrecy::ExposeSecret;
232    ///
233    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
234    /// let mut rng = rand::thread_rng();
235    /// let identity = Identity::generate(&mut rng)?;
236    ///
237    /// // Get the secret bytes
238    /// let secret_bytes = identity.signing_key_bytes();
239    ///
240    /// // Only expose when needed for cryptographic operations
241    /// let raw_bytes = secret_bytes.expose_secret();
242    /// // Use raw_bytes for signing, serialization, etc.
243    /// # Ok(())
244    /// # }
245    /// ```
246    pub fn signing_key_bytes(&self) -> Secret<Vec<u8>> {
247        Secret::new(self.keypair.to_bytes().to_vec())
248    }
249}
250
251/// Verifies a signature for a given message and public key
252///
253/// # Arguments
254///
255/// * `verifying_key` - Public key to verify the signature
256/// * `message` - Message that was signed
257/// * `signature` - Signature to verify
258///
259/// # Returns
260///
261/// * `Ok(())` - If the signature is valid
262/// * `Err(IdentityError::InvalidSignature)` - If the signature is invalid
263///
264/// # Example
265///
266/// ```
267/// use core_identity::{Identity, verify_signature};
268///
269/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
270/// let mut rng = rand::thread_rng();
271/// let identity = Identity::generate(&mut rng)?;
272/// let message = b"Authentic message";
273/// let signature = identity.sign(message);
274///
275/// // Verify with just the public key
276/// verify_signature(&identity.verifying_key(), message, &signature)?;
277/// # Ok(())
278/// # }
279/// ```
280pub fn verify_signature(
281    verifying_key: &VerifyingKey,
282    message: &[u8],
283    signature: &Signature,
284) -> Result<()> {
285    verifying_key
286        .verify(message, signature)
287        .map_err(|_| IdentityError::InvalidSignature)
288}
289
290/// Serializable representation of a public key
291///
292/// This type allows Ed25519 public keys to be serialized and deserialized
293/// safely for storage or transmission.
294///
295/// # Example
296///
297/// ```
298/// use core_identity::{Identity, SerializableVerifyingKey};
299///
300/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
301/// let mut rng = rand::thread_rng();
302/// let identity = Identity::generate(&mut rng)?;
303/// let public_key = identity.verifying_key();
304///
305/// // Convert to serializable form
306/// let serializable = SerializableVerifyingKey::from(&public_key);
307///
308/// // Serialize (e.g., with JSON)
309/// let json = serde_json::to_string(&serializable)?;
310///
311/// // Deserialize
312/// let deserialized: SerializableVerifyingKey = serde_json::from_str(&json)?;
313/// # Ok(())
314/// # }
315/// ```
316#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
317pub struct SerializableVerifyingKey([u8; 32]);
318
319impl SerializableVerifyingKey {
320    /// Returns the bytes of the public key.
321    pub fn as_bytes(&self) -> &[u8; 32] {
322        &self.0
323    }
324}
325
326impl From<&VerifyingKey> for SerializableVerifyingKey {
327    fn from(vk: &VerifyingKey) -> Self {
328        Self(vk.to_bytes())
329    }
330}
331
332impl TryFrom<SerializableVerifyingKey> for VerifyingKey {
333    type Error = IdentityError;
334
335    fn try_from(svk: SerializableVerifyingKey) -> Result<Self> {
336        VerifyingKey::from_bytes(&svk.0).map_err(|_| IdentityError::InvalidPublicKey)
337    }
338}
339
340// ============================================================================
341// Kani Formal Verification Proofs
342// ============================================================================
343
344/// Formal verification proofs for cryptographic size invariants.
345/// Run with: `cargo kani --package core-identity`
346#[cfg(kani)]
347mod kani_proofs {
348    /// Ed25519 public key size is always 32 bytes.
349    #[kani::proof]
350    #[kani::unwind(0)]
351    fn proof_ed25519_pubkey_size() {
352        // Ed25519 public keys are 32 bytes (compressed curve point)
353        const ED25519_PUBKEY_LEN: usize = 32;
354        kani::assert(ED25519_PUBKEY_LEN == 32, "Ed25519 public key must be 32 bytes");
355    }
356
357    /// Ed25519 signature size is always 64 bytes.
358    #[kani::proof]
359    #[kani::unwind(0)]
360    fn proof_ed25519_signature_size() {
361        // Ed25519 signatures are 64 bytes (R + S components)
362        const ED25519_SIG_LEN: usize = 64;
363        kani::assert(ED25519_SIG_LEN == 64, "Ed25519 signature must be 64 bytes");
364    }
365
366    /// Ed25519 seed/secret key size is always 32 bytes.
367    #[kani::proof]
368    #[kani::unwind(0)]
369    fn proof_ed25519_seed_size() {
370        const ED25519_SEED_LEN: usize = 32;
371        kani::assert(ED25519_SEED_LEN == 32, "Ed25519 seed must be 32 bytes");
372    }
373
374    /// Blake3 hash output is always 32 bytes.
375    #[kani::proof]
376    #[kani::unwind(0)]
377    fn proof_blake3_hash_size() {
378        const BLAKE3_OUTPUT_LEN: usize = 32;
379        kani::assert(BLAKE3_OUTPUT_LEN == 32, "Blake3 hash must be 32 bytes");
380    }
381}
382