Skip to main content

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