encrypt/decrypt and db interaction
This commit is contained in:
parent
196510e9a2
commit
1e4e1c9a5f
109
src-tauri/Cargo.lock
generated
109
src-tauri/Cargo.lock
generated
@ -68,10 +68,12 @@ checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"netstat2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sodiumoxide",
|
||||
"sqlx",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tokio",
|
||||
@ -433,6 +435,30 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset 0.7.1",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.8"
|
||||
@ -697,7 +723,7 @@ version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"memoffset 0.6.5",
|
||||
"rustc_version 0.3.3",
|
||||
]
|
||||
|
||||
@ -1623,6 +1649,15 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@ -1696,6 +1731,20 @@ dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netstat2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0faa3f4ad230fd2bf2a5dad71476ecbaeaed904b3c7e7e5b1f266c415c03761f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
@ -1729,6 +1778,26 @@ dependencies = [
|
||||
"winrt-notification",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
@ -2372,6 +2441,29 @@ dependencies = [
|
||||
"cty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@ -3060,6 +3152,21 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.26.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ddf41e393a9133c81d5f0974195366bd57082deac6e0eb02ed39b8341c2bb6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "5.0.0"
|
||||
|
@ -21,6 +21,8 @@ tauri = { version = "1.0.5", features = ["api-all"] }
|
||||
sodiumoxide = "0.2.7"
|
||||
tokio = { version = ">=1.19", features = ["full"] }
|
||||
sqlx = { version = "0.6.2", features = ["sqlite", "runtime-tokio-rustls"] }
|
||||
netstat2 = "0.9.1"
|
||||
sysinfo = "0.26.8"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
|
@ -1,9 +1,9 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE credentials (
|
||||
access_key_id TEXT,
|
||||
secret_key BLOB, -- encrypted
|
||||
nonce BLOB,
|
||||
expires INTEGER
|
||||
access_key_id TEXT NOT NULL,
|
||||
secret_key_enc BLOB NOT NULL,
|
||||
salt BLOB NOT NULL,
|
||||
nonce BLOB NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE config (
|
||||
|
46
src-tauri/src/clientinfo.rs
Normal file
46
src-tauri/src/clientinfo.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo};
|
||||
use sysinfo::{System, SystemExt, Pid, ProcessExt};
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
|
||||
fn get_associated_pids(local_port: u16) -> Result<Vec<u32>, netstat2::error::Error> {
|
||||
let mut it = netstat2::iterate_sockets_info(
|
||||
AddressFamilyFlags::IPV4,
|
||||
ProtocolFlags::TCP
|
||||
)?;
|
||||
|
||||
for (i, item) in it.enumerate() {
|
||||
let sock_info = item?;
|
||||
let proto_info = match sock_info.protocol_socket_info {
|
||||
ProtocolSocketInfo::Tcp(tcp_info) => tcp_info,
|
||||
ProtocolSocketInfo::Udp(_) => {continue;}
|
||||
};
|
||||
|
||||
if proto_info.local_port == local_port
|
||||
&& proto_info.remote_port == 12345
|
||||
&& proto_info.local_addr == std::net::Ipv4Addr::LOCALHOST
|
||||
&& proto_info.remote_addr == std::net::Ipv4Addr::LOCALHOST
|
||||
{
|
||||
println!("PIDs associated with socket: {:?}", &sock_info.associated_pids);
|
||||
println!("Scanned {i} sockets");
|
||||
return Ok(sock_info.associated_pids)
|
||||
}
|
||||
}
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
|
||||
pub fn get_client_info(local_port: u16) -> Result<(), ClientInfoError> {
|
||||
let mut sys = System::new();
|
||||
for p in get_associated_pids(local_port)? {
|
||||
let pid = Pid::from(p as usize);
|
||||
sys.refresh_process(pid);
|
||||
let proc = sys.process(pid)
|
||||
.ok_or(ClientInfoError::PidNotFound)?;
|
||||
let path = proc.exe().to_string_lossy();
|
||||
println!("exe for requesting process: {path}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -2,6 +2,36 @@ use std::fmt::{Display, Formatter};
|
||||
use std::convert::From;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use sqlx::{
|
||||
error::Error as SqlxError,
|
||||
migrate::MigrateError,
|
||||
};
|
||||
|
||||
|
||||
// error during initial setup (primarily loading state from db)
|
||||
pub enum SetupError {
|
||||
InvalidRecord, // e.g. wrong size blob for nonce or salt
|
||||
DbError(SqlxError),
|
||||
}
|
||||
impl From<SqlxError> for SetupError {
|
||||
fn from(e: SqlxError) -> SetupError {
|
||||
SetupError::DbError(e)
|
||||
}
|
||||
}
|
||||
impl From<MigrateError> for SetupError {
|
||||
fn from (e: MigrateError) -> SetupError {
|
||||
SetupError::DbError(SqlxError::from(e))
|
||||
}
|
||||
}
|
||||
impl Display for SetupError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
SetupError::InvalidRecord => write!(f, "Malformed database record"),
|
||||
SetupError::DbError(e) => write!(f, "Error from database: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// error when attempting to tell a request handler whether to release or deny crednetials
|
||||
pub enum SendResponseError {
|
||||
@ -26,8 +56,8 @@ pub enum RequestError {
|
||||
InvalidUtf8,
|
||||
MalformedHttpRequest,
|
||||
RequestTooLarge,
|
||||
NoCredentials(GetCredentialsError),
|
||||
}
|
||||
|
||||
impl From<tokio::io::Error> for RequestError {
|
||||
fn from(e: std::io::Error) -> RequestError {
|
||||
RequestError::StreamIOError(e)
|
||||
@ -38,6 +68,11 @@ impl From<Utf8Error> for RequestError {
|
||||
RequestError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
impl From<GetCredentialsError> for RequestError {
|
||||
fn from (e: GetCredentialsError) -> RequestError {
|
||||
RequestError::NoCredentials(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RequestError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
|
||||
@ -47,6 +82,55 @@ impl Display for RequestError {
|
||||
InvalidUtf8 => write!(f, "Could not decode UTF-8 from bytestream"),
|
||||
MalformedHttpRequest => write!(f, "Maformed HTTP request"),
|
||||
RequestTooLarge => write!(f, "HTTP request too large"),
|
||||
NoCredentials(GetCredentialsError::Locked) => write!(f, "Recieved go-ahead but app is locked"),
|
||||
NoCredentials(GetCredentialsError::Empty) => write!(f, "Received go-ahead but no credentials are known"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum GetCredentialsError {
|
||||
Locked,
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
pub enum UnlockError {
|
||||
NotLocked,
|
||||
NoCredentials,
|
||||
BadPassphrase,
|
||||
InvalidUtf8, // Somehow we got invalid utf-8 even though decryption succeeded
|
||||
DbError(SqlxError),
|
||||
}
|
||||
impl From<SqlxError> for UnlockError {
|
||||
fn from (e: SqlxError) -> UnlockError {
|
||||
match e {
|
||||
SqlxError::RowNotFound => UnlockError::NoCredentials,
|
||||
_ => UnlockError::DbError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for UnlockError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
|
||||
use UnlockError::*;
|
||||
match self {
|
||||
NotLocked => write!(f, "App is not locked"),
|
||||
NoCredentials => write!(f, "No saved credentials were found"),
|
||||
BadPassphrase => write!(f, "Invalid passphrase"),
|
||||
InvalidUtf8 => write!(f, "Decrypted data was corrupted"),
|
||||
DbError(e) => write!(f, "Database error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Errors encountered while trying to figure out who's on the other end of a request
|
||||
pub enum ClientInfoError {
|
||||
PidNotFound,
|
||||
NetstatError(netstat2::error::Error),
|
||||
}
|
||||
impl From<netstat2::error::Error> for ClientInfoError {
|
||||
fn from(e: netstat2::error::Error) -> ClientInfoError {
|
||||
ClientInfoError::NetstatError(e)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use tauri::State;
|
||||
|
||||
use crate::state::AppState;
|
||||
use crate::storage;
|
||||
use crate::state::{AppState, Session};
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Serialize, Deserialize)]
|
||||
@ -33,9 +32,21 @@ pub fn respond(response: RequestResponse, app_state: State<'_, AppState>) -> Res
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn unlock(passphrase: String, app_state: State<'_, AppState>) -> bool {
|
||||
let root_credentials = storage::load(&passphrase);
|
||||
app_state.set_creds(root_credentials); // for now
|
||||
true
|
||||
pub async fn unlock(passphrase: String, app_state: State<'_, AppState>) -> Result<(), String> {
|
||||
app_state.decrypt(&passphrase)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn session_status(app_state: State<'_, AppState>) -> String {
|
||||
let session = app_state.session.read().unwrap();
|
||||
match *session {
|
||||
Session::Locked(_) => "locked".into(),
|
||||
Session::Unlocked(_) => "unlocked".into(),
|
||||
Session::Empty => "empty".into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
mod errors;
|
||||
mod clientinfo;
|
||||
mod ipc;
|
||||
mod state;
|
||||
mod server;
|
||||
@ -13,7 +14,7 @@ mod storage;
|
||||
|
||||
|
||||
fn main() {
|
||||
let initial_state = match state::AppState::new(state::SessionStatus::Locked, None) {
|
||||
let initial_state = match state::AppState::new() {
|
||||
Ok(state) => state,
|
||||
Err(e) => {eprintln!("{}", e); return;}
|
||||
};
|
||||
|
@ -45,6 +45,11 @@ async fn handle(mut stream: TcpStream, app_handle: AppHandle) -> Result<(), Requ
|
||||
let (chan_send, chan_recv) = oneshot::channel();
|
||||
let app_state = app_handle.state::<crate::state::AppState>();
|
||||
let request_id = app_state.register_request(chan_send);
|
||||
|
||||
if let std::net::SocketAddr::V4(addr) = stream.peer_addr()? {
|
||||
crate::clientinfo::get_client_info(addr.port());
|
||||
}
|
||||
|
||||
// Do we want to panic if this fails? Does that mean the frontend is dead?
|
||||
app_handle.emit_all("credentials-request", Request {id: request_id}).unwrap();
|
||||
|
||||
@ -77,7 +82,7 @@ async fn handle(mut stream: TcpStream, app_handle: AppHandle) -> Result<(), Requ
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let creds = app_state.get_creds_serialized();
|
||||
let creds = app_state.get_creds_serialized()?;
|
||||
|
||||
stream.write(b"\r\nContent-Length: ").await?;
|
||||
stream.write(creds.as_bytes().len().to_string().as_bytes()).await?;
|
||||
|
@ -4,6 +4,13 @@ use std::sync::RwLock;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use tokio::sync::oneshot::Sender;
|
||||
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions, sqlite::SqliteConnectOptions};
|
||||
use sodiumoxide::crypto::{
|
||||
pwhash,
|
||||
pwhash::Salt,
|
||||
secretbox,
|
||||
secretbox::{Nonce, Key}
|
||||
};
|
||||
use tauri::async_runtime as runtime;
|
||||
|
||||
use crate::ipc;
|
||||
use crate::errors::*;
|
||||
@ -26,54 +33,98 @@ pub enum Credentials {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum SessionStatus {
|
||||
Unlocked,
|
||||
Locked,
|
||||
Empty,
|
||||
pub struct LockedCredentials {
|
||||
access_key_id: String,
|
||||
secret_key_enc: Vec<u8>,
|
||||
salt: Salt,
|
||||
nonce: Nonce,
|
||||
}
|
||||
|
||||
|
||||
pub enum Session {
|
||||
Unlocked(Credentials),
|
||||
Locked(LockedCredentials),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
// #[derive(Serialize, Deserialize)]
|
||||
// pub enum SessionStatus {
|
||||
// Unlocked,
|
||||
// Locked,
|
||||
// Empty,
|
||||
// }
|
||||
|
||||
|
||||
pub struct AppState {
|
||||
status: RwLock<SessionStatus>,
|
||||
credentials: RwLock<Option<Credentials>>,
|
||||
request_count: RwLock<u64>,
|
||||
open_requests: RwLock<HashMap<u64, Sender<ipc::Approval>>>,
|
||||
pub session: RwLock<Session>,
|
||||
pub request_count: RwLock<u64>,
|
||||
pub open_requests: RwLock<HashMap<u64, Sender<ipc::Approval>>>,
|
||||
pool: SqlitePool,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(status: SessionStatus, creds: Option<Credentials>) -> Result<Self, sqlx::error::Error> {
|
||||
pub fn new() -> Result<Self, SetupError> {
|
||||
let conn_opts = SqliteConnectOptions::new()
|
||||
.filename("creddy.db")
|
||||
.create_if_missing(true);
|
||||
let pool_opts = SqlitePoolOptions::new();
|
||||
|
||||
let pool: SqlitePool = tauri::async_runtime::block_on(pool_opts.connect_with(conn_opts))?;
|
||||
tauri::async_runtime::block_on(sqlx::migrate!().run(&pool))?;
|
||||
let pool: SqlitePool = runtime::block_on(pool_opts.connect_with(conn_opts))?;
|
||||
runtime::block_on(sqlx::migrate!().run(&pool))?;
|
||||
let creds = runtime::block_on(Self::load_creds(&pool))?;
|
||||
|
||||
let state = AppState {
|
||||
status: RwLock::new(status),
|
||||
credentials: RwLock::new(creds),
|
||||
session: RwLock::new(creds),
|
||||
request_count: RwLock::new(0),
|
||||
open_requests: RwLock::new(HashMap::new()),
|
||||
pool,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
async fn _load_from_db(&self) -> Result<(), sqlx::error::Error> {
|
||||
let row: (i32,) = sqlx::query_as("SELECT COUNT(*) FROM credentials")
|
||||
.fetch_one(&self.pool)
|
||||
async fn load_creds(pool: &SqlitePool) -> Result<Session, SetupError> {
|
||||
let res = sqlx::query!("SELECT * FROM credentials")
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
let row = match res {
|
||||
Some(r) => r,
|
||||
None => {return Ok(Session::Empty);}
|
||||
};
|
||||
|
||||
let mut status = self.status.write().unwrap();
|
||||
if row.0 > 0 {
|
||||
*status = SessionStatus::Locked;
|
||||
}
|
||||
else {
|
||||
*status = SessionStatus::Empty;
|
||||
}
|
||||
let salt_buf: [u8; 32] = row.salt
|
||||
.try_into()
|
||||
.map_err(|_e| SetupError::InvalidRecord)?;
|
||||
let nonce_buf: [u8; 24] = row.nonce
|
||||
.try_into()
|
||||
.map_err(|_e| SetupError::InvalidRecord)?;
|
||||
|
||||
let creds = LockedCredentials {
|
||||
access_key_id: row.access_key_id,
|
||||
secret_key_enc: row.secret_key_enc,
|
||||
salt: Salt(salt_buf),
|
||||
nonce: Nonce(nonce_buf),
|
||||
};
|
||||
Ok(Session::Locked(creds))
|
||||
}
|
||||
|
||||
pub async fn save_creds(&self, creds: Credentials, passphrase: &str) -> Result<(), sqlx::error::Error> {
|
||||
let (key_id, secret_key) = match creds {
|
||||
Credentials::LongLived {access_key_id, secret_access_key} => {
|
||||
(access_key_id, secret_access_key)
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let salt = pwhash::gen_salt();
|
||||
let mut key_buf = [0; secretbox::KEYBYTES];
|
||||
pwhash::derive_key_interactive(&mut key_buf, passphrase.as_bytes(), &salt).unwrap();
|
||||
let key = Key(key_buf);
|
||||
// not sure we need both salt AND nonce given that we generate a
|
||||
// fresh salt every time we encrypt, but better safe than sorry
|
||||
let nonce = secretbox::gen_nonce();
|
||||
let key_enc = secretbox::seal(secret_key.as_bytes(), &nonce, &key);
|
||||
// insert into database
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -85,7 +136,7 @@ impl AppState {
|
||||
};
|
||||
|
||||
let mut open_requests = self.open_requests.write().unwrap();
|
||||
open_requests.insert(*count, chan);
|
||||
open_requests.insert(*count, chan); // `count` is the request id
|
||||
*count
|
||||
}
|
||||
|
||||
@ -100,17 +151,36 @@ impl AppState {
|
||||
.map_err(|_e| SendResponseError::Abandoned)
|
||||
}
|
||||
|
||||
pub fn set_creds(&self, new_creds: Credentials) {
|
||||
let mut current_creds = self.credentials.write().unwrap();
|
||||
*current_creds = Some(new_creds);
|
||||
let mut status = self.status.write().unwrap();
|
||||
*status = SessionStatus::Unlocked;
|
||||
pub async fn decrypt(&self, passphrase: &str) -> Result<(), UnlockError> {
|
||||
let session = self.session.read().unwrap();
|
||||
let locked = match *session {
|
||||
Session::Empty => {return Err(UnlockError::NoCredentials);},
|
||||
Session::Unlocked(_) => {return Err(UnlockError::NotLocked);},
|
||||
Session::Locked(ref c) => c,
|
||||
};
|
||||
|
||||
let mut key_buf = [0; secretbox::KEYBYTES];
|
||||
// pretty sure this only fails if we're out of memory
|
||||
pwhash::derive_key_interactive(&mut key_buf, passphrase.as_bytes(), &locked.salt).unwrap();
|
||||
let decrypted = secretbox::open(&locked.secret_key_enc, &locked.nonce, &Key(key_buf))
|
||||
.map_err(|_e| UnlockError::BadPassphrase)?;
|
||||
|
||||
let secret_str = String::from_utf8(decrypted).map_err(|_e| UnlockError::InvalidUtf8)?;
|
||||
let mut session = self.session.write().unwrap();
|
||||
let creds = Credentials::LongLived {
|
||||
access_key_id: locked.access_key_id.clone(),
|
||||
secret_access_key: secret_str,
|
||||
};
|
||||
*session = Session::Unlocked(creds);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_creds_serialized(&self) -> String {
|
||||
let creds_option = self.credentials.read().unwrap();
|
||||
// fix this at some point
|
||||
let creds = creds_option.as_ref().unwrap();
|
||||
serde_json::to_string(creds).unwrap()
|
||||
pub fn get_creds_serialized(&self) -> Result<String, GetCredentialsError> {
|
||||
let session = self.session.read().unwrap();
|
||||
match *session {
|
||||
Session::Unlocked(ref creds) => Ok(serde_json::to_string(creds).unwrap()),
|
||||
Session::Locked(_) => Err(GetCredentialsError::Locked),
|
||||
Session::Empty => Err(GetCredentialsError::Empty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user