p2p_chat/crypto/
identity.rs

1//! This module manages the user's identity, which consists of a libp2p keypair
2//! and an HPKE keypair.
3use crate::crypto::HpkeContext;
4use anyhow::Result;
5use libp2p::{identity, PeerId};
6use serde::{Deserialize, Serialize};
7use std::fs;
8use std::path::Path;
9
10/// A serializable representation of the user's keypairs.
11#[derive(Serialize, Deserialize)]
12pub struct KeyPair {
13    /// The serialized Ed25519 keypair for libp2p.
14    pub libp2p_keypair: Vec<u8>,
15    /// The X25519 private key for HPKE.
16    pub hpke_private_key: Vec<u8>,
17    /// The X25519 public key for HPKE.
18    pub hpke_public_key: Vec<u8>,
19}
20
21/// Represents the user's identity, including their libp2p and HPKE keypairs.
22pub struct Identity {
23    /// The user's peer ID, derived from the libp2p public key.
24    pub peer_id: PeerId,
25    /// The libp2p keypair.
26    pub libp2p_keypair: identity::Keypair,
27    /// The HPKE context, containing the HPKE keypair.
28    pub hpke_context: HpkeContext,
29}
30
31impl Identity {
32    /// Generates a new identity.
33    ///
34    /// This will create a new `Identity` with a randomly generated libp2p Ed25519
35    /// keypair and an HPKE X25519 keypair.
36    ///
37    /// # Errors
38    ///
39    /// This function will return an error if key generation fails.
40    pub fn generate() -> Result<Self> {
41        // Generate libp2p Ed25519 keypair.
42        let libp2p_keypair = identity::Keypair::generate_ed25519();
43        let peer_id = PeerId::from(libp2p_keypair.public());
44
45        // Generate HPKE X25519 keypair.
46        let hpke_context = HpkeContext::new()?;
47
48        Ok(Self {
49            peer_id,
50            libp2p_keypair,
51            hpke_context,
52        })
53    }
54
55    /// Loads an identity from a file, or generates a new one if the file doesn't exist.
56    ///
57    /// # Arguments
58    ///
59    /// * `path` - The path to the identity file.
60    ///
61    /// # Errors
62    ///
63    /// This function will return an error if loading or generating the identity fails.
64    pub fn load_or_generate(path: &str) -> Result<Self> {
65        if Path::new(path).exists() {
66            Self::load(path)
67        } else {
68            let identity = Self::generate()?;
69            identity.save(path)?;
70            Ok(identity)
71        }
72    }
73
74    /// Loads an identity from a file.
75    ///
76    /// # Arguments
77    ///
78    /// * `path` - The path to the identity file.
79    ///
80    /// # Errors
81    ///
82    /// This function will return an error if the file cannot be read or if the
83    /// keypair data is invalid.
84    pub fn load(path: &str) -> Result<Self> {
85        let content = fs::read_to_string(path)?;
86        let keypair_data: KeyPair = serde_json::from_str(&content)?;
87
88        // Reconstruct libp2p keypair.
89        let libp2p_keypair =
90            identity::Keypair::from_protobuf_encoding(&keypair_data.libp2p_keypair)?;
91        let peer_id = PeerId::from(libp2p_keypair.public());
92
93        // Reconstruct HPKE context.
94        let hpke_context = HpkeContext::from_private_key(&keypair_data.hpke_private_key)?;
95
96        Ok(Self {
97            peer_id,
98            libp2p_keypair,
99            hpke_context,
100        })
101    }
102
103    /// Saves the identity to a file.
104    ///
105    /// # Arguments
106    ///
107    /// * `path` - The path to the identity file.
108    ///
109    /// # Errors
110    ///
111    /// This function will return an error if the identity cannot be saved.
112    pub fn save(&self, path: &str) -> Result<()> {
113        let keypair_data = KeyPair {
114            libp2p_keypair: self.libp2p_keypair.to_protobuf_encoding()?,
115            hpke_private_key: self.hpke_context.private_key_bytes(),
116            hpke_public_key: self.hpke_context.public_key_bytes(),
117        };
118
119        let content = serde_json::to_string_pretty(&keypair_data)?;
120
121        if let Some(parent) = Path::new(path).parent() {
122            fs::create_dir_all(parent)?;
123        }
124
125        fs::write(path, content)?;
126        Ok(())
127    }
128
129    /// Returns the HPKE public key bytes.
130    pub fn hpke_public_key(&self) -> Vec<u8> {
131        self.hpke_context.public_key_bytes()
132    }
133
134    /// Encrypts a message for a recipient using their public key.
135    ///
136    /// # Arguments
137    ///
138    /// * `recipient_public_key` - The public key of the recipient.
139    /// * `plaintext` - The data to encrypt.
140    /// This function will return an error if encryption fails.
141    pub fn encrypt_for(&self, recipient_public_key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
142        self.hpke_context.seal(recipient_public_key, plaintext)
143    }
144
145    /// Decrypts a message from a sender using their public key.
146    ///
147    /// # Arguments
148    ///
149    /// * `sender_public_key` - The public key of the sender.
150    /// * `ciphertext` - The data to decrypt.
151    ///
152    /// # Errors
153    ///
154    /// This function will return an error if decryption fails.
155    pub fn decrypt_from(&self, sender_public_key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
156        self.hpke_context.open(sender_public_key, ciphertext)
157    }
158}