Fix (sealing): Simple sealing with random nonce
Some checks failed
Rust / build (pull_request) Failing after 40s
Some checks failed
Rust / build (pull_request) Failing after 40s
This commit is contained in:
parent
88ed714e22
commit
5de9e1d74e
4 changed files with 46 additions and 29 deletions
|
|
@ -3,5 +3,6 @@
|
|||
CREATE TABLE root_key (
|
||||
version INTEGER PRIMARY KEY CHECK ( version = 1 ),
|
||||
encrypted_key BLOB NOT NULL,
|
||||
nonce BLOB,
|
||||
type TEXT NOT NULL CHECK ( type IN ('dev_only', 'simple') )
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use super::structs::KvV2WriteRequest;
|
||||
use crate::{
|
||||
common::HttpError, engines::{
|
||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
||||
}, storage::sealing::{encrypt, decrypt}, DbPool
|
||||
DbPool,
|
||||
common::HttpError,
|
||||
engines::{
|
||||
EnginePath,
|
||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper},
|
||||
},
|
||||
storage::sealing::{decrypt, encrypt},
|
||||
};
|
||||
use axum::{
|
||||
Extension, Json,
|
||||
|
|
@ -52,9 +56,7 @@ pub async fn get_data(
|
|||
Path(path): Path<String>,
|
||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||
) -> Result<Response, ()> {
|
||||
debug!(
|
||||
"Get request: Engine: {engine_path}, path: {path}",
|
||||
);
|
||||
debug!("Get request: Engine: {engine_path}, path: {path}",);
|
||||
|
||||
let res = if params.version != 0 {
|
||||
// With specific version
|
||||
|
|
@ -138,7 +140,8 @@ pub async fn post_data(
|
|||
"src/engines/kv/post_secret.sql",
|
||||
engine_path,
|
||||
kv_path,
|
||||
nonce, enc,
|
||||
nonce,
|
||||
enc,
|
||||
ts,
|
||||
secret.version,
|
||||
)
|
||||
|
|
@ -230,8 +233,6 @@ pub async fn delete_data(
|
|||
Ok(NoContent.into_response())
|
||||
}
|
||||
|
||||
|
||||
// Not
|
||||
pub async fn patch_data(
|
||||
State(pool): State<DbPool>,
|
||||
Path(kv_path): Path<String>,
|
||||
|
|
@ -241,4 +242,3 @@ pub async fn patch_data(
|
|||
// TODO: implement only application/merge-patch+json
|
||||
todo!("Not implemented")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ use super::DbPool;
|
|||
enum KeyEnum {
|
||||
/// Final key
|
||||
MainKey(Vec<u8>),
|
||||
/// Encrypted with single secret
|
||||
Simple(Vec<u8>),
|
||||
/// Encrypted with single secret (protected_rk, nonce)
|
||||
Simple(Vec<u8>, Vec<u8>),
|
||||
/// Unknown or not initialized
|
||||
Uninitialized,
|
||||
// ShamirPortion(Vec<String>),
|
||||
|
|
@ -25,6 +25,7 @@ static ROOT_KEY_MAYBE: RwLock<KeyEnum> = RwLock::const_new(KeyEnum::Uninitialize
|
|||
struct ProtectedRK {
|
||||
pub protection_type: String,
|
||||
pub encrypted_key: Vec<u8>,
|
||||
pub nonce: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Returns `true` if vault is initialized or unsealed.
|
||||
|
|
@ -41,7 +42,7 @@ pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
|||
|
||||
let rk = sqlx::query_as!(
|
||||
ProtectedRK,
|
||||
"SELECT encrypted_key, type as protection_type FROM root_key ORDER BY version LIMIT 1"
|
||||
"SELECT encrypted_key, type as protection_type, nonce FROM root_key ORDER BY version LIMIT 1"
|
||||
)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
|
|
@ -69,7 +70,10 @@ pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
|||
*lock = KeyEnum::MainKey(v.encrypted_key);
|
||||
}
|
||||
"simple" => {
|
||||
*lock = KeyEnum::Simple(v.encrypted_key);
|
||||
*lock = KeyEnum::Simple(
|
||||
v.encrypted_key,
|
||||
v.nonce.expect("Simple encryption but the nonce is missing"),
|
||||
);
|
||||
}
|
||||
_ => panic!("Unknown root key type in database"),
|
||||
}
|
||||
|
|
@ -79,6 +83,7 @@ pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
|||
/// Must NOT be used in production.
|
||||
/// Token is plainly stored in the database and will be unsealed directly by [prepare_unseal]!
|
||||
/// Danger!
|
||||
#[allow(unused)]
|
||||
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();
|
||||
|
|
@ -86,17 +91,23 @@ pub async fn init_insecure_in_db(pool: &DbPool) {
|
|||
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;
|
||||
write_new_root_key(pool, root_key, "dev_only", None).await;
|
||||
}
|
||||
|
||||
async fn write_new_root_key(pool: &DbPool, protected_key: Vec<u8>, type_to_be: &str) {
|
||||
async fn write_new_root_key(
|
||||
pool: &DbPool,
|
||||
protected_key: Vec<u8>,
|
||||
type_to_be: &str,
|
||||
nonce: Option<&[u8]>,
|
||||
) {
|
||||
let _ = sqlx::query!(
|
||||
"
|
||||
INSERT INTO root_key (encrypted_key, type, version)
|
||||
VALUES ($1, $2, 1)
|
||||
INSERT INTO root_key (encrypted_key, type, version, nonce)
|
||||
VALUES ($1, $2, 1, $3)
|
||||
",
|
||||
protected_key,
|
||||
type_to_be
|
||||
type_to_be,
|
||||
nonce
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
|
|
@ -117,7 +128,7 @@ pub async fn sealing_status() {
|
|||
let lock = ROOT_KEY_MAYBE.read().await;
|
||||
match &*lock {
|
||||
KeyEnum::MainKey(_) => todo!(),
|
||||
KeyEnum::Simple(_) => todo!(),
|
||||
KeyEnum::Simple(_, _) => todo!(),
|
||||
KeyEnum::Uninitialized => todo!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -130,8 +141,8 @@ pub async fn provide_key(key: String) {
|
|||
info!("Providing keys is useless since vault is already unlocked");
|
||||
return;
|
||||
}
|
||||
KeyEnum::Simple(protected_rk) => {
|
||||
KeyEnum::MainKey(simple::unseal(protected_rk, key).await)
|
||||
KeyEnum::Simple(protected_rk, nonce) => {
|
||||
KeyEnum::MainKey(simple::unseal(protected_rk, key, nonce).await)
|
||||
}
|
||||
KeyEnum::Uninitialized => {
|
||||
error!("Cannot process provided key when the vault is uninitialized");
|
||||
|
|
|
|||
|
|
@ -1,31 +1,36 @@
|
|||
use aes_gcm_siv::{aead::{Aead, OsRng}, Aes256GcmSiv, KeyInit};
|
||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||
use aes_gcm_siv::{
|
||||
AeadCore, Aes256GcmSiv, KeyInit,
|
||||
aead::{Aead, OsRng, generic_array::GenericArray},
|
||||
};
|
||||
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||
|
||||
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 nonce: GenericArray<u8, <Aes256GcmSiv as AeadCore>::NonceSize> =
|
||||
Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
||||
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: &[u8] = nonce.as_slice();
|
||||
debug_assert_eq!(nonce.len(), 12);
|
||||
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;
|
||||
write_new_root_key(pool, protected_rk, "simple", Some(nonce.as_slice())).await;
|
||||
BASE64_STANDARD.encode(user_key)
|
||||
}
|
||||
|
||||
pub async fn unseal(protected_rk: &Vec<u8>, key: String) -> Vec<u8> {
|
||||
pub async fn unseal(protected_rk: &Vec<u8>, key: String, nonce: &[u8]) -> 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
|
||||
debug_assert_eq!(nonce.len(), 12);
|
||||
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||
cipher.decrypt(nonce, protected_rk.as_ref()).unwrap()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue