pub struct Keypair { /* private fields */ }Expand description
Keypair holds an Ed25519 keypair used to authenticate a NWEP node.
The keypair is stored in a heap-allocated Box so that its memory address remains
stable when the Keypair value is moved. The C library keeps a raw pointer to the
keypair for TLS signing during handshakes; if the data were allowed to move, that
pointer would become dangling.
Key material is zeroed on drop via [nwep_keypair_clear].
§Examples
use nwep::Keypair;
nwep::init().unwrap();
let kp = Keypair::generate().unwrap();
println!("node id: {}", kp.node_id().unwrap());Implementations§
Source§impl Keypair
impl Keypair
Sourcepub fn generate() -> Result<Self, Error>
pub fn generate() -> Result<Self, Error>
generate creates a new Ed25519 keypair from OS-provided randomness.
§Errors
Returns an Error if the underlying C library call fails.
Examples found in repository?
10fn load_or_generate_keypair(path: &str) -> Result<Keypair, Box<dyn std::error::Error>> {
11 match fs::read_to_string(path) {
12 Ok(contents) => {
13 let hex = contents.trim();
14 if hex.len() != 64 {
15 return Err(format!("invalid key file {path}: expected 64 hex chars").into());
16 }
17 let bytes: Vec<u8> = (0..32)
18 .map(|i| u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16))
19 .collect::<Result<_, _>>()?;
20 let mut seed = [0u8; 32];
21 seed.copy_from_slice(&bytes);
22 Ok(Keypair::from_seed(&seed)?)
23 }
24 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
25 let kp = Keypair::generate()?;
26 let hex: String = kp.seed().iter().map(|b| format!("{b:02x}")).collect();
27 fs::write(path, format!("{hex}\n"))
28 .map_err(|e| format!("saving key file: {e}"))?;
29 Ok(kp)
30 }
31 Err(e) => Err(e.into()),
32 }
33}More examples
4fn main() {
5 let url = std::env::args().nth(1).unwrap_or_else(|| {
6 eprintln!("usage: client <web://url/path>");
7 process::exit(1);
8 });
9
10 nwep::init().unwrap_or_else(|e| {
11 eprintln!("init: {e}");
12 process::exit(1);
13 });
14
15 nwep::log::set_level(nwep::log::LogLevel::Warn);
16 nwep::log::set_stderr(true);
17
18 let parsed = Url::parse(&url).unwrap_or_else(|e| {
19 eprintln!("parse url: {e}");
20 process::exit(1);
21 });
22 let path = if parsed.path.is_empty() { "/".to_string() } else { parsed.path.clone() };
23
24 let keypair = Keypair::generate().unwrap_or_else(|e| {
25 eprintln!("keygen: {e}");
26 process::exit(1);
27 });
28
29 let client = ClientBuilder::new()
30 .connect(keypair, &url)
31 .unwrap_or_else(|e| {
32 eprintln!("connect: {e}");
33 process::exit(1);
34 });
35
36 let resp = client.get(&path).unwrap_or_else(|e| {
37 eprintln!("get: {e}");
38 process::exit(1);
39 });
40
41 println!("{}", resp.status);
42 println!("{}", String::from_utf8_lossy(&resp.body));
43
44 client.close();
45}Sourcepub fn from_seed(seed: &[u8; 32]) -> Result<Self, Error>
pub fn from_seed(seed: &[u8; 32]) -> Result<Self, Error>
from_seed derives an Ed25519 keypair from a 32-byte seed.
The seed is the first 32 bytes of the standard 64-byte Ed25519 private key
representation (seed || pubkey). Passing the same seed always produces the
same keypair.
§Errors
Returns an Error if the C library call fails.
Examples found in repository?
10fn load_or_generate_keypair(path: &str) -> Result<Keypair, Box<dyn std::error::Error>> {
11 match fs::read_to_string(path) {
12 Ok(contents) => {
13 let hex = contents.trim();
14 if hex.len() != 64 {
15 return Err(format!("invalid key file {path}: expected 64 hex chars").into());
16 }
17 let bytes: Vec<u8> = (0..32)
18 .map(|i| u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16))
19 .collect::<Result<_, _>>()?;
20 let mut seed = [0u8; 32];
21 seed.copy_from_slice(&bytes);
22 Ok(Keypair::from_seed(&seed)?)
23 }
24 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
25 let kp = Keypair::generate()?;
26 let hex: String = kp.seed().iter().map(|b| format!("{b:02x}")).collect();
27 fs::write(path, format!("{hex}\n"))
28 .map_err(|e| format!("saving key file: {e}"))?;
29 Ok(kp)
30 }
31 Err(e) => Err(e.into()),
32 }
33}Sourcepub fn from_privkey(privkey: &[u8; 64]) -> Result<Self, Error>
pub fn from_privkey(privkey: &[u8; 64]) -> Result<Self, Error>
from_privkey reconstructs a keypair from a 64-byte Ed25519 private key.
The 64-byte private key is the concatenation seed || pubkey. The public key is
re-derived from the seed by the C library and the provided public-key suffix is
used for validation.
§Errors
Returns an Error if the key material is invalid or the C library call fails.
Sourcepub fn public_key(&self) -> [u8; 32]
pub fn public_key(&self) -> [u8; 32]
public_key returns the 32-byte Ed25519 public key.
Sourcepub fn seed(&self) -> [u8; 32]
pub fn seed(&self) -> [u8; 32]
seed returns the 32-byte Ed25519 seed (first half of the private key).
The seed is sufficient to fully reconstruct the keypair via Keypair::from_seed.
Examples found in repository?
10fn load_or_generate_keypair(path: &str) -> Result<Keypair, Box<dyn std::error::Error>> {
11 match fs::read_to_string(path) {
12 Ok(contents) => {
13 let hex = contents.trim();
14 if hex.len() != 64 {
15 return Err(format!("invalid key file {path}: expected 64 hex chars").into());
16 }
17 let bytes: Vec<u8> = (0..32)
18 .map(|i| u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16))
19 .collect::<Result<_, _>>()?;
20 let mut seed = [0u8; 32];
21 seed.copy_from_slice(&bytes);
22 Ok(Keypair::from_seed(&seed)?)
23 }
24 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
25 let kp = Keypair::generate()?;
26 let hex: String = kp.seed().iter().map(|b| format!("{b:02x}")).collect();
27 fs::write(path, format!("{hex}\n"))
28 .map_err(|e| format!("saving key file: {e}"))?;
29 Ok(kp)
30 }
31 Err(e) => Err(e.into()),
32 }
33}Sourcepub fn private_key(&self) -> [u8; 64]
pub fn private_key(&self) -> [u8; 64]
private_key returns the full 64-byte Ed25519 private key (seed || pubkey).
Sourcepub fn node_id(&self) -> Result<NodeId, Error>
pub fn node_id(&self) -> Result<NodeId, Error>
node_id derives the NodeId for this keypair by hashing the public key.
The NodeId uniquely identifies a node on the network and is embedded in NWEP addresses, ensuring that connection targets cannot be impersonated.
§Errors
Returns an Error if the C library call fails.
Examples found in repository?
35fn main() {
36 nwep::init().unwrap_or_else(|e| {
37 eprintln!("init: {e}");
38 process::exit(1);
39 });
40
41 let key_file = std::env::var("NWEP_KEY_FILE").unwrap_or_else(|_| DEFAULT_KEY_FILE.into());
42 let keypair = load_or_generate_keypair(&key_file).unwrap_or_else(|e| {
43 eprintln!("keypair: {e}");
44 process::exit(1);
45 });
46
47 let node_id = keypair.node_id().unwrap_or_else(|e| {
48 eprintln!("node_id: {e}");
49 process::exit(1);
50 });
51 eprintln!("node id: {node_id}");
52
53 let mut router = Router::new();
54 router.handle_func("/hello", |w: &mut ResponseWriter, _r: &Request| {
55 let _ = w.respond("ok", b"hello from nwep-rust");
56 });
57
58 let addr = std::env::args().nth(1).unwrap_or_else(|| DEFAULT_ADDR.into());
59
60 let (server, event_loop) = ServerBuilder::new(&addr, keypair)
61 .on_connect(|info| eprintln!("connected: {}", info.node_id))
62 .on_disconnect(|info, code| eprintln!("disconnected: {} (code {code})", info.node_id))
63 .build(router)
64 .unwrap_or_else(|e| {
65 eprintln!("server: {e}");
66 process::exit(1);
67 });
68
69 let url = server.url("/hello");
70 eprintln!("listening on {}", server.addr());
71 println!("{url}"); // URL printed to stdout for the client to read
72
73 if let Err(e) = event_loop.run() {
74 eprintln!("server run: {e}");
75 process::exit(1);
76 }
77}