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