Initialize `radicle-tools` crate
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
fcb1007f9d
commit
a3bf5ab7c3
|
|
@ -826,6 +826,14 @@ name = "radicle-std-ext"
|
|||
version = "0.1.0"
|
||||
source = "git+https://github.com/radicle-dev/radicle-link?tag=cycle/2022-07-12#541a8161cb24c3b7b10d44f958cc5c5ed05cf443"
|
||||
|
||||
[[package]]
|
||||
name = "radicle-tools"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"radicle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["radicle", "radicle-node"]
|
||||
members = ["radicle", "radicle-node", "radicle-tools"]
|
||||
|
||||
[patch.crates-io.radicle-git-ext]
|
||||
git = "https://github.com/radicle-dev/radicle-link"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "radicle-tools"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Alexis Sellier <alexis@radicle.xyz>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1" }
|
||||
|
||||
[dependencies.radicle]
|
||||
version = "0"
|
||||
path = "../radicle"
|
||||
|
||||
[[bin]]
|
||||
name = "rad-init"
|
||||
path = "src/rad-init.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rad-auth"
|
||||
path = "src/rad-auth.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rad-self"
|
||||
path = "src/rad-self.rs"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fn main() -> anyhow::Result<()> {
|
||||
let keypair = radicle::crypto::KeyPair::generate();
|
||||
let profile = radicle::Profile::init(keypair)?;
|
||||
|
||||
println!("id: {}", profile.id());
|
||||
println!("home: {}", profile.home.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
use std::path::Path;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let cwd = Path::new(".").canonicalize()?;
|
||||
let name = cwd.file_name().unwrap().to_string_lossy().to_string();
|
||||
let repo = radicle::git::raw::Repository::open(cwd)?;
|
||||
let profile = radicle::Profile::load()?;
|
||||
let (id, _) = radicle::rad::init(
|
||||
&repo,
|
||||
&name,
|
||||
"",
|
||||
radicle::git::BranchName::from("master"),
|
||||
&profile.signer,
|
||||
&profile.storage,
|
||||
)?;
|
||||
|
||||
println!("ok: {}", id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fn main() -> anyhow::Result<()> {
|
||||
let profile = radicle::Profile::load()?;
|
||||
|
||||
println!("id: {}", profile.id());
|
||||
println!("home: {}", profile.home.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -116,6 +116,12 @@ impl TryFrom<&[u8]> for Signature {
|
|||
#[serde(into = "String", try_from = "String")]
|
||||
pub struct PublicKey(pub ed25519::PublicKey);
|
||||
|
||||
impl PublicKey {
|
||||
pub fn from_pem(pem: &str) -> Result<Self, ed25519::Error> {
|
||||
ed25519::PublicKey::from_pem(pem).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// The private/signing key.
|
||||
pub type SecretKey = ed25519::SecretKey;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use once_cell::sync::Lazy;
|
|||
|
||||
use crate::collections::HashMap;
|
||||
use crate::crypto::PublicKey;
|
||||
use crate::storage;
|
||||
use crate::storage::refs::Refs;
|
||||
use crate::storage::RemoteId;
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ pub use git_ref_format::{refname, RefStr, RefString};
|
|||
pub use git_url as url;
|
||||
pub use git_url::Url;
|
||||
pub use radicle_git_ext as ext;
|
||||
pub use storage::BranchName;
|
||||
|
||||
/// Default port of the `git` transport protocol.
|
||||
pub const PROTOCOL_PORT: u16 = 9418;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::os::unix::fs::DirBuilderExt;
|
||||
use std::os::unix::prelude::OpenOptionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::crypto::{PublicKey, SecretKey};
|
||||
|
||||
pub struct UnsafeKeystore {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl UnsafeKeystore {
|
||||
pub fn new<P: AsRef<Path>>(path: &P) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&mut self, public: &PublicKey, secret: &SecretKey) -> Result<(), io::Error> {
|
||||
// TODO: Zeroize secret key.
|
||||
let public = public.to_pem();
|
||||
let secret = secret.to_pem();
|
||||
|
||||
fs::DirBuilder::new()
|
||||
.recursive(true)
|
||||
.mode(0o700)
|
||||
.create(&self.path)?;
|
||||
|
||||
fs::OpenOptions::new()
|
||||
.mode(0o644)
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(self.path.join("radicle.pub"))?
|
||||
.write_all(public.as_bytes())?;
|
||||
|
||||
fs::OpenOptions::new()
|
||||
.mode(0o600)
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(self.path.join("radicle"))?
|
||||
.write_all(secret.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Result<(PublicKey, SecretKey), io::Error> {
|
||||
let public = fs::read(self.path.join("radicle.pub"))?;
|
||||
let public = String::from_utf8(public).unwrap();
|
||||
let public = PublicKey::from_pem(&public).unwrap();
|
||||
|
||||
let secret = fs::read(self.path.join("radicle"))?;
|
||||
let secret = String::from_utf8(secret).unwrap();
|
||||
let secret = SecretKey::from_pem(&secret).unwrap();
|
||||
|
||||
Ok((public, secret))
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,15 @@ pub mod crypto;
|
|||
pub mod git;
|
||||
pub mod hash;
|
||||
pub mod identity;
|
||||
pub mod keystore;
|
||||
pub mod node;
|
||||
pub mod profile;
|
||||
pub mod rad;
|
||||
pub mod serde_ext;
|
||||
pub mod storage;
|
||||
#[cfg(feature = "test")]
|
||||
pub mod test;
|
||||
|
||||
pub use keystore::UnsafeKeystore;
|
||||
pub use profile::Profile;
|
||||
pub use storage::git::Storage;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{env, io};
|
||||
|
||||
use crate::crypto::{KeyPair, PublicKey, SecretKey, Signature, Signer};
|
||||
use crate::keystore::UnsafeKeystore;
|
||||
use crate::storage::git::Storage;
|
||||
|
||||
pub struct UnsafeSigner {
|
||||
public: PublicKey,
|
||||
secret: SecretKey,
|
||||
}
|
||||
|
||||
impl Signer for UnsafeSigner {
|
||||
fn public_key(&self) -> &PublicKey {
|
||||
&self.public
|
||||
}
|
||||
|
||||
fn sign(&self, msg: &[u8]) -> Signature {
|
||||
Signature(self.secret.sign(msg, None))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Profile {
|
||||
pub home: PathBuf,
|
||||
pub signer: UnsafeSigner,
|
||||
pub storage: Storage,
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
pub fn init(keypair: KeyPair) -> Result<Self, io::Error> {
|
||||
let home = self::home()?;
|
||||
let mut keystore = UnsafeKeystore::new(&home.join("keys"));
|
||||
let public = keypair.pk.into();
|
||||
let signer = UnsafeSigner {
|
||||
public,
|
||||
secret: keypair.sk,
|
||||
};
|
||||
let storage = Storage::open(&home.join("storage"))?;
|
||||
|
||||
keystore.put(&signer.public, &signer.secret)?;
|
||||
|
||||
Ok(Profile {
|
||||
home,
|
||||
signer,
|
||||
storage,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load() -> Result<Self, io::Error> {
|
||||
let home = self::home()?;
|
||||
let (public, secret) = UnsafeKeystore::new(&home.join("keys")).get()?;
|
||||
let signer = UnsafeSigner { public, secret };
|
||||
let storage = Storage::open(&home.join("storage"))?;
|
||||
|
||||
Ok(Profile {
|
||||
home,
|
||||
signer,
|
||||
storage,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &PublicKey {
|
||||
self.signer.public_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path to the radicle home folder.
|
||||
pub fn home() -> Result<PathBuf, io::Error> {
|
||||
if let Some(home) = env::var_os("RAD_HOME") {
|
||||
Ok(PathBuf::from(home))
|
||||
} else if let Some(home) = env::var_os("HOME") {
|
||||
Ok(PathBuf::from(home).join(".radicle"))
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"Neither `RAD_HOME` nor `HOME` are set",
|
||||
))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue