Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ members = [
"core",
"misc/metrics",
"misc/multistream-select",
"misc/peer-id-generator",
"misc/keygen",
"muxers/mplex",
"muxers/yamux",
"protocols/dcutr",
Expand Down
15 changes: 8 additions & 7 deletions misc/peer-id-generator/Cargo.toml → misc/keygen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
[package]
name = "peer-id-generator"
edition = "2021"
rust-version = "1.56.1"
name = "keygen"
version = "0.1.0"
description = "Generate peer ids that are prefixed with a specific string"
authors = ["Parity Technologies <[email protected]>"]
edition = "2021"
authors = ["demfabris <[email protected]>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
categories = ["network-programming", "asynchronous"]
publish = false

[dependencies]
structopt = "0.3.26"
zeroize = "1"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
libp2p-core = { path = "../../core", default-features = false, version = "0.32.0"}
num_cpus = "1.8"
base64 = "0.13.0"
41 changes: 41 additions & 0 deletions misc/keygen/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::path::Path;

use libp2p_core::identity::Keypair;
use libp2p_core::PeerId;

#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Config {
pub identity: Identity,
}

impl Config {
pub fn from_file(path: &Path) -> Result<Self, Box<dyn Error>> {
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
}

pub fn from_key_material(peer_id: PeerId, keypair: &Keypair) -> Result<Self, Box<dyn Error>> {
let priv_key = base64::encode(keypair.to_protobuf_encoding()?);
let peer_id = peer_id.to_base58();
Ok(Self {
identity: Identity { peer_id, priv_key },
})
}
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Identity {
#[serde(rename = "PeerID")]
pub peer_id: String,
pub priv_key: String,
}

impl zeroize::Zeroize for Config {
fn zeroize(&mut self) {
self.identity.peer_id.zeroize();
self.identity.priv_key.zeroize();
}
}
126 changes: 126 additions & 0 deletions misc/keygen/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::error::Error;
use std::path::PathBuf;
use std::str::{self, FromStr};
use std::sync::mpsc;
use std::thread;

mod config;

use libp2p_core::identity::{self, ed25519};
use libp2p_core::PeerId;
use structopt::StructOpt;
use zeroize::Zeroizing;

#[derive(Debug, StructOpt)]
#[structopt(name = "libp2p key material generator")]
struct Args {
/// JSON formatted output
#[structopt(long, global = true)]
json: bool,

#[structopt(subcommand)]
cmd: Command,
}

#[derive(Debug, StructOpt)]
enum Command {
/// Read from config file
From {
/// Provide a IPFS config file
#[structopt(parse(from_os_str))]
config: PathBuf,
},
/// Generate random
Rand {
/// The keypair prefix
#[structopt(long)]
prefix: Option<String>,
},
}

// Due to the fact that a peer id uses a SHA-256 multihash, it always starts with the
// bytes 0x1220, meaning that only some characters are valid.
const ALLOWED_FIRST_BYTE: &[u8] = b"NPQRSTUVWXYZ";

// The base58 alphabet is not necessarily obvious.
const ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

fn main() -> Result<(), Box<dyn Error>> {
let args = Args::from_args();

let (local_peer_id, local_keypair) = match args.cmd {
// Generate keypair from some sort of key material. Currently supporting `IPFS` config file
Command::From { config } => {
let config = Zeroizing::new(config::Config::from_file(config.as_ref())?);

let keypair = identity::Keypair::from_protobuf_encoding(&Zeroizing::new(
base64::decode(config.identity.priv_key.as_bytes())?,
))?;

let peer_id = keypair.public().into();
assert_eq!(
PeerId::from_str(&config.identity.peer_id)?,
peer_id,
"Expect peer id derived from private key and peer id retrieved from config to match."
);

(peer_id, keypair)
}

// Generate a random keypair, optionally with a prefix
Command::Rand { prefix } => {
if let Some(prefix) = prefix {
if prefix.as_bytes().iter().any(|c| !ALPHABET.contains(c)) {
eprintln!("Prefix {} is not valid base58", prefix);
std::process::exit(1);
}

// Checking conformity to ALLOWED_FIRST_BYTE.
if !prefix.is_empty() && !ALLOWED_FIRST_BYTE.contains(&prefix.as_bytes()[0]) {
eprintln!("Prefix {} is not reachable", prefix);
eprintln!(
"Only the following bytes are possible as first byte: {}",
str::from_utf8(ALLOWED_FIRST_BYTE).unwrap()
);
std::process::exit(1);
}

let (tx, rx) = mpsc::channel::<(PeerId, identity::Keypair)>();

// Find peer IDs in a multithreaded fashion.
for _ in 0..thread::available_parallelism()?.get() {
let prefix = prefix.clone();
let tx = tx.clone();

thread::spawn(move || loop {
let keypair = ed25519::Keypair::generate();
let peer_id = identity::PublicKey::Ed25519(keypair.public()).to_peer_id();
let base58 = peer_id.to_base58();
if base58[8..].starts_with(&prefix) {
tx.send((peer_id, identity::Keypair::Ed25519(keypair)))
.expect("to send");
}
});
}

rx.recv().expect("to recv")
} else {
let keypair = identity::Keypair::Ed25519(ed25519::Keypair::generate());
(keypair.public().into(), keypair)
}
}
};

if args.json {
let config = config::Config::from_key_material(local_peer_id, &local_keypair)?;
println!("{}", serde_json::to_string(&config)?);
} else {
println!(
"PeerId: {:?} Keypair: {:?}",
local_peer_id,
local_keypair.to_protobuf_encoding()
);
}

Ok(())
}
87 changes: 0 additions & 87 deletions misc/peer-id-generator/src/main.rs

This file was deleted.