Feat (sealing): Simple Password sealing
Password is generated on first startup. The password given to the user is not same as the one used to encrypt secrets
This commit is contained in:
parent
4d342e8b99
commit
88ed714e22
9 changed files with 220 additions and 60 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -7,6 +7,5 @@
|
||||||
*.pdf
|
*.pdf
|
||||||
target/
|
target/
|
||||||
go_client/openapi.json
|
go_client/openapi.json
|
||||||
crates/storage-sled/sled_db
|
|
||||||
test.db
|
*.db*
|
||||||
src/storage/database.db
|
|
||||||
|
|
|
||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1478,6 +1478,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-siv",
|
"aes-gcm-siv",
|
||||||
"axum",
|
"axum",
|
||||||
|
"base64",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ json-patch = "4.0.0"
|
||||||
# serde_with = "3.8.1"
|
# serde_with = "3.8.1"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
aes-gcm-siv = "0.11.1"
|
aes-gcm-siv = "0.11.1"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
|
||||||
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
||||||
sqlx = { version = "0.8.3", features = [
|
sqlx = { version = "0.8.3", features = [
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@
|
||||||
CREATE TABLE root_key (
|
CREATE TABLE root_key (
|
||||||
version INTEGER PRIMARY KEY CHECK ( version = 1 ),
|
version INTEGER PRIMARY KEY CHECK ( version = 1 ),
|
||||||
encrypted_key BLOB NOT NULL,
|
encrypted_key BLOB NOT NULL,
|
||||||
type TEXT NOT NULL CHECK ( type IN ('dev_only') )
|
type TEXT NOT NULL CHECK ( type IN ('dev_only', 'simple') )
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::structs::KvV2WriteRequest;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::HttpError, engines::{
|
common::HttpError, engines::{
|
||||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
||||||
}, storage::sealing::{seal, unseal}, DbPool
|
}, storage::sealing::{encrypt, decrypt}, DbPool
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
|
|
@ -35,7 +35,7 @@ struct SecretDataInternal {
|
||||||
|
|
||||||
impl SecretDataInternal {
|
impl SecretDataInternal {
|
||||||
pub async fn into_external(self) -> KvSecretData {
|
pub async fn into_external(self) -> KvSecretData {
|
||||||
let secret = unseal(self.nonce, &self.encrypted_data).await;
|
let secret = decrypt(self.nonce, &self.encrypted_data).await;
|
||||||
KvSecretData {
|
KvSecretData {
|
||||||
created_time: self.created_time,
|
created_time: self.created_time,
|
||||||
deletion_time: self.deletion_time,
|
deletion_time: self.deletion_time,
|
||||||
|
|
@ -53,9 +53,7 @@ pub async fn get_data(
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, ()> {
|
) -> Result<Response, ()> {
|
||||||
debug!(
|
debug!(
|
||||||
"Get request: Engine: {}, path: {}",
|
"Get request: Engine: {engine_path}, path: {path}",
|
||||||
engine_path,
|
|
||||||
path,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = if params.version != 0 {
|
let res = if params.version != 0 {
|
||||||
|
|
@ -89,13 +87,13 @@ pub async fn get_data(
|
||||||
version: Some(secret_content.version_number),
|
version: Some(secret_content.version_number),
|
||||||
};
|
};
|
||||||
let return_secret = Json(return_secret);
|
let return_secret = Json(return_secret);
|
||||||
info!("{:?}", return_secret);
|
info!("{return_secret:?}");
|
||||||
|
|
||||||
Ok(return_secret.into_response())
|
Ok(return_secret.into_response())
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
sqlx::Error::RowNotFound => {
|
sqlx::Error::RowNotFound => {
|
||||||
warn!("Secret not found (could be correct behavior) {:?}", e);
|
warn!("Secret not found (could be correct behavior) {e:?}");
|
||||||
Ok(HttpError::simple(
|
Ok(HttpError::simple(
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
"Secret not found within kv2 engine",
|
"Secret not found within kv2 engine",
|
||||||
|
|
@ -125,7 +123,7 @@ pub async fn post_data(
|
||||||
|
|
||||||
let content = serde_json::to_string(&secret.data).unwrap();
|
let content = serde_json::to_string(&secret.data).unwrap();
|
||||||
|
|
||||||
let (nonce, enc) = seal(&content).await;
|
let (nonce, enc) = encrypt(&content).await;
|
||||||
let nonce = nonce.as_slice();
|
let nonce = nonce.as_slice();
|
||||||
|
|
||||||
let mut tx = pool.begin().await.unwrap();
|
let mut tx = pool.begin().await.unwrap();
|
||||||
|
|
@ -169,7 +167,7 @@ pub async fn delete_data(
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, Response> {
|
) -> Result<Response, Response> {
|
||||||
debug!("Secret: {}, path: {}", path, path);
|
debug!("Secret: {path}, path: {path}");
|
||||||
|
|
||||||
let del_time = UtcDateTime::now().unix_timestamp();
|
let del_time = UtcDateTime::now().unix_timestamp();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,11 @@ async fn main() {
|
||||||
.layer(middleware::from_fn(set_default_content_type_json))
|
.layer(middleware::from_fn(set_default_content_type_json))
|
||||||
.with_state(pool.clone());
|
.with_state(pool.clone());
|
||||||
|
|
||||||
storage::sealing::get_or_init_root_key(&pool).await;
|
if !storage::sealing::prepare_unseal(&pool).await {
|
||||||
|
// storage::sealing::init_insecure_in_db(&pool).await;
|
||||||
|
let user_key = storage::sealing::simple::init_simple(&pool).await;
|
||||||
|
warn!("New sealing password generated: {user_key}");
|
||||||
|
}
|
||||||
|
|
||||||
warn!("Listening on {}", listen_addr.to_string());
|
warn!("Listening on {}", listen_addr.to_string());
|
||||||
// Start listening
|
// Start listening
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
pub mod simple;
|
||||||
|
|
||||||
use aes_gcm_siv::{
|
use aes_gcm_siv::{
|
||||||
aead::{Aead, OsRng}, AeadCore, Aes256GcmSiv, KeyInit
|
AeadCore, Aes256GcmSiv, KeyInit,
|
||||||
|
aead::{Aead, OsRng},
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use log::{error, info, warn};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use super::DbPool;
|
use super::DbPool;
|
||||||
|
|
@ -10,89 +13,171 @@ use super::DbPool;
|
||||||
enum KeyEnum {
|
enum KeyEnum {
|
||||||
/// Final key
|
/// Final key
|
||||||
MainKey(Vec<u8>),
|
MainKey(Vec<u8>),
|
||||||
|
/// Encrypted with single secret
|
||||||
|
Simple(Vec<u8>),
|
||||||
|
/// Unknown or not initialized
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
// Portion(Vec<String>),
|
// ShamirPortion(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
static KEY_MAPS: RwLock<KeyEnum> = RwLock::const_new(KeyEnum::Uninitialized);
|
static ROOT_KEY_MAYBE: RwLock<KeyEnum> = RwLock::const_new(KeyEnum::Uninitialized);
|
||||||
|
|
||||||
pub async fn get_or_init_root_key(pool: &DbPool) {
|
struct ProtectedRK {
|
||||||
let mut value = KEY_MAPS.write().await;
|
pub protection_type: String,
|
||||||
|
pub encrypted_key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
match *value {
|
/// Returns `true` if vault is initialized or unsealed.
|
||||||
KeyEnum::MainKey(_) => panic!("Root key already initialized!"),
|
/// Returns `false` if uninitialized (nothing in the database).
|
||||||
KeyEnum::Uninitialized => get_root_key_db(pool, &mut value).await,
|
pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
||||||
|
{
|
||||||
|
if !matches!(*ROOT_KEY_MAYBE.read().await, KeyEnum::Uninitialized) {
|
||||||
|
info!("Vault unseal is already prepared");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Write lock on KEY_MAPS is hold throughout the process and set at last
|
|
||||||
// to avoid secrets being sealed with a new but discarded key
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_root_key_db(pool: &DbPool, value: &mut KeyEnum) {
|
let lock = ROOT_KEY_MAYBE.write(); // Not awaited just here
|
||||||
let rk = sqlx::query!("SELECT encrypted_key, type FROM root_key ORDER BY version LIMIT 1")
|
|
||||||
.fetch_optional(pool)
|
let rk = sqlx::query_as!(
|
||||||
.await
|
ProtectedRK,
|
||||||
.expect("Failed to optionally read root key from the database");
|
"SELECT encrypted_key, type as protection_type FROM root_key ORDER BY version LIMIT 1"
|
||||||
|
)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to optionally read root key from the database");
|
||||||
|
|
||||||
let v = match rk {
|
let v = match rk {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
warn!("No root key was found in the database!");
|
warn!("No root key was found in the database!");
|
||||||
init_new_root_key(pool, value).await;
|
return false;
|
||||||
return;
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
info!("Root key of type {} found in the database", v.r#type);
|
info!(
|
||||||
match &*v.r#type {
|
"Root key of type {} found in the database",
|
||||||
|
v.protection_type
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut lock = lock.await;
|
||||||
|
match &*v.protection_type {
|
||||||
"dev_only" => {
|
"dev_only" => {
|
||||||
warn!("Root key is of type {}. This is INSECURE and must only be used for development purposes!", v.r#type);
|
warn!(
|
||||||
*value = KeyEnum::MainKey(v.encrypted_key);
|
"Root key is of type {}. This is INSECURE and must only be used for development purposes!",
|
||||||
return;
|
v.protection_type
|
||||||
},
|
);
|
||||||
|
*lock = KeyEnum::MainKey(v.encrypted_key);
|
||||||
|
}
|
||||||
|
"simple" => {
|
||||||
|
*lock = KeyEnum::Simple(v.encrypted_key);
|
||||||
|
}
|
||||||
_ => panic!("Unknown root key type in database"),
|
_ => panic!("Unknown root key type in database"),
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init_new_root_key(pool: &DbPool, value: &mut KeyEnum) {
|
/// Must NOT be used in production.
|
||||||
warn!("Initializing new root key!");
|
/// Token is plainly stored in the database and will be unsealed directly by [prepare_unseal]!
|
||||||
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
/// Danger!
|
||||||
|
pub async fn init_insecure_in_db(pool: &DbPool) {
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
let key = key.as_slice().to_owned();
|
warn!(
|
||||||
|
"Danger: INSECURE! Generated root key is stored plainly in the database. Must ONLY be used for development!"
|
||||||
|
);
|
||||||
|
write_new_root_key(pool, root_key, "dev_only").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_new_root_key(pool: &DbPool, protected_key: Vec<u8>, type_to_be: &str) {
|
||||||
let _ = sqlx::query!(
|
let _ = sqlx::query!(
|
||||||
"
|
"
|
||||||
INSERT INTO root_key (encrypted_key, type, version)
|
INSERT INTO root_key (encrypted_key, type, version)
|
||||||
VALUES ($1, 'dev_only', 1)
|
VALUES ($1, $2, 1)
|
||||||
",
|
",
|
||||||
key
|
protected_key,
|
||||||
|
type_to_be
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to write new root key to the database");
|
.expect("Failed to write new root key to the database");
|
||||||
|
|
||||||
*value = KeyEnum::MainKey(key);
|
|
||||||
info!("Initialized new root key!");
|
info!("Initialized new root key!");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn seal(data: &String) -> ([u8; 12], Vec<u8>) {
|
pub async fn reseal(pool: &DbPool) {
|
||||||
let cipher = match &*KEY_MAPS.read().await {
|
{
|
||||||
KeyEnum::MainKey(key) => Aes256GcmSiv::new_from_slice(key),
|
let mut lock = ROOT_KEY_MAYBE.write().await;
|
||||||
KeyEnum::Uninitialized => panic!("Cannot seal secret since the vault is not unsealed"),
|
*lock = KeyEnum::Uninitialized;
|
||||||
|
}
|
||||||
|
prepare_unseal(pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sealing_status() {
|
||||||
|
let lock = ROOT_KEY_MAYBE.read().await;
|
||||||
|
match &*lock {
|
||||||
|
KeyEnum::MainKey(_) => todo!(),
|
||||||
|
KeyEnum::Simple(_) => todo!(),
|
||||||
|
KeyEnum::Uninitialized => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn provide_key(key: String) {
|
||||||
|
let progressed_something = {
|
||||||
|
let read_lock = ROOT_KEY_MAYBE.read().await;
|
||||||
|
match &*read_lock {
|
||||||
|
KeyEnum::MainKey(_) => {
|
||||||
|
info!("Providing keys is useless since vault is already unlocked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KeyEnum::Simple(protected_rk) => {
|
||||||
|
KeyEnum::MainKey(simple::unseal(protected_rk, key).await)
|
||||||
|
}
|
||||||
|
KeyEnum::Uninitialized => {
|
||||||
|
error!("Cannot process provided key when the vault is uninitialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Progress on unsealing: {}",
|
||||||
|
match progressed_something {
|
||||||
|
KeyEnum::MainKey(_) => "done and ready",
|
||||||
|
_ => "not yet ready",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut write_lock = ROOT_KEY_MAYBE.write().await;
|
||||||
|
*write_lock = progressed_something;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn encrypt(data: &String) -> ([u8; 12], Vec<u8>) {
|
||||||
|
let cipher = if let KeyEnum::MainKey(key) = &*ROOT_KEY_MAYBE.read().await {
|
||||||
|
Aes256GcmSiv::new_from_slice(key)
|
||||||
|
} else {
|
||||||
|
panic!("Cannot seal secret since the vault is not unsealed")
|
||||||
}
|
}
|
||||||
.expect("Failed to create new AesGcmSiv cipher from variable size key");
|
.expect("Failed to create new AesGcmSiv cipher from variable size key");
|
||||||
|
|
||||||
let nonce: aes_gcm_siv::aead::generic_array::GenericArray<u8, <Aes256GcmSiv as aes_gcm_siv::AeadCore>::NonceSize> =
|
let nonce: aes_gcm_siv::aead::generic_array::GenericArray<
|
||||||
Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
u8,
|
||||||
|
<Aes256GcmSiv as aes_gcm_siv::AeadCore>::NonceSize,
|
||||||
|
> = Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
||||||
let enc = cipher.encrypt(&nonce, data.as_bytes()).unwrap();
|
let enc = cipher.encrypt(&nonce, data.as_bytes()).unwrap();
|
||||||
debug_assert!(nonce.len() == 12);
|
debug_assert!(nonce.len() == 12);
|
||||||
let nonce = nonce.as_slice().try_into().expect("Nonce should be exactly 12 bytes");
|
let nonce = nonce
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.expect("Nonce should be exactly 12 bytes");
|
||||||
(nonce, enc)
|
(nonce, enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unseal(nonce: Vec<u8>, data: impl AsRef<[u8]>) -> String {
|
pub async fn decrypt(nonce: Vec<u8>, data: impl AsRef<[u8]>) -> String {
|
||||||
assert!(nonce.len() == 12);
|
assert!(nonce.len() == 12);
|
||||||
let cipher = match &*KEY_MAPS.read().await {
|
let cipher = match &*ROOT_KEY_MAYBE.read().await {
|
||||||
KeyEnum::MainKey(key) => Aes256GcmSiv::new_from_slice(key),
|
KeyEnum::MainKey(key) => Aes256GcmSiv::new_from_slice(key),
|
||||||
KeyEnum::Uninitialized => panic!("Cannot seal secret since the vault is not unsealed"),
|
_ => panic!("Cannot seal secret since the vault is not unsealed"),
|
||||||
}
|
}
|
||||||
.expect("Failed to create new AesGcmSiv cipher from variable size key");
|
.expect("Failed to create new AesGcmSiv cipher from variable size key");
|
||||||
|
|
||||||
|
|
|
||||||
31
src/storage/sealing/simple.rs
Normal file
31
src/storage/sealing/simple.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use aes_gcm_siv::{aead::{Aead, OsRng}, Aes256GcmSiv, KeyInit};
|
||||||
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use super::write_new_root_key;
|
||||||
|
|
||||||
|
pub async fn init_simple(pool: &DbPool) -> String {
|
||||||
|
// let root_key = write_new_root_key(pool, p_type).await;
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
|
let (user_key, protected_rk) = {
|
||||||
|
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let cipher = Aes256GcmSiv::new(&key);
|
||||||
|
let nonce: &[u8; 12] = b"hello world!"; // TODO
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
|
let enc = cipher.encrypt(nonce, root_key.as_slice()).unwrap();
|
||||||
|
(key, enc)
|
||||||
|
};
|
||||||
|
write_new_root_key(pool, protected_rk, "simple").await;
|
||||||
|
BASE64_STANDARD.encode(user_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unseal(protected_rk: &Vec<u8>, key: String) -> Vec<u8> {
|
||||||
|
let key = BASE64_STANDARD.decode(key).unwrap();
|
||||||
|
let cipher = Aes256GcmSiv::new_from_slice(&key).unwrap();
|
||||||
|
let nonce: &[u8; 12] = b"hello world!"; // TODO
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
|
cipher.decrypt(nonce, protected_rk.as_ref()).unwrap()
|
||||||
|
}
|
||||||
47
src/sys.rs
47
src/sys.rs
|
|
@ -1,8 +1,49 @@
|
||||||
use axum::Router;
|
use axum::{
|
||||||
|
extract::State, routing::{get, post}, Json, Router
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::storage::DbPool;
|
use crate::storage::{DbPool, sealing};
|
||||||
|
|
||||||
/// System routes
|
/// System routes
|
||||||
pub fn sys_router(pool: DbPool) -> Router<DbPool> {
|
pub fn sys_router(pool: DbPool) -> Router<DbPool> {
|
||||||
Router::new().with_state(pool)
|
Router::new()
|
||||||
|
.route("/seal", post(seal_post))
|
||||||
|
.route("/seal-status", get(seal_status_get))
|
||||||
|
.route("/unseal", post(unseal_post))
|
||||||
|
.with_state(pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn seal_post(State(pool): State<DbPool>) {
|
||||||
|
sealing::reseal(&pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct UnsealRequest {
|
||||||
|
/// Required, unless `reset` is true
|
||||||
|
pub key: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
/// Specifies if previously-provided unseal keys are discarded and the unseal process is reset.
|
||||||
|
pub reset: bool,
|
||||||
|
|
||||||
|
// #[serde(default)]
|
||||||
|
// /// Used to migrate the seal from shamir to autoseal or autoseal to shamir. Must be provided on all unseal key calls.
|
||||||
|
// pub migrate: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unseal_post(State(pool): State<DbPool>, Json(req): Json<UnsealRequest>) -> Result<(), ()> {
|
||||||
|
if req.reset {
|
||||||
|
sealing::reseal(&pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(key) = req.key {
|
||||||
|
sealing::provide_key(key).await;
|
||||||
|
} else if !req.reset {
|
||||||
|
// No request key nor reset = bad request
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn seal_status_get(State(pool): State<DbPool>) {}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue