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 (
|
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,
|
||||||
|
nonce BLOB,
|
||||||
type TEXT NOT NULL CHECK ( type IN ('dev_only', 'simple') )
|
type TEXT NOT NULL CHECK ( type IN ('dev_only', 'simple') )
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
use super::structs::KvV2WriteRequest;
|
use super::structs::KvV2WriteRequest;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::HttpError, engines::{
|
DbPool,
|
||||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
common::HttpError,
|
||||||
}, storage::sealing::{encrypt, decrypt}, DbPool
|
engines::{
|
||||||
|
EnginePath,
|
||||||
|
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper},
|
||||||
|
},
|
||||||
|
storage::sealing::{decrypt, encrypt},
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
|
|
@ -52,9 +56,7 @@ pub async fn get_data(
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, ()> {
|
) -> Result<Response, ()> {
|
||||||
debug!(
|
debug!("Get request: Engine: {engine_path}, path: {path}",);
|
||||||
"Get request: Engine: {engine_path}, path: {path}",
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = if params.version != 0 {
|
let res = if params.version != 0 {
|
||||||
// With specific version
|
// With specific version
|
||||||
|
|
@ -138,7 +140,8 @@ pub async fn post_data(
|
||||||
"src/engines/kv/post_secret.sql",
|
"src/engines/kv/post_secret.sql",
|
||||||
engine_path,
|
engine_path,
|
||||||
kv_path,
|
kv_path,
|
||||||
nonce, enc,
|
nonce,
|
||||||
|
enc,
|
||||||
ts,
|
ts,
|
||||||
secret.version,
|
secret.version,
|
||||||
)
|
)
|
||||||
|
|
@ -230,8 +233,6 @@ pub async fn delete_data(
|
||||||
Ok(NoContent.into_response())
|
Ok(NoContent.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Not
|
|
||||||
pub async fn patch_data(
|
pub async fn patch_data(
|
||||||
State(pool): State<DbPool>,
|
State(pool): State<DbPool>,
|
||||||
Path(kv_path): Path<String>,
|
Path(kv_path): Path<String>,
|
||||||
|
|
@ -241,4 +242,3 @@ pub async fn patch_data(
|
||||||
// TODO: implement only application/merge-patch+json
|
// TODO: implement only application/merge-patch+json
|
||||||
todo!("Not implemented")
|
todo!("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ use super::DbPool;
|
||||||
enum KeyEnum {
|
enum KeyEnum {
|
||||||
/// Final key
|
/// Final key
|
||||||
MainKey(Vec<u8>),
|
MainKey(Vec<u8>),
|
||||||
/// Encrypted with single secret
|
/// Encrypted with single secret (protected_rk, nonce)
|
||||||
Simple(Vec<u8>),
|
Simple(Vec<u8>, Vec<u8>),
|
||||||
/// Unknown or not initialized
|
/// Unknown or not initialized
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
// ShamirPortion(Vec<String>),
|
// ShamirPortion(Vec<String>),
|
||||||
|
|
@ -25,6 +25,7 @@ static ROOT_KEY_MAYBE: RwLock<KeyEnum> = RwLock::const_new(KeyEnum::Uninitialize
|
||||||
struct ProtectedRK {
|
struct ProtectedRK {
|
||||||
pub protection_type: String,
|
pub protection_type: String,
|
||||||
pub encrypted_key: Vec<u8>,
|
pub encrypted_key: Vec<u8>,
|
||||||
|
pub nonce: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if vault is initialized or unsealed.
|
/// 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!(
|
let rk = sqlx::query_as!(
|
||||||
ProtectedRK,
|
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)
|
.fetch_optional(pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -69,7 +70,10 @@ pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
||||||
*lock = KeyEnum::MainKey(v.encrypted_key);
|
*lock = KeyEnum::MainKey(v.encrypted_key);
|
||||||
}
|
}
|
||||||
"simple" => {
|
"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"),
|
_ => 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.
|
/// Must NOT be used in production.
|
||||||
/// Token is plainly stored in the database and will be unsealed directly by [prepare_unseal]!
|
/// Token is plainly stored in the database and will be unsealed directly by [prepare_unseal]!
|
||||||
/// Danger!
|
/// Danger!
|
||||||
|
#[allow(unused)]
|
||||||
pub async fn init_insecure_in_db(pool: &DbPool) {
|
pub async fn init_insecure_in_db(pool: &DbPool) {
|
||||||
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
let root_key = root_key.as_slice().to_owned();
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
@ -86,17 +91,23 @@ pub async fn init_insecure_in_db(pool: &DbPool) {
|
||||||
warn!(
|
warn!(
|
||||||
"Danger: INSECURE! Generated root key is stored plainly in the database. Must ONLY be used for development!"
|
"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!(
|
let _ = sqlx::query!(
|
||||||
"
|
"
|
||||||
INSERT INTO root_key (encrypted_key, type, version)
|
INSERT INTO root_key (encrypted_key, type, version, nonce)
|
||||||
VALUES ($1, $2, 1)
|
VALUES ($1, $2, 1, $3)
|
||||||
",
|
",
|
||||||
protected_key,
|
protected_key,
|
||||||
type_to_be
|
type_to_be,
|
||||||
|
nonce
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -117,7 +128,7 @@ pub async fn sealing_status() {
|
||||||
let lock = ROOT_KEY_MAYBE.read().await;
|
let lock = ROOT_KEY_MAYBE.read().await;
|
||||||
match &*lock {
|
match &*lock {
|
||||||
KeyEnum::MainKey(_) => todo!(),
|
KeyEnum::MainKey(_) => todo!(),
|
||||||
KeyEnum::Simple(_) => todo!(),
|
KeyEnum::Simple(_, _) => todo!(),
|
||||||
KeyEnum::Uninitialized => 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");
|
info!("Providing keys is useless since vault is already unlocked");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
KeyEnum::Simple(protected_rk) => {
|
KeyEnum::Simple(protected_rk, nonce) => {
|
||||||
KeyEnum::MainKey(simple::unseal(protected_rk, key).await)
|
KeyEnum::MainKey(simple::unseal(protected_rk, key, nonce).await)
|
||||||
}
|
}
|
||||||
KeyEnum::Uninitialized => {
|
KeyEnum::Uninitialized => {
|
||||||
error!("Cannot process provided key when the vault is 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 aes_gcm_siv::{
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
AeadCore, Aes256GcmSiv, KeyInit,
|
||||||
|
aead::{Aead, OsRng, generic_array::GenericArray},
|
||||||
|
};
|
||||||
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
|
|
||||||
use crate::DbPool;
|
use crate::DbPool;
|
||||||
|
|
||||||
use super::write_new_root_key;
|
use super::write_new_root_key;
|
||||||
|
|
||||||
pub async fn init_simple(pool: &DbPool) -> String {
|
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 = 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 root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
let (user_key, protected_rk) = {
|
let (user_key, protected_rk) = {
|
||||||
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
let cipher = Aes256GcmSiv::new(&key);
|
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 nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
let enc = cipher.encrypt(nonce, root_key.as_slice()).unwrap();
|
let enc = cipher.encrypt(nonce, root_key.as_slice()).unwrap();
|
||||||
(key, enc)
|
(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)
|
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 key = BASE64_STANDARD.decode(key).unwrap();
|
||||||
let cipher = Aes256GcmSiv::new_from_slice(&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);
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
cipher.decrypt(nonce, protected_rk.as_ref()).unwrap()
|
cipher.decrypt(nonce, protected_rk.as_ref()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue