Fix (sealing): Simple sealing with random nonce
Some checks failed
Rust / build (pull_request) Failing after 40s

This commit is contained in:
Laurenz 2025-03-27 22:13:57 +01:00
parent 88ed714e22
commit 5de9e1d74e
Signed by: C0ffeeCode
SSH key fingerprint: SHA256:prvFOyBjButRypyXm7X8lbbCkly2Dq1PF7e/mrsPVjw
4 changed files with 46 additions and 29 deletions

View file

@ -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') )
);

View file

@ -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")
}

View file

@ -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");

View file

@ -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()
}