Identity

Struct Identity 

Source
pub struct Identity { /* private fields */ }
Expand description

Cryptographic identity based on Ed25519

Each node in the network has a unique identity represented by a key pair. The public key is the node’s identity, the private key never leaves this module.

§Security

  • Private keys are automatically zeroized on drop
  • Uses OS-level CSPRNG for key generation
  • Blake3 hashing for fast public key comparisons

§Example

use core_identity::Identity;

let mut rng = rand::thread_rng();
let identity = Identity::generate(&mut rng)?;
let public_key = identity.verifying_key();
let public_key_hash = identity.public_key_hash();

assert_eq!(public_key_hash.len(), 32);

Implementations§

Source§

impl Identity

Source

pub fn generate<R>(csprng: &mut R) -> Result<Identity, IdentityError>
where R: RngCore + CryptoRng,

Generates a new identity using a provided CSPRNG.

§Example
use core_identity::Identity;

let mut rng = rand::thread_rng();
let identity = Identity::generate(&mut rng).expect("Failed to generate identity");
assert_eq!(identity.public_key_hash().len(), 32);
§Errors

This function is infallible in practice, but returns Result for consistency with the API.

Examples found in repository?
examples/simple_auth_flow.rs (line 28)
14fn main() -> Result<(), Box<dyn std::error::Error>> {
15    println!("p47h Open Core - Simple Authorization Flow");
16    println!("-------------------------------------------");
17    println!();
18
19    // -------------------------------------------------------------------------
20    // Step 1: Create Identities
21    // -------------------------------------------------------------------------
22    // Each user or device in the network has a unique cryptographic identity.
23    // The identity is an Ed25519 keypair. We use the public key hash as the
24    // peer identifier for policy rules.
25
26    let mut rng = rand::thread_rng();
27
28    let alice = Identity::generate(&mut rng)?;
29    let alice_id = format!("alice-{}", hex::encode(&alice.public_key_hash()[..4]));
30
31    let bob = Identity::generate(&mut rng)?;
32    let bob_id = format!("bob-{}", hex::encode(&bob.public_key_hash()[..4]));
33
34    println!("Created identities:");
35    println!("  Alice: {}", alice_id);
36    println!("  Bob:   {}", bob_id);
37    println!();
38
39    // -------------------------------------------------------------------------
40    // Step 2: Define a Policy
41    // -------------------------------------------------------------------------
42    // A policy is a collection of rules that define who can do what on which
43    // resources. Each rule specifies:
44    //   - peer_id: The identity this rule applies to
45    //   - action: What operation is allowed (Read, Write, Execute, Delete, All)
46    //   - resource: What resource pattern this applies to (supports wildcards)
47
48    // Policy parameters:
49    //   - name: Human-readable identifier
50    //   - valid_duration: How long the policy is valid (in seconds)
51    //   - current_time: Unix timestamp when the policy was created
52    let current_time = 1700000000; // Fixed timestamp for reproducibility
53    let valid_duration = 86400; // 24 hours
54
55    let policy = Policy::new("file-access-policy", valid_duration, current_time)?
56        // Rule 1: Alice can read any file under /data/
57        .add_rule(PolicyRule::new(
58            alice_id.clone(),
59            Action::Read,
60            Resource::File("/data/*".into()),
61        ))?
62        // Rule 2: Alice can write to her personal directory
63        .add_rule(PolicyRule::new(
64            alice_id.clone(),
65            Action::Write,
66            Resource::File("/home/alice/*".into()),
67        ))?
68        // Rule 3: Bob has full access to the /shared/ directory
69        .add_rule(PolicyRule::new(
70            bob_id.clone(),
71            Action::All,
72            Resource::File("/shared/*".into()),
73        ))?;
74
75    println!("Created policy: {}", policy.name());
76    println!("  Rules: {}", policy.rules().len());
77    println!("  Valid until: {} (Unix timestamp)", policy.valid_until());
78    println!();
79
80    // -------------------------------------------------------------------------
81    // Step 3: Evaluate Access Requests
82    // -------------------------------------------------------------------------
83    // The PolicyAuthorizer evaluates whether a specific peer can perform a
84    // specific action on a specific resource. It checks all rules and returns
85    // true if at least one rule allows the access.
86
87    let authorizer = PolicyAuthorizer::new(policy.rules());
88
89    // Define test cases: (peer, action, resource, expected_result)
90    let test_cases = [
91        // Alice reading from /data/ - should be ALLOWED (Rule 1)
92        (&alice_id, Action::Read, "/data/report.txt", true),
93        // Alice writing to /data/ - should be DENIED (no rule allows this)
94        (&alice_id, Action::Write, "/data/report.txt", false),
95        // Alice writing to her home - should be ALLOWED (Rule 2)
96        (&alice_id, Action::Write, "/home/alice/notes.txt", true),
97        // Bob reading from /data/ - should be DENIED (no rule for Bob on /data/)
98        (&bob_id, Action::Read, "/data/report.txt", false),
99        // Bob writing to /shared/ - should be ALLOWED (Rule 3, Action::All)
100        (&bob_id, Action::Write, "/shared/document.pdf", true),
101        // Bob deleting from /shared/ - should be ALLOWED (Rule 3, Action::All)
102        (&bob_id, Action::Delete, "/shared/old-file.txt", true),
103        // Unknown user - should be DENIED (no rules match)
104        (
105            &"unknown-user".to_string(),
106            Action::Read,
107            "/data/file.txt",
108            false,
109        ),
110    ];
111
112    println!("Evaluating access requests:");
113    println!();
114
115    for (peer_id, action, resource_path, expected) in test_cases {
116        let resource = Resource::File(resource_path.into());
117        let allowed = authorizer.is_allowed(peer_id, &action, &resource);
118
119        // Verify our expectations match the actual result
120        let status = if allowed { "ALLOWED" } else { "DENIED" };
121        let check = if allowed == expected {
122            "OK"
123        } else {
124            "MISMATCH"
125        };
126
127        println!(
128            "  {} {:?} {} -> {} [{}]",
129            peer_id, action, resource_path, status, check
130        );
131    }
132
133    println!();
134    println!("-------------------------------------------");
135    println!("Authorization flow completed successfully.");
136
137    Ok(())
138}
Source

pub fn from_seed(seed: &[u8; 32]) -> Result<Identity, IdentityError>

Creates an identity from a 32-byte seed

Useful for testing or deriving identities deterministically

§Example
use core_identity::Identity;

let seed = [42u8; 32];
let identity1 = Identity::from_seed(&seed).unwrap();
let identity2 = Identity::from_seed(&seed).unwrap();

// Same seed produces same identity
assert_eq!(
    identity1.verifying_key().as_bytes(),
    identity2.verifying_key().as_bytes()
);
Source

pub fn from_bytes(bytes: &[u8; 32]) -> Result<Identity, IdentityError>

Creates an identity from raw signing key bytes

§Security

This method should only be used for deserialization from secure storage (e.g., encrypted keystore).

§Errors

Currently infallible, but returns Result for API consistency.

Source

pub fn verifying_key(&self) -> VerifyingKey

Returns the public key

This is the identity shared with other nodes

§Example
use core_identity::Identity;

let mut rng = rand::thread_rng();
let identity = Identity::generate(&mut rng)?;
let public_key = identity.verifying_key();

// Public key is 32 bytes
assert_eq!(public_key.as_bytes().len(), 32);
Source

pub fn public_key_hash(&self) -> [u8; 32]

Returns the hash of the public key

Uses the configured hash provider (default: Blake3) for efficient lookups.

Examples found in repository?
examples/simple_auth_flow.rs (line 29)
14fn main() -> Result<(), Box<dyn std::error::Error>> {
15    println!("p47h Open Core - Simple Authorization Flow");
16    println!("-------------------------------------------");
17    println!();
18
19    // -------------------------------------------------------------------------
20    // Step 1: Create Identities
21    // -------------------------------------------------------------------------
22    // Each user or device in the network has a unique cryptographic identity.
23    // The identity is an Ed25519 keypair. We use the public key hash as the
24    // peer identifier for policy rules.
25
26    let mut rng = rand::thread_rng();
27
28    let alice = Identity::generate(&mut rng)?;
29    let alice_id = format!("alice-{}", hex::encode(&alice.public_key_hash()[..4]));
30
31    let bob = Identity::generate(&mut rng)?;
32    let bob_id = format!("bob-{}", hex::encode(&bob.public_key_hash()[..4]));
33
34    println!("Created identities:");
35    println!("  Alice: {}", alice_id);
36    println!("  Bob:   {}", bob_id);
37    println!();
38
39    // -------------------------------------------------------------------------
40    // Step 2: Define a Policy
41    // -------------------------------------------------------------------------
42    // A policy is a collection of rules that define who can do what on which
43    // resources. Each rule specifies:
44    //   - peer_id: The identity this rule applies to
45    //   - action: What operation is allowed (Read, Write, Execute, Delete, All)
46    //   - resource: What resource pattern this applies to (supports wildcards)
47
48    // Policy parameters:
49    //   - name: Human-readable identifier
50    //   - valid_duration: How long the policy is valid (in seconds)
51    //   - current_time: Unix timestamp when the policy was created
52    let current_time = 1700000000; // Fixed timestamp for reproducibility
53    let valid_duration = 86400; // 24 hours
54
55    let policy = Policy::new("file-access-policy", valid_duration, current_time)?
56        // Rule 1: Alice can read any file under /data/
57        .add_rule(PolicyRule::new(
58            alice_id.clone(),
59            Action::Read,
60            Resource::File("/data/*".into()),
61        ))?
62        // Rule 2: Alice can write to her personal directory
63        .add_rule(PolicyRule::new(
64            alice_id.clone(),
65            Action::Write,
66            Resource::File("/home/alice/*".into()),
67        ))?
68        // Rule 3: Bob has full access to the /shared/ directory
69        .add_rule(PolicyRule::new(
70            bob_id.clone(),
71            Action::All,
72            Resource::File("/shared/*".into()),
73        ))?;
74
75    println!("Created policy: {}", policy.name());
76    println!("  Rules: {}", policy.rules().len());
77    println!("  Valid until: {} (Unix timestamp)", policy.valid_until());
78    println!();
79
80    // -------------------------------------------------------------------------
81    // Step 3: Evaluate Access Requests
82    // -------------------------------------------------------------------------
83    // The PolicyAuthorizer evaluates whether a specific peer can perform a
84    // specific action on a specific resource. It checks all rules and returns
85    // true if at least one rule allows the access.
86
87    let authorizer = PolicyAuthorizer::new(policy.rules());
88
89    // Define test cases: (peer, action, resource, expected_result)
90    let test_cases = [
91        // Alice reading from /data/ - should be ALLOWED (Rule 1)
92        (&alice_id, Action::Read, "/data/report.txt", true),
93        // Alice writing to /data/ - should be DENIED (no rule allows this)
94        (&alice_id, Action::Write, "/data/report.txt", false),
95        // Alice writing to her home - should be ALLOWED (Rule 2)
96        (&alice_id, Action::Write, "/home/alice/notes.txt", true),
97        // Bob reading from /data/ - should be DENIED (no rule for Bob on /data/)
98        (&bob_id, Action::Read, "/data/report.txt", false),
99        // Bob writing to /shared/ - should be ALLOWED (Rule 3, Action::All)
100        (&bob_id, Action::Write, "/shared/document.pdf", true),
101        // Bob deleting from /shared/ - should be ALLOWED (Rule 3, Action::All)
102        (&bob_id, Action::Delete, "/shared/old-file.txt", true),
103        // Unknown user - should be DENIED (no rules match)
104        (
105            &"unknown-user".to_string(),
106            Action::Read,
107            "/data/file.txt",
108            false,
109        ),
110    ];
111
112    println!("Evaluating access requests:");
113    println!();
114
115    for (peer_id, action, resource_path, expected) in test_cases {
116        let resource = Resource::File(resource_path.into());
117        let allowed = authorizer.is_allowed(peer_id, &action, &resource);
118
119        // Verify our expectations match the actual result
120        let status = if allowed { "ALLOWED" } else { "DENIED" };
121        let check = if allowed == expected {
122            "OK"
123        } else {
124            "MISMATCH"
125        };
126
127        println!(
128            "  {} {:?} {} -> {} [{}]",
129            peer_id, action, resource_path, status, check
130        );
131    }
132
133    println!();
134    println!("-------------------------------------------");
135    println!("Authorization flow completed successfully.");
136
137    Ok(())
138}
Source

pub fn sign(&self, message: &[u8]) -> Signature

Signs a message with the private key

§Arguments
  • message - The bytes to sign
§Returns

A signature that can be verified with the public key

§Example
use core_identity::{Identity, verify_signature};

let mut rng = rand::thread_rng();
let identity = Identity::generate(&mut rng)?;
let message = b"Important message";

let signature = identity.sign(message);
verify_signature(&identity.verifying_key(), message, &signature)?;
Source

pub fn signing_key_bytes(&self) -> Secret<Vec<u8>>

Returns the signing key bytes for serialization

§Security

This method returns a Secret<Vec<u8>> that prevents accidental exposure:

  • Cannot be printed with Debug/Display
  • Will not appear in logs automatically
  • Requires explicit .expose_secret() to access

Should only be used for:

  • Secure serialization (encrypted keystore)
  • Conversion to libp2p keypair
  • Low-level cryptographic operations

WARNING: Use .expose_secret() only when absolutely necessary. The exposed bytes should be:

  • Encrypted immediately if stored
  • Zeroized after use
  • Never logged or transmitted unencrypted
§Example
use core_identity::Identity;
use secrecy::ExposeSecret;

let mut rng = rand::thread_rng();
let identity = Identity::generate(&mut rng)?;

// Get the secret bytes
let secret_bytes = identity.signing_key_bytes();

// Only expose when needed for cryptographic operations
let raw_bytes = secret_bytes.expose_secret();
// Use raw_bytes for signing, serialization, etc.

Trait Implementations§

Source§

impl Drop for Identity

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V