Merge pull request #92 from uqbar-dao/dr/register-refactor

Dr/register refactor
This commit is contained in:
dr-frmr 2023-12-09 16:32:50 -05:00 committed by GitHub
commit 9d1923ba2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 185 deletions

View File

@ -23,7 +23,7 @@ pub fn encode_keyfile(
password: String,
username: String,
routers: Vec<String>,
networking_key: Document,
networking_key: &[u8],
jwt: Vec<u8>,
file_key: Vec<u8>,
) -> Vec<u8> {
@ -49,9 +49,7 @@ pub fn encode_keyfile(
let jwt_nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let file_nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let keyciphertext: Vec<u8> = cipher
.encrypt(&network_nonce, networking_key.as_ref())
.unwrap();
let keyciphertext: Vec<u8> = cipher.encrypt(&network_nonce, networking_key).unwrap();
let jwtciphertext: Vec<u8> = cipher.encrypt(&jwt_nonce, jwt.as_ref()).unwrap();
let fileciphertext: Vec<u8> = cipher.encrypt(&file_nonce, file_key.as_ref()).unwrap();

View File

@ -66,10 +66,9 @@ async fn serve_register_fe(
// that updates their PKI info on-chain.
let (kill_tx, kill_rx) = oneshot::channel::<bool>();
let disk_keyfile = match fs::read(format!("{}/.keys", home_directory_path)).await {
Ok(keyfile) => keyfile,
Err(_) => Vec::new(),
};
let disk_keyfile: Option<Vec<u8>> = fs::read(format!("{}/.keys", home_directory_path))
.await
.ok();
let (tx, mut rx) = mpsc::channel::<(Identity, Keyfile, Vec<u8>)>(1);
let (our, decoded_keyfile, encoded_keyfile) = tokio::select! {

View File

@ -1,15 +1,13 @@
use aes_gcm::aead::KeyInit;
use ethers::prelude::{abigen, namehash, Address as EthAddress, Provider, U256};
use ethers_providers::Ws;
use hmac::Hmac;
use jwt::SignWithKey;
use ring::pkcs8::Document;
use ring::rand::SystemRandom;
use ring::signature;
use ring::signature::KeyPair;
use sha2::Sha256;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot};
use warp::{
http::{
@ -101,16 +99,34 @@ pub async fn register(
ip: String,
port: u16,
rpc_url: String,
keyfile: Vec<u8>,
keyfile: Option<Vec<u8>>,
) {
let our_temp_arc = Arc::new(Mutex::new(None)); // Networking info is generated and passed to the UI, but not used until confirmed
let our_ws_info = our_temp_arc.clone();
// Networking info is generated and passed to the UI, but not used until confirmed
let (public_key, serialized_networking_keypair) = keygen::generate_networking_key();
let net_keypair = Arc::new(serialized_networking_keypair.as_ref().to_vec());
let tx = Arc::new(tx);
let net_keypair_arc = Arc::new(Mutex::new(None));
let net_keypair_ws_info = net_keypair_arc.clone();
// TODO: if IP is localhost, don't allow registration as direct
let ws_port = crate::http::utils::find_open_port(9000).await.unwrap();
let keyfile_arc = Arc::new(Mutex::new(Some(keyfile)));
let keyfile_vet = keyfile_arc.clone();
// This is a temporary identity, passed to the UI. If it is confirmed through a /boot or /confirm-change-network-keys, then it will be used to replace the current identity
let our_temp_id = Arc::new(Identity {
networking_key: format!("0x{}", public_key),
name: "".to_string(),
ws_routing: Some((ip.clone(), ws_port)),
allowed_routers: vec![
"uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(),
"uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(),
"uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(),
],
});
let keyfile = warp::any().map(move || keyfile.clone());
let our_temp_id = warp::any().map(move || our_temp_id.clone());
let net_keypair = warp::any().map(move || net_keypair.clone());
let tx = warp::any().map(move || tx.clone());
let ip = warp::any().map(move || ip.clone());
let rpc_url = warp::any().map(move || rpc_url.clone());
let static_files = warp::path("static").and(warp::fs::dir("./src/register-ui/build/static/"));
@ -118,73 +134,60 @@ pub async fn register(
.and(warp::get())
.and(warp::fs::file("./src/register-ui/build/index.html"));
let keyfile_info_copy = keyfile_arc.clone();
let boot_tx = tx.clone();
let boot_our_arc = our_temp_arc.clone();
let boot_net_keypair_arc = net_keypair_arc.clone();
let import_tx = tx.clone();
let import_ip = ip.clone();
let import_rpc_url = rpc_url.clone();
let login_tx = tx.clone();
let login_keyfile_arc = keyfile_arc.clone();
let generate_keys_ip = ip.clone();
let api = warp::path("info")
.and(
warp::get()
.and(warp::any().map(move || keyfile_info_copy.clone()))
.and(keyfile.clone())
.and_then(get_unencrypted_info),
)
.or(warp::path("generate-networking-info").and(
warp::post()
.and(warp::any().map(move || generate_keys_ip.clone()))
.and(warp::any().map(move || our_ws_info.clone()))
.and(warp::any().map(move || net_keypair_ws_info.clone()))
.and(our_temp_id.clone())
.and_then(generate_networking_info),
))
.or(warp::path("vet-keyfile").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || keyfile_vet.clone()))
.and(keyfile.clone())
.and_then(handle_keyfile_vet),
))
.or(warp::path("boot").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || boot_tx.clone()))
.and(warp::any().map(move || boot_our_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || boot_net_keypair_arc.lock().unwrap().take().unwrap()))
.and(tx.clone())
.and(our_temp_id.clone())
.and(net_keypair.clone())
.and_then(handle_boot),
))
.or(warp::path("import-keyfile").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || import_ip.clone()))
.and(warp::any().map(move || import_rpc_url.clone()))
.and(warp::any().map(move || import_tx.clone()))
.and(ip.clone())
.and(rpc_url.clone())
.and(tx.clone())
.and_then(handle_import_keyfile),
))
.or(warp::path("login").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || ip.clone()))
.and(warp::any().map(move || rpc_url.clone()))
.and(warp::any().map(move || login_tx.clone()))
.and(warp::any().map(move || login_keyfile_arc.lock().unwrap().take().unwrap()))
.and(ip)
.and(rpc_url)
.and(tx.clone())
.and(keyfile.clone())
.and_then(handle_login),
))
.or(warp::path("confirm-change-network-keys").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || tx.clone()))
.and(warp::any().map(move || our_temp_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || net_keypair_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || keyfile_arc.lock().unwrap().take().unwrap()))
.and(tx)
.and(our_temp_id)
.and(net_keypair)
.and(keyfile)
.and_then(confirm_change_network_keys),
));
@ -208,16 +211,14 @@ pub async fn register(
.await;
}
async fn get_unencrypted_info(
keyfile_arc: Arc<Mutex<Option<Vec<u8>>>>,
) -> Result<impl Reply, Rejection> {
async fn get_unencrypted_info(keyfile: Option<Vec<u8>>) -> Result<impl Reply, Rejection> {
let (name, allowed_routers) = {
match keyfile_arc.lock().unwrap().clone() {
match keyfile {
Some(encoded_keyfile) => match keygen::get_username_and_routers(&encoded_keyfile) {
Ok(k) => k,
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile".to_string()),
warp::reply::json(&"Failed to decode keyfile"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
@ -225,84 +226,57 @@ async fn get_unencrypted_info(
},
None => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response())
}
}
};
let our = UnencryptedIdentity {
Ok(warp::reply::with_status(
Ok(warp::reply::json(&UnencryptedIdentity {
name,
allowed_routers,
};
Ok(warp::reply::with_status(Ok(warp::reply::json(&our)), StatusCode::OK).into_response())
})),
StatusCode::OK,
)
.into_response())
}
async fn generate_networking_info(
ip: String,
our_temp_arc: Arc<Mutex<Option<Identity>>>,
networking_keypair_arc: Arc<Mutex<Option<Document>>>,
) -> Result<impl Reply, Rejection> {
let (public_key, serialized_networking_keypair) = keygen::generate_networking_key();
*networking_keypair_arc.lock().unwrap() = Some(serialized_networking_keypair);
// TODO: if IP is localhost, don't allow registration as direct
let ws_port = crate::http::utils::find_open_port(9000).await.unwrap();
// This is a temporary identity, passed to the UI. If it is confirmed through a /boot or /confirm-change-network-keys, then it will be used to replace the current identity
let our_temp = Identity {
networking_key: format!("0x{}", public_key),
name: "".to_string(),
ws_routing: Some((ip, ws_port)),
allowed_routers: vec![
"uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(),
"uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(),
"uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(),
],
};
*our_temp_arc.lock().unwrap() = Some(our_temp.clone());
Ok(warp::reply::json(&our_temp))
async fn generate_networking_info(our_temp_id: Arc<Identity>) -> Result<impl Reply, Rejection> {
Ok(warp::reply::json(our_temp_id.as_ref()))
}
async fn handle_keyfile_vet(
payload: KeyfileVet,
keyfile_arc: Arc<Mutex<Option<Vec<u8>>>>,
keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
let encoded_keyfile = match payload.keyfile.is_empty() {
true => keyfile_arc.lock().unwrap().clone().unwrap(),
false => base64::decode(payload.keyfile).unwrap(),
true => keyfile.ok_or(warp::reject())?,
false => base64::decode(payload.keyfile).map_err(|_| warp::reject())?,
};
let decoded_keyfile = match keygen::decode_keyfile(&encoded_keyfile, &payload.password) {
Ok(k) => k,
Err(_) => return Err(warp::reject()),
};
let decoded_keyfile =
keygen::decode_keyfile(&encoded_keyfile, &payload.password).map_err(|_| warp::reject())?;
let keyfile_vetted = KeyfileVetted {
Ok(warp::reply::json(&KeyfileVetted {
username: decoded_keyfile.username,
networking_key: format!(
"0x{}",
hex::encode(decoded_keyfile.networking_keypair.public_key().as_ref())
),
routers: decoded_keyfile.routers,
};
Ok(warp::reply::json(&keyfile_vetted))
}))
}
async fn handle_boot(
info: BootInfo,
sender: RegistrationSender,
mut our: Identity,
networking_keypair: Document,
sender: Arc<RegistrationSender>,
our: Arc<Identity>,
networking_keypair: Arc<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
let mut our = our.as_ref().clone();
our.name = info.username;
if info.direct {
our.allowed_routers = vec![];
} else {
@ -326,35 +300,26 @@ async fn handle_boot(
info.password,
decoded_keyfile.username.clone(),
decoded_keyfile.routers.clone(),
networking_keypair,
networking_keypair.as_ref(),
decoded_keyfile.jwt_secret_bytes.clone(),
decoded_keyfile.file_key.clone(),
);
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn handle_import_keyfile(
info: ImportKeyfileInfo,
ip: String,
_rpc_url: String,
sender: RegistrationSender,
sender: Arc<RegistrationSender>,
) -> Result<impl Reply, Rejection> {
// if keyfile was not present in node and is present from user upload
let encoded_keyfile = match base64::decode(info.keyfile.clone()) {
Ok(k) => k,
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not valid base64".to_string()),
warp::reply::json(&"Keyfile not valid base64"),
StatusCode::BAD_REQUEST,
)
.into_response())
@ -363,7 +328,7 @@ async fn handle_import_keyfile(
let Some(ws_port) = crate::http::utils::find_open_port(9000).await else {
return Ok(warp::reply::with_status(
warp::reply::json(&"Unable to find free port".to_string()),
warp::reply::json(&"Unable to find free port"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response());
@ -404,36 +369,28 @@ async fn handle_import_keyfile(
// .into_response());
// }
let encoded_keyfile_str = info.keyfile.clone();
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn handle_login(
info: LoginInfo,
ip: String,
_rpc_url: String,
sender: RegistrationSender,
encoded_keyfile: Vec<u8>,
sender: Arc<RegistrationSender>,
encoded_keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
if encoded_keyfile.is_empty() {
if encoded_keyfile.is_none() {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response());
}
let encoded_keyfile = encoded_keyfile.unwrap();
let Some(ws_port) = crate::http::utils::find_open_port(9000).await else {
return Ok(warp::reply::with_status(
warp::reply::json(&"Unable to find free port".to_string()),
warp::reply::json(&"Unable to find free port"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response());
@ -459,47 +416,32 @@ async fn handle_login(
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile".to_string()),
warp::reply::json(&"Failed to decode keyfile"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
};
// if !networking_info_valid(rpc_url, ip, ws_port, &our).await {
// return Ok(warp::reply::with_status(
// warp::reply::json(&"Networking info invalid".to_string()),
// StatusCode::UNAUTHORIZED,
// )
// .into_response());
// }
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn confirm_change_network_keys(
info: LoginAndResetInfo,
sender: RegistrationSender,
mut our: Identity, // the arc of our temporary identity
networking_keypair: Document,
encoded_keyfile: Vec<u8>,
sender: Arc<RegistrationSender>,
our: Arc<Identity>,
networking_keypair: Arc<Vec<u8>>,
encoded_keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
if encoded_keyfile.is_empty() {
if encoded_keyfile.is_none() {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response());
}
let encoded_keyfile = encoded_keyfile.unwrap();
let mut our = our.as_ref().clone();
// Get our name from our current keyfile
let old_decoded_keyfile = match keygen::decode_keyfile(&encoded_keyfile, &info.password) {
@ -509,7 +451,7 @@ async fn confirm_change_network_keys(
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Invalid password".to_string()),
warp::reply::json(&"Invalid password"),
StatusCode::UNAUTHORIZED,
)
.into_response());
@ -536,35 +478,26 @@ async fn confirm_change_network_keys(
info.password,
decoded_keyfile.username.clone(),
decoded_keyfile.routers.clone(),
networking_keypair,
networking_keypair.as_ref(),
decoded_keyfile.jwt_secret_bytes.clone(),
decoded_keyfile.file_key.clone(),
);
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our.clone(), decoded_keyfile, encoded_keyfile).await
}
async fn success_response(
sender: RegistrationSender,
sender: Arc<RegistrationSender>,
our: Identity,
decoded_keyfile: Keyfile,
encoded_keyfile: Vec<u8>,
encoded_keyfile_str: String,
) -> Result<warp::reply::Response, Rejection> {
let encoded_keyfile_str = base64::encode(&encoded_keyfile);
let token = match generate_jwt(&decoded_keyfile.jwt_secret_bytes, &our.name) {
Some(token) => token,
None => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate JWT".to_string()),
warp::reply::json(&"Failed to generate JWT"),
StatusCode::SERVICE_UNAVAILABLE,
)
.into_response())
@ -588,26 +521,13 @@ async fn success_response(
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate Auth JWT".to_string()),
warp::reply::json(&"Failed to generate Auth JWT"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
}
// match HeaderValue::from_str(&format!("uqbar-ws-auth_{}={};", &our.name, &token)) {
// Ok(v) => {
// headers.append(SET_COOKIE, v);
// },
// Err(_) => {
// return Ok(warp::reply::with_status(
// warp::reply::json(&"Failed to generate WS JWT".to_string()),
// StatusCode::INTERNAL_SERVER_ERROR,
// )
// .into_response())
// }
// }
Ok(response)
}