Refactored key-management a lot.

pull/2/head
Revertron 3 years ago
parent 4c4493f797
commit 364a969a53

@ -12,6 +12,7 @@ rust-crypto = "^0.2"
num_cpus = "1.13.0"
byteorder = "1.3.2"
web-view = { version = "0.7.2", features = [] }
tinyfiledialogs = "3.3.10"
serde = { version = "1.0.102", features = ["derive"] }
serde_json = "1.0.42"
num-bigint = "0.2"

@ -3,6 +3,7 @@ use crate::event::Event;
use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::Read;
use std::sync::MutexGuard;
pub struct Context {
pub settings: Settings,

@ -4,6 +4,9 @@ pub enum Event {
MinerStopped,
KeyGeneratorStarted,
KeyGeneratorStopped,
KeyCreated {path: String, public: String},
KeyLoaded {path: String, public: String},
KeySaved {path: String, public: String},
NewBlockReceived,
BlockchainChanged,
ActionStopMining,

@ -21,6 +21,8 @@ pub struct Keystore {
private_key: Bytes,
public_key: Bytes,
#[serde(skip)]
path: String,
#[serde(skip)]
seed: Vec<u8>
}
@ -30,18 +32,21 @@ impl Keystore {
let mut rng = thread_rng();
rng.fill(&mut buf);
let (private, public) = keypair(&buf);
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), seed: Vec::from(&buf[..])}
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(&buf[..])}
}
pub fn from_bytes(seed: &[u8]) -> Self {
let (private, public) = keypair(&seed);
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), seed: Vec::from(seed)}
Keystore {private_key: Bytes::from_bytes(&private), public_key: Bytes::from_bytes(&public), path: String::new(), seed: Vec::from(seed)}
}
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
match fs::read(&Path::new(filename)) {
let path = Path::new(filename);
match fs::read(&path) {
Ok(key) => {
Some(Self::from_bytes(key.as_slice()))
let mut keystore = Self::from_bytes(key.as_slice());
keystore.path = path.to_str().unwrap().to_owned();
Some(keystore)
},
Err(_) => {
None
@ -50,11 +55,12 @@ impl Keystore {
}
//TODO Implement error conditions
pub fn save(&self, filename: &str, _password: &str) {
pub fn save(&mut self, filename: &str, _password: &str) {
match File::create(Path::new(filename)) {
Ok(mut f) => {
//TODO implement key encryption
f.write_all(&self.seed).expect("Error saving keystore");
self.path = filename.to_owned();
}
Err(_) => { println!("Error saving key file!"); }
}
@ -68,6 +74,10 @@ impl Keystore {
self.private_key.clone()
}
pub fn get_path(&self) -> &str {
&self.path
}
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
signature(message, self.private_key.data.as_slice())
}
@ -120,6 +130,10 @@ impl Bytes {
self.data.as_slice()
}
pub fn to_string(&self) -> String {
crate::utils::to_hex(&self.data)
}
pub fn zero32() -> Self {
Bytes { data: [0u8; 32].to_vec() }
}

@ -1,8 +1,9 @@
#![windows_subsystem = "windows"]
extern crate web_view;
extern crate tinyfiledialogs as tfd;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize};
use std::thread;
use rand::RngCore;
@ -27,7 +28,10 @@ fn main() {
println!("ALFIS 0.1.0");
let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
None => {
println!("Generated temporary keystore. Please, generate full-privileged keys.");
Keystore::new()
}
Some(keystore) => { keystore }
};
let blockchain: Blockchain = Blockchain::new(&settings);
@ -81,36 +85,66 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
Loaded => {
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
let handle = web_view.handle();
let context_copy = context.clone();
let mut c = context.lock().unwrap();
c.bus.register(move |_uuid, e| {
println!("Got event from bus {:?}", &e);
let visible = match e {
Event::MinerStarted => { true }
Event::KeyGeneratorStarted => { true }
Event::MinerStopped => { false }
Event::KeyGeneratorStopped => { false }
_ => { false }
let eval = match e {
Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::MinerStarted => { format!("showMiningIndicator({});", true) }
Event::KeyGeneratorStarted => { format!("showMiningIndicator({});", true) }
Event::MinerStopped => { format!("showMiningIndicator({});", false) }
Event::KeyGeneratorStopped => { format!("showMiningIndicator({});", false) }
_ => { String::new() }
};
handle.dispatch(move |web_view| {
web_view.eval(&format!("showMiningIndicator({});", visible)).expect("Error evaluating!");
return WVResult::Ok(());
}).expect("Error dispatching!");
if !eval.is_empty() {
println!("Evaluating {}", &eval);
handle.dispatch(move |web_view| {
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
return WVResult::Ok(());
}).expect("Error dispatching!");
}
true
});
}
LoadKey { name, pass } => {
match Keystore::from_file(&name, &pass) {
None => {
println!("Error loading keystore '{}'!", &name);
},
Some(keystore) => {
let mut c = context.lock().unwrap();
c.set_keystore(keystore);
LoadKey {} => {
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key"], "*.key")));
match result {
None => {}
Some(file_name) => {
match Keystore::from_file(&file_name, "") {
None => {
println!("Error loading keystore '{}'!", &file_name);
},
Some(keystore) => {
println!("Loaded keystore with key: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
c.bus.post(Event::KeyLoaded {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
c.set_keystore(keystore);
}
}
}
}
},
CreateKey { name, pass } => {
create_key(context.clone(), &name, &pass);
CreateKey {} => {
create_key(context.clone());
}
SaveKey {} => {
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.key"], "Key files (*.key)");
match result {
None => {}
Some(new_path) => {
let mut c = context.lock().unwrap();
let path = new_path.clone();
let public = c.keystore.get_public().to_string();
c.keystore.save(&new_path, "");
println!("Key file saved to {}", &path);
c.bus.post(Event::KeySaved {path, public });
}
}
}
CheckDomain { name} => {
let c = context.lock().unwrap();
@ -170,28 +204,32 @@ fn create_transaction<S: Into<String>>(keystore: &Keystore, name: S, method: S,
transaction
}
fn create_key(context: Arc<Mutex<Context>>, filename: &str, password: &str) {
fn create_key(context: Arc<Mutex<Context>>) {
let mining = Arc::new(AtomicBool::new(true));
let miners_count = Arc::new(AtomicUsize::new(0));
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
for _ in 0..num_cpus::get() {
let context = context.clone();
let filename= filename.to_owned();
let password= password.to_owned();
let mining = mining.clone();
let miners_count = miners_count.clone();
thread::spawn(move || {
miners_count.fetch_add(1, Ordering::Relaxed);
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
None => {
println!("Keystore mining finished");
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
}
Some(keystore) => {
println!("Key mined successfully: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap();
mining.store(false,Ordering::Relaxed);
keystore.save(&filename, &password);
c.bus.post(Event::KeyCreated {path: keystore.get_path().to_owned(), public: keystore.get_public().to_string()});
c.set_keystore(keystore);
c.bus.post(Event::KeyGeneratorStopped);
}
}
let miners = miners_count.fetch_sub(1, Ordering::Relaxed) - 1;
if miners == 0 {
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
}
});
}
context.lock().unwrap().bus.register(move |_uuid, e| {
@ -222,8 +260,9 @@ fn generate_key(difficulty: usize, mining: Arc<AtomicBool>) -> Option<Keystore>
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {
Loaded,
LoadKey{name: String, pass: String},
CreateKey{name: String, pass: String},
LoadKey{},
CreateKey{},
SaveKey{},
CheckDomain{name: String},
CreateDomain{name: String, records: String, tags: String},
ChangeDomain{name: String, records: String, tags: String},

@ -10,6 +10,7 @@ use num_cpus;
use crate::{Block, Bytes, Context, hash_is_good, Transaction};
use crate::event::Event;
use std::ops::DerefMut;
pub struct Miner {
context: Arc<Mutex<Context>>,

@ -13,6 +13,7 @@ use mio::net::{TcpListener, TcpStream};
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
use std::ops::DerefMut;
const SERVER: Token = Token(0);
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));

@ -37,9 +37,7 @@
</ul>
<p class="menu-label">Key management</p>
<ul class="menu-list">
<li><a onclick="openTab(this, 'key_load')">Load key</a></li>
<li><a onclick="openTab(this, 'key_create')">Create key</a></li>
<li><a onclick="openTab(this, 'key_passwd')">Change password</a></li>
<li><a onclick="openTab(this, 'key_load')">Manage keys</a></li>
</ul>
<p class="menu-label">Domain management</p>
<ul class="menu-list">
@ -60,93 +58,33 @@
<div class="content is-hidden" id="key_load">
<form action="#">
<div class="field">
<label class="label">Key file name</label>
<div class="control">
<input class="input" type="text" placeholder="default.key" id="load_key_name">
</div>
<label class="label">Key path</label>
<p id="key_file_name">Key not saved</p>
</div>
<div class="field">
<label class="label">Key password</label>
<div class="control">
<input class="input" type="password" placeholder="123456" id="load_key_password">
</div>
<label class="label">Key public key</label>
<p id="key_public_key">00000050C9516C42946D38EDD6A2DB1279F6E73E9C6D2EA6769A5C2BA3E381D6</p>
</div>
<br>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" onclick="loadKey();">Load</button>
</div>
</div>
</form>
</div>
<div class="content is-hidden" id="key_create">
<form action="#">
<div class="field">
<label class="label">Key file name</label>
<div class="control">
<input class="input" type="text" placeholder="default.key" id="create_key_name">
<button class="button is-success" onclick="loadKey();">Load key</button>
</div>
</div>
<div class="field">
<label class="label">Key password</label>
<div class="control">
<input class="input" type="password" placeholder="123456" id="create_key_password">
<button class="button is-warning" onclick="createKey();">Mine new key</button>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" onclick="createKey();">Create</button>
<button class="button is-primary" onclick="saveKey();">Save key</button>
</div>
</div>
</form>
</div>
<div class="content is-hidden" id="key_passwd">
<form action="#">
<div class="field">
<label class="label">Key file name</label>
<div class="control">
<input class="input" type="text" placeholder="default.key" id="change_key_name">
</div>
</div>
<div class="field">
<label class="label">Old key password</label>
<div class="control">
<input class="input" type="password" placeholder="123456" id="change_key_password">
</div>
</div>
<div class="field">
<label class="label">New key password</label>
<div class="control">
<input class="input" type="password" placeholder="123456" id="change_key_password_new">
</div>
</div>
<div class="field">
<label class="label">Repeat key password</label>
<div class="control">
<input class="input" type="password" placeholder="123456" id="change_key_password_repeat">
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Change password</button>
</div>
</div>
</form>
</div>
<div class="content is-hidden" id="dom_new">
<form action="#">
<div class="field">

@ -24,15 +24,15 @@ function openTab(element, tabName) {
}
function loadKey() {
key_name = document.getElementById("load_key_name").value;
key_pass = document.getElementById("load_key_password").value;
external.invoke(JSON.stringify({cmd: 'loadKey', name: key_name, pass: key_pass}));
external.invoke(JSON.stringify({cmd: 'loadKey'}));
}
function createKey() {
key_name = document.getElementById("create_key_name").value;
key_pass = document.getElementById("create_key_password").value;
external.invoke(JSON.stringify({cmd: 'createKey', name: key_name, pass: key_pass}));
external.invoke(JSON.stringify({cmd: 'createKey'}));
}
function saveKey() {
external.invoke(JSON.stringify({cmd: 'saveKey'}));
}
function createDomain() {
@ -115,4 +115,14 @@ function miningIndicatorClick(element) {
showModalDialog("Do you really want to stop mining?", function() {
external.invoke(JSON.stringify({cmd: 'stopMining'}));
});
}
function keystoreChanged(path, pub_key) {
if (path == '') {
path = "In memory";
}
key_file_name = document.getElementById("key_file_name");
key_file_name.innerHTML = path;
key_file_key = document.getElementById("key_public_key");
key_file_key.innerHTML = pub_key;
}
Loading…
Cancel
Save