(keystore) better keystore impl. Closes #4
This commit is contained in:
11
Cargo.toml
11
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kuska-ssb"
|
name = "kuska-ssb"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
authors = ["Dhole <dhole@riseup.net>", "Adria Massanet <adria@codecontext.io>"]
|
authors = ["Dhole <dhole@riseup.net>", "Adria Massanet <adria@codecontext.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -9,7 +9,6 @@ name = "kuska_ssb"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kuska-handshake = { git = "https://github.com/Kuska-ssb/kuska-handshake", branch = "master" , features=["sync","async_std"] }
|
kuska-handshake = { git = "https://github.com/Kuska-ssb/kuska-handshake", branch = "master" , features=["sync","async_std"] }
|
||||||
|
|
||||||
sodiumoxide = { git = "https://github.com/Dhole/sodiumoxidez", branch = "extra" }
|
sodiumoxide = { git = "https://github.com/Dhole/sodiumoxidez", branch = "extra" }
|
||||||
base64 = "0.11.0"
|
base64 = "0.11.0"
|
||||||
hex = "0.4.0"
|
hex = "0.4.0"
|
||||||
@ -22,4 +21,10 @@ serde_json = { version = "1.0.48", features=["preserve_order","arbitrary_precisi
|
|||||||
dirs = "2.0"
|
dirs = "2.0"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "ssb-cli"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
structopt = "0.3.9"
|
||||||
|
@ -3,8 +3,10 @@ extern crate kuska_ssb;
|
|||||||
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
|
extern crate structopt;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use async_std::io::{Read, Write};
|
use async_std::io::{Read, Write};
|
||||||
use async_std::net::TcpStream;
|
use async_std::net::TcpStream;
|
||||||
@ -13,6 +15,7 @@ use kuska_handshake::async_std::{handshake_client, BoxStream};
|
|||||||
use kuska_ssb::api::{
|
use kuska_ssb::api::{
|
||||||
ApiHelper, CreateHistoryStreamArgs, CreateStreamArgs, LatestUserMessage, WhoAmI,
|
ApiHelper, CreateHistoryStreamArgs, CreateStreamArgs, LatestUserMessage, WhoAmI,
|
||||||
};
|
};
|
||||||
|
use kuska_ssb::crypto::ToSodiumObject;
|
||||||
use kuska_ssb::discovery::ssb_net_id;
|
use kuska_ssb::discovery::ssb_net_id;
|
||||||
use kuska_ssb::feed::{is_privatebox, privatebox_decipher, Feed, Message};
|
use kuska_ssb::feed::{is_privatebox, privatebox_decipher, Feed, Message};
|
||||||
use kuska_ssb::keystore::from_patchwork_local;
|
use kuska_ssb::keystore::from_patchwork_local;
|
||||||
@ -21,6 +24,15 @@ use kuska_ssb::rpc::{RecvMsg, RequestNo, RpcStream};
|
|||||||
|
|
||||||
type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
#[structopt(name = "example", about = "An example of StructOpt usage.")]
|
||||||
|
struct Opt {
|
||||||
|
/// Connect to server
|
||||||
|
// format is: server:port:<server_id>
|
||||||
|
#[structopt(short, long)]
|
||||||
|
connect: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn whoami_res_parse(body: &[u8]) -> AnyResult<WhoAmI> {
|
pub fn whoami_res_parse(body: &[u8]) -> AnyResult<WhoAmI> {
|
||||||
Ok(serde_json::from_slice(body)?)
|
Ok(serde_json::from_slice(body)?)
|
||||||
}
|
}
|
||||||
@ -120,10 +132,19 @@ async fn main() -> AnyResult<()> {
|
|||||||
env_logger::init();
|
env_logger::init();
|
||||||
log::set_max_level(log::LevelFilter::max());
|
log::set_max_level(log::LevelFilter::max());
|
||||||
|
|
||||||
let OwnedIdentity { pk, sk, .. } = from_patchwork_local().expect("read local secret");
|
let OwnedIdentity { pk, sk, id } = from_patchwork_local().await.expect("read local secret");
|
||||||
|
println!("connecting with identity {}", id);
|
||||||
|
|
||||||
let mut socket = TcpStream::connect("127.0.0.1:8080").await?;
|
let opt = Opt::from_args();
|
||||||
let handshake = handshake_client(&mut socket, ssb_net_id(), pk, sk.clone(), pk).await?;
|
let connect: Vec<_> = opt.connect.split(":").collect();
|
||||||
|
if connect.len() != 3 {
|
||||||
|
panic!("connection string should be server:port:id");
|
||||||
|
}
|
||||||
|
let server_pk = connect[2][1..].to_ed25519_pk()?;
|
||||||
|
|
||||||
|
let mut socket = TcpStream::connect(format!("{}:{}", connect[0], connect[1])).await?;
|
||||||
|
|
||||||
|
let handshake = handshake_client(&mut socket, ssb_net_id(), pk, sk.clone(), server_pk).await?;
|
||||||
|
|
||||||
println!("💃 handshake complete");
|
println!("💃 handshake complete");
|
||||||
|
|
||||||
|
@ -26,6 +26,12 @@ impl<'a> ToSsbId for ed25519::PublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ToSsbId for ed25519::SecretKey {
|
||||||
|
fn to_ssb_id(&self) -> String {
|
||||||
|
format!("{}{}", base64::encode(self), CURVE_ED25519_SUFFIX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToSodiumObject for str {
|
impl ToSodiumObject for str {
|
||||||
fn to_ed25519_pk(self: &str) -> Result<ed25519::PublicKey> {
|
fn to_ed25519_pk(self: &str) -> Result<ed25519::PublicKey> {
|
||||||
if !self.ends_with(CURVE_ED25519_SUFFIX) {
|
if !self.ends_with(CURVE_ED25519_SUFFIX) {
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
HomeNotFound,
|
HomeNotFound,
|
||||||
InvalidConfig,
|
InvalidConfig,
|
||||||
|
Serde(serde_json::Error),
|
||||||
CryptoFormat(crate::crypto::Error),
|
CryptoFormat(crate::crypto::Error),
|
||||||
Io(std::io::Error),
|
SyncIo(std::io::Error),
|
||||||
}
|
}
|
||||||
impl From<crate::crypto::Error> for Error {
|
impl From<crate::crypto::Error> for Error {
|
||||||
fn from(err: crate::crypto::Error) -> Self {
|
fn from(err: crate::crypto::Error) -> Self {
|
||||||
@ -13,7 +14,13 @@ impl From<crate::crypto::Error> for Error {
|
|||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl From<std::io::Error> for Error {
|
||||||
fn from(err: std::io::Error) -> Self {
|
fn from(err: std::io::Error) -> Self {
|
||||||
Error::Io(err)
|
Error::SyncIo(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
fn from(err: serde_json::Error) -> Self {
|
||||||
|
Error::Serde(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,4 +3,4 @@ mod identity;
|
|||||||
pub mod patchwork;
|
pub mod patchwork;
|
||||||
|
|
||||||
pub use identity::OwnedIdentity;
|
pub use identity::OwnedIdentity;
|
||||||
pub use patchwork::{from_patchwork_config, from_patchwork_local};
|
pub use patchwork::{from_patchwork_local, read_patchwork_config, write_patchwork_config};
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use std::io;
|
use async_std::io::{Read, Write};
|
||||||
use std::string::ToString;
|
use async_std::prelude::*;
|
||||||
|
|
||||||
use crate::crypto::ToSodiumObject;
|
use std::string::ToString;
|
||||||
|
|
||||||
use super::error::{Error, Result};
|
use super::error::{Error, Result};
|
||||||
use super::OwnedIdentity;
|
use super::OwnedIdentity;
|
||||||
|
use crate::crypto::{ToSodiumObject, ToSsbId};
|
||||||
|
use serde_json::to_vec_pretty;
|
||||||
|
|
||||||
pub const CURVE_ED25519: &str = "ed25519";
|
pub const CURVE_ED25519: &str = "ed25519";
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct JsonSSBSecret {
|
struct JsonSSBSecret {
|
||||||
id: String,
|
id: String,
|
||||||
curve: String,
|
curve: String,
|
||||||
@ -16,28 +18,29 @@ struct JsonSSBSecret {
|
|||||||
private: String,
|
private: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_ioerr<T: ToString>(err: T) -> io::Error {
|
fn to_io_error<T: ToString>(err: T) -> async_std::io::Error {
|
||||||
io::Error::new(io::ErrorKind::Other, err.to_string())
|
async_std::io::Error::new(std::io::ErrorKind::Other, err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_patchwork_local() -> Result<OwnedIdentity> {
|
pub async fn from_patchwork_local() -> Result<OwnedIdentity> {
|
||||||
let home_dir = dirs::home_dir().ok_or(Error::HomeNotFound)?;
|
let home_dir = dirs::home_dir().ok_or(Error::HomeNotFound)?;
|
||||||
let local_key_file = format!("{}/.ssb/secret", home_dir.to_string_lossy());
|
let local_key_file = format!("{}/.ssb/secret", home_dir.to_string_lossy());
|
||||||
let content = std::fs::read_to_string(local_key_file)?;
|
let mut file = async_std::fs::File::open(local_key_file).await?;
|
||||||
Ok(from_patchwork_config(content)?)
|
read_patchwork_config(&mut file).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_patchwork_config<T: AsRef<str>>(config: T) -> Result<OwnedIdentity> {
|
pub async fn read_patchwork_config<R: Read + Unpin>(reader: &mut R) -> Result<OwnedIdentity> {
|
||||||
// strip all comments
|
let mut buf = String::new();
|
||||||
let json = config
|
reader.read_to_string(&mut buf).await?;
|
||||||
.as_ref()
|
|
||||||
|
let json = buf
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|line| !line.starts_with('#'))
|
.filter(|line| !line.starts_with('#'))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
// parse json
|
// parse json
|
||||||
let secret: JsonSSBSecret = serde_json::from_str(json.as_ref()).map_err(to_ioerr)?;
|
let secret: JsonSSBSecret = serde_json::from_str(json.as_ref()).map_err(to_io_error)?;
|
||||||
|
|
||||||
if secret.curve != CURVE_ED25519 {
|
if secret.curve != CURVE_ED25519 {
|
||||||
return Err(Error::InvalidConfig);
|
return Err(Error::InvalidConfig);
|
||||||
@ -49,3 +52,17 @@ pub fn from_patchwork_config<T: AsRef<str>>(config: T) -> Result<OwnedIdentity>
|
|||||||
sk: secret.private.to_ed25519_sk()?,
|
sk: secret.private.to_ed25519_sk()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn write_patchwork_config<W: Write + Unpin>(
|
||||||
|
id: &OwnedIdentity,
|
||||||
|
writer: &mut W,
|
||||||
|
) -> Result<()> {
|
||||||
|
let json = JsonSSBSecret {
|
||||||
|
id: id.id.clone(),
|
||||||
|
curve: CURVE_ED25519.to_owned(),
|
||||||
|
public: id.pk.to_ssb_id(),
|
||||||
|
private: id.sk.to_ssb_id(),
|
||||||
|
};
|
||||||
|
let encoded = to_vec_pretty(&json)?;
|
||||||
|
Ok(writer.write_all(&encoded).await?)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user