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}