Skip to main content

Keypair

Struct Keypair 

Source
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

Source

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?
examples/server.rs (line 25)
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
Hide additional examples
examples/client.rs (line 24)
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}
Source

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?
examples/server.rs (line 22)
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}
Source

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.

Source

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

public_key returns the 32-byte Ed25519 public key.

Source

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?
examples/server.rs (line 26)
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}
Source

pub fn private_key(&self) -> [u8; 64]

private_key returns the full 64-byte Ed25519 private key (seed || pubkey).

Source

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?
examples/server.rs (line 47)
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}
Source

pub fn clear(&mut self)

clear zeroes the key material held by this keypair.

This is called automatically on drop. Call it explicitly when you need to erase key material before the value goes out of scope, for example before passing ownership elsewhere.

Trait Implementations§

Source§

impl Drop for Keypair

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Send for Keypair

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, 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.