Refactored key-management a lot.

This commit is contained in:
Revertron
2021-02-15 23:09:30 +01:00
parent 4c4493f797
commit 364a969a53
9 changed files with 126 additions and 118 deletions
+1
View File
@@ -12,6 +12,7 @@ rust-crypto = "^0.2"
num_cpus = "1.13.0" num_cpus = "1.13.0"
byteorder = "1.3.2" byteorder = "1.3.2"
web-view = { version = "0.7.2", features = [] } web-view = { version = "0.7.2", features = [] }
tinyfiledialogs = "3.3.10"
serde = { version = "1.0.102", features = ["derive"] } serde = { version = "1.0.102", features = ["derive"] }
serde_json = "1.0.42" serde_json = "1.0.42"
num-bigint = "0.2" num-bigint = "0.2"
+1
View File
@@ -3,6 +3,7 @@ use crate::event::Event;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::sync::MutexGuard;
pub struct Context { pub struct Context {
pub settings: Settings, pub settings: Settings,
+3
View File
@@ -4,6 +4,9 @@ pub enum Event {
MinerStopped, MinerStopped,
KeyGeneratorStarted, KeyGeneratorStarted,
KeyGeneratorStopped, KeyGeneratorStopped,
KeyCreated {path: String, public: String},
KeyLoaded {path: String, public: String},
KeySaved {path: String, public: String},
NewBlockReceived, NewBlockReceived,
BlockchainChanged, BlockchainChanged,
ActionStopMining, ActionStopMining,
+19 -5
View File
@@ -21,6 +21,8 @@ pub struct Keystore {
private_key: Bytes, private_key: Bytes,
public_key: Bytes, public_key: Bytes,
#[serde(skip)] #[serde(skip)]
path: String,
#[serde(skip)]
seed: Vec<u8> seed: Vec<u8>
} }
@@ -30,18 +32,21 @@ impl Keystore {
let mut rng = thread_rng(); let mut rng = thread_rng();
rng.fill(&mut buf); rng.fill(&mut buf);
let (private, public) = keypair(&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 { pub fn from_bytes(seed: &[u8]) -> Self {
let (private, public) = keypair(&seed); 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> { 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) => { 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(_) => { Err(_) => {
None None
@@ -50,11 +55,12 @@ impl Keystore {
} }
//TODO Implement error conditions //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)) { match File::create(Path::new(filename)) {
Ok(mut f) => { Ok(mut f) => {
//TODO implement key encryption //TODO implement key encryption
f.write_all(&self.seed).expect("Error saving keystore"); f.write_all(&self.seed).expect("Error saving keystore");
self.path = filename.to_owned();
} }
Err(_) => { println!("Error saving key file!"); } Err(_) => { println!("Error saving key file!"); }
} }
@@ -68,6 +74,10 @@ impl Keystore {
self.private_key.clone() self.private_key.clone()
} }
pub fn get_path(&self) -> &str {
&self.path
}
pub fn sign(&self, message: &[u8]) -> [u8; 64] { pub fn sign(&self, message: &[u8]) -> [u8; 64] {
signature(message, self.private_key.data.as_slice()) signature(message, self.private_key.data.as_slice())
} }
@@ -120,6 +130,10 @@ impl Bytes {
self.data.as_slice() self.data.as_slice()
} }
pub fn to_string(&self) -> String {
crate::utils::to_hex(&self.data)
}
pub fn zero32() -> Self { pub fn zero32() -> Self {
Bytes { data: [0u8; 32].to_vec() } Bytes { data: [0u8; 32].to_vec() }
} }
+61 -22
View File
@@ -1,8 +1,9 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
extern crate web_view; extern crate web_view;
extern crate tinyfiledialogs as tfd;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize};
use std::thread; use std::thread;
use rand::RngCore; use rand::RngCore;
@@ -27,7 +28,10 @@ fn main() {
println!("ALFIS 0.1.0"); println!("ALFIS 0.1.0");
let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings"); let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") { 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 } Some(keystore) => { keystore }
}; };
let blockchain: Blockchain = Blockchain::new(&settings); let blockchain: Blockchain = Blockchain::new(&settings);
@@ -81,36 +85,66 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
Loaded => { Loaded => {
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!"); web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
let handle = web_view.handle(); let handle = web_view.handle();
let context_copy = context.clone();
let mut c = context.lock().unwrap(); let mut c = context.lock().unwrap();
c.bus.register(move |_uuid, e| { c.bus.register(move |_uuid, e| {
println!("Got event from bus {:?}", &e); println!("Got event from bus {:?}", &e);
let visible = match e { let eval = match e {
Event::MinerStarted => { true } Event::KeyCreated { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeyGeneratorStarted => { true } Event::KeyLoaded { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::MinerStopped => { false } Event::KeySaved { path, public } => { format!("keystoreChanged('{}', '{}');", &path, &public) }
Event::KeyGeneratorStopped => { false } Event::MinerStarted => { format!("showMiningIndicator({});", true) }
_ => { false } Event::KeyGeneratorStarted => { format!("showMiningIndicator({});", true) }
Event::MinerStopped => { format!("showMiningIndicator({});", false) }
Event::KeyGeneratorStopped => { format!("showMiningIndicator({});", false) }
_ => { String::new() }
}; };
if !eval.is_empty() {
println!("Evaluating {}", &eval);
handle.dispatch(move |web_view| { handle.dispatch(move |web_view| {
web_view.eval(&format!("showMiningIndicator({});", visible)).expect("Error evaluating!"); web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
return WVResult::Ok(()); return WVResult::Ok(());
}).expect("Error dispatching!"); }).expect("Error dispatching!");
}
true true
}); });
} }
LoadKey { name, pass } => { LoadKey {} => {
match Keystore::from_file(&name, &pass) { 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 => { None => {
println!("Error loading keystore '{}'!", &name); println!("Error loading keystore '{}'!", &file_name);
}, },
Some(keystore) => { Some(keystore) => {
println!("Loaded keystore with key: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap(); 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); c.set_keystore(keystore);
} }
} }
}
}
}, },
CreateKey { name, pass } => { CreateKey {} => {
create_key(context.clone(), &name, &pass); 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} => { CheckDomain { name} => {
let c = context.lock().unwrap(); let c = context.lock().unwrap();
@@ -170,28 +204,32 @@ fn create_transaction<S: Into<String>>(keystore: &Keystore, name: S, method: S,
transaction 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 mining = Arc::new(AtomicBool::new(true));
let miners_count = Arc::new(AtomicUsize::new(0));
{ context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); } { context.lock().unwrap().bus.post(Event::KeyGeneratorStarted); }
for _ in 0..num_cpus::get() { for _ in 0..num_cpus::get() {
let context = context.clone(); let context = context.clone();
let filename= filename.to_owned();
let password= password.to_owned();
let mining = mining.clone(); let mining = mining.clone();
let miners_count = miners_count.clone();
thread::spawn(move || { thread::spawn(move || {
miners_count.fetch_add(1, Ordering::Relaxed);
match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) { match generate_key(KEYSTORE_DIFFICULTY, mining.clone()) {
None => { None => {
println!("Keystore mining finished"); println!("Keystore mining finished");
context.lock().unwrap().bus.post(Event::KeyGeneratorStopped);
} }
Some(keystore) => { Some(keystore) => {
println!("Key mined successfully: {:?}", &keystore.get_public());
let mut c = context.lock().unwrap(); let mut c = context.lock().unwrap();
mining.store(false,Ordering::Relaxed); 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.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| { 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")] #[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd { pub enum Cmd {
Loaded, Loaded,
LoadKey{name: String, pass: String}, LoadKey{},
CreateKey{name: String, pass: String}, CreateKey{},
SaveKey{},
CheckDomain{name: String}, CheckDomain{name: String},
CreateDomain{name: String, records: String, tags: String}, CreateDomain{name: String, records: String, tags: String},
ChangeDomain{name: String, records: String, tags: String}, ChangeDomain{name: String, records: String, tags: String},
+1
View File
@@ -10,6 +10,7 @@ use num_cpus;
use crate::{Block, Bytes, Context, hash_is_good, Transaction}; use crate::{Block, Bytes, Context, hash_is_good, Transaction};
use crate::event::Event; use crate::event::Event;
use std::ops::DerefMut;
pub struct Miner { pub struct Miner {
context: Arc<Mutex<Context>>, context: Arc<Mutex<Context>>,
+1
View File
@@ -13,6 +13,7 @@ use mio::net::{TcpListener, TcpStream};
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers}; use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown}; use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
use std::ops::DerefMut;
const SERVER: Token = Token(0); const SERVER: Token = Token(0);
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000)); const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
+15 -77
View File
@@ -37,9 +37,7 @@
</ul> </ul>
<p class="menu-label">Key management</p> <p class="menu-label">Key management</p>
<ul class="menu-list"> <ul class="menu-list">
<li><a onclick="openTab(this, 'key_load')">Load key</a></li> <li><a onclick="openTab(this, 'key_load')">Manage keys</a></li>
<li><a onclick="openTab(this, 'key_create')">Create key</a></li>
<li><a onclick="openTab(this, 'key_passwd')">Change password</a></li>
</ul> </ul>
<p class="menu-label">Domain management</p> <p class="menu-label">Domain management</p>
<ul class="menu-list"> <ul class="menu-list">
@@ -60,93 +58,33 @@
<div class="content is-hidden" id="key_load"> <div class="content is-hidden" id="key_load">
<form action="#"> <form action="#">
<div class="field"> <div class="field">
<label class="label">Key file name</label> <label class="label">Key path</label>
<div class="control"> <p id="key_file_name">Key not saved</p>
<input class="input" type="text" placeholder="default.key" id="load_key_name">
</div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Key password</label> <label class="label">Key public key</label>
<div class="control"> <p id="key_public_key">00000050C9516C42946D38EDD6A2DB1279F6E73E9C6D2EA6769A5C2BA3E381D6</p>
<input class="input" type="password" placeholder="123456" id="load_key_password">
</div>
</div> </div>
<br>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-link" onclick="loadKey();">Load</button> <button class="button is-success" onclick="loadKey();">Load key</button>
</div>
<div class="control">
<button class="button is-warning" onclick="createKey();">Mine new key</button>
</div>
<div class="control">
<button class="button is-primary" onclick="saveKey();">Save key</button>
</div> </div>
</div> </div>
</form> </form>
</div> </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">
</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">
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" onclick="createKey();">Create</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"> <div class="content is-hidden" id="dom_new">
<form action="#"> <form action="#">
<div class="field"> <div class="field">
+16 -6
View File
@@ -24,15 +24,15 @@ function openTab(element, tabName) {
} }
function loadKey() { function loadKey() {
key_name = document.getElementById("load_key_name").value; external.invoke(JSON.stringify({cmd: 'loadKey'}));
key_pass = document.getElementById("load_key_password").value;
external.invoke(JSON.stringify({cmd: 'loadKey', name: key_name, pass: key_pass}));
} }
function createKey() { function createKey() {
key_name = document.getElementById("create_key_name").value; external.invoke(JSON.stringify({cmd: 'createKey'}));
key_pass = document.getElementById("create_key_password").value; }
external.invoke(JSON.stringify({cmd: 'createKey', name: key_name, pass: key_pass}));
function saveKey() {
external.invoke(JSON.stringify({cmd: 'saveKey'}));
} }
function createDomain() { function createDomain() {
@@ -116,3 +116,13 @@ function miningIndicatorClick(element) {
external.invoke(JSON.stringify({cmd: 'stopMining'})); 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;
}