feat(server::user-agent): Unseal implemented
This commit is contained in:
@@ -1,25 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package arbiter.unseal;
|
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
|
||||||
|
|
||||||
message UnsealStart {
|
|
||||||
bytes client_pubkey = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsealStartResponse {
|
|
||||||
bytes server_pubkey = 1;
|
|
||||||
}
|
|
||||||
message UnsealEncryptedKey {
|
|
||||||
bytes nonce = 1;
|
|
||||||
bytes ciphertext = 2;
|
|
||||||
bytes associated_data = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UnsealResult {
|
|
||||||
UNSEAL_RESULT_UNSPECIFIED = 0;
|
|
||||||
UNSEAL_RESULT_SUCCESS = 1;
|
|
||||||
UNSEAL_RESULT_INVALID_KEY = 2;
|
|
||||||
UNSEAL_RESULT_UNBOOTSTRAPPED = 3;
|
|
||||||
}
|
|
||||||
@@ -3,19 +3,49 @@ syntax = "proto3";
|
|||||||
package arbiter;
|
package arbiter;
|
||||||
|
|
||||||
import "auth.proto";
|
import "auth.proto";
|
||||||
import "unseal.proto";
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message UnsealStart {
|
||||||
|
bytes client_pubkey = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnsealStartResponse {
|
||||||
|
bytes server_pubkey = 1;
|
||||||
|
}
|
||||||
|
message UnsealEncryptedKey {
|
||||||
|
bytes nonce = 1;
|
||||||
|
bytes ciphertext = 2;
|
||||||
|
bytes associated_data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UnsealResult {
|
||||||
|
UNSEAL_RESULT_UNSPECIFIED = 0;
|
||||||
|
UNSEAL_RESULT_SUCCESS = 1;
|
||||||
|
UNSEAL_RESULT_INVALID_KEY = 2;
|
||||||
|
UNSEAL_RESULT_UNBOOTSTRAPPED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VaultState {
|
||||||
|
VAULT_STATE_UNSPECIFIED = 0;
|
||||||
|
VAULT_STATE_UNBOOTSTRAPPED = 1;
|
||||||
|
VAULT_STATE_SEALED = 2;
|
||||||
|
VAULT_STATE_UNSEALED = 3;
|
||||||
|
VAULT_STATE_ERROR = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message UserAgentRequest {
|
message UserAgentRequest {
|
||||||
oneof payload {
|
oneof payload {
|
||||||
arbiter.auth.ClientMessage auth_message = 1;
|
arbiter.auth.ClientMessage auth_message = 1;
|
||||||
arbiter.unseal.UnsealStart unseal_start = 2;
|
UnsealStart unseal_start = 2;
|
||||||
arbiter.unseal.UnsealEncryptedKey unseal_encrypted_key = 3;
|
UnsealEncryptedKey unseal_encrypted_key = 3;
|
||||||
|
google.protobuf.Empty query_vault_state = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
message UserAgentResponse {
|
message UserAgentResponse {
|
||||||
oneof payload {
|
oneof payload {
|
||||||
arbiter.auth.ServerMessage auth_message = 1;
|
arbiter.auth.ServerMessage auth_message = 1;
|
||||||
arbiter.unseal.UnsealStartResponse unseal_start_response = 2;
|
UnsealStartResponse unseal_start_response = 2;
|
||||||
arbiter.unseal.UnsealResult unseal_result = 3;
|
UnsealResult unseal_result = 3;
|
||||||
|
VaultState vault_state = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ pub mod proto {
|
|||||||
pub mod auth {
|
pub mod auth {
|
||||||
tonic::include_proto!("arbiter.auth");
|
tonic::include_proto!("arbiter.auth");
|
||||||
}
|
}
|
||||||
pub mod unseal {
|
|
||||||
tonic::include_proto!("arbiter.unseal");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|||||||
@@ -1,25 +1,18 @@
|
|||||||
use std::{
|
use std::{ops::DerefMut, sync::Mutex};
|
||||||
ops::DerefMut,
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use arbiter_proto::proto::{
|
use arbiter_proto::proto::{
|
||||||
UserAgentResponse,
|
UnsealEncryptedKey, UnsealResult, UnsealStart, UnsealStartResponse, UserAgentResponse,
|
||||||
auth::{
|
auth::{
|
||||||
self, AuthChallengeRequest, AuthOk, ServerMessage as AuthServerMessage,
|
self, AuthChallengeRequest, AuthOk, ServerMessage as AuthServerMessage,
|
||||||
server_message::Payload as ServerAuthPayload,
|
server_message::Payload as ServerAuthPayload,
|
||||||
},
|
},
|
||||||
unseal::{UnsealEncryptedKey, UnsealResult, UnsealStart, UnsealStartResponse},
|
|
||||||
user_agent_response::Payload as UserAgentResponsePayload,
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
};
|
};
|
||||||
use chacha20poly1305::{
|
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
||||||
AeadInPlace, XChaCha20Poly1305, XNonce,
|
|
||||||
aead::KeyInit,
|
|
||||||
};
|
|
||||||
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, dsl::update};
|
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, dsl::update};
|
||||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||||
use ed25519_dalek::VerifyingKey;
|
use ed25519_dalek::VerifyingKey;
|
||||||
use kameo::{Actor, actor::ActorRef, messages};
|
use kameo::{Actor, actor::ActorRef, error::SendError, messages};
|
||||||
use memsafe::MemSafe;
|
use memsafe::MemSafe;
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
@@ -30,6 +23,7 @@ use crate::{
|
|||||||
ServerContext,
|
ServerContext,
|
||||||
actors::{
|
actors::{
|
||||||
bootstrap::{Bootstrapper, ConsumeToken},
|
bootstrap::{Bootstrapper, ConsumeToken},
|
||||||
|
keyholder::{self, KeyHolder, TryUnseal},
|
||||||
user_agent::state::{
|
user_agent::state::{
|
||||||
AuthRequestContext, ChallengeContext, DummyContext, UnsealContext, UserAgentEvents,
|
AuthRequestContext, ChallengeContext, DummyContext, UnsealContext, UserAgentEvents,
|
||||||
UserAgentStateMachine, UserAgentStates,
|
UserAgentStateMachine, UserAgentStates,
|
||||||
@@ -50,6 +44,7 @@ pub(crate) use transport::handle_user_agent;
|
|||||||
pub struct UserAgentActor {
|
pub struct UserAgentActor {
|
||||||
db: db::DatabasePool,
|
db: db::DatabasePool,
|
||||||
bootstapper: ActorRef<Bootstrapper>,
|
bootstapper: ActorRef<Bootstrapper>,
|
||||||
|
keyholder: ActorRef<KeyHolder>,
|
||||||
state: UserAgentStateMachine<DummyContext>,
|
state: UserAgentStateMachine<DummyContext>,
|
||||||
// will be used in future
|
// will be used in future
|
||||||
_tx: Sender<Result<UserAgentResponse, Status>>,
|
_tx: Sender<Result<UserAgentResponse, Status>>,
|
||||||
@@ -63,6 +58,7 @@ impl UserAgentActor {
|
|||||||
Self {
|
Self {
|
||||||
db: context.db.clone(),
|
db: context.db.clone(),
|
||||||
bootstapper: context.bootstrapper.clone(),
|
bootstapper: context.bootstrapper.clone(),
|
||||||
|
keyholder: context.keyholder.clone(),
|
||||||
state: UserAgentStateMachine::new(DummyContext),
|
state: UserAgentStateMachine::new(DummyContext),
|
||||||
_tx: tx,
|
_tx: tx,
|
||||||
}
|
}
|
||||||
@@ -72,11 +68,13 @@ impl UserAgentActor {
|
|||||||
pub(crate) fn new_manual(
|
pub(crate) fn new_manual(
|
||||||
db: db::DatabasePool,
|
db: db::DatabasePool,
|
||||||
bootstapper: ActorRef<Bootstrapper>,
|
bootstapper: ActorRef<Bootstrapper>,
|
||||||
|
keyholder: ActorRef<KeyHolder>,
|
||||||
tx: Sender<Result<UserAgentResponse, Status>>,
|
tx: Sender<Result<UserAgentResponse, Status>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
db,
|
db,
|
||||||
bootstapper,
|
bootstapper,
|
||||||
|
keyholder,
|
||||||
state: UserAgentStateMachine::new(DummyContext),
|
state: UserAgentStateMachine::new(DummyContext),
|
||||||
_tx: tx,
|
_tx: tx,
|
||||||
}
|
}
|
||||||
@@ -280,22 +278,57 @@ impl UserAgentActor {
|
|||||||
let shared_secret = ephemeral_secret.diffie_hellman(&unseal_context.client_public_key);
|
let shared_secret = ephemeral_secret.diffie_hellman(&unseal_context.client_public_key);
|
||||||
let cipher = XChaCha20Poly1305::new(shared_secret.as_bytes().into());
|
let cipher = XChaCha20Poly1305::new(shared_secret.as_bytes().into());
|
||||||
|
|
||||||
let mut root_key_buffer = MemSafe::new(req.ciphertext.clone()).unwrap();
|
let mut seal_key_buffer = MemSafe::new(req.ciphertext.clone()).unwrap();
|
||||||
let mut write_handle = root_key_buffer.write().unwrap();
|
|
||||||
let write_handle = write_handle.deref_mut();
|
|
||||||
|
|
||||||
let decryption_result = cipher
|
let decryption_result = {
|
||||||
.decrypt_in_place(nonce, &req.associated_data, write_handle);
|
let mut write_handle = seal_key_buffer.write().unwrap();
|
||||||
|
let write_handle = write_handle.deref_mut();
|
||||||
|
cipher.decrypt_in_place(nonce, &req.associated_data, write_handle)
|
||||||
|
};
|
||||||
|
|
||||||
match decryption_result {
|
match decryption_result {
|
||||||
Ok(_) => todo!("Send key to the keyguarding"),
|
Ok(_) => {
|
||||||
|
match self
|
||||||
|
.keyholder
|
||||||
|
.ask(TryUnseal {
|
||||||
|
seal_key_raw: seal_key_buffer,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Successfully unsealed key with client-provided key");
|
||||||
|
self.transition(UserAgentEvents::ReceivedValidKey)?;
|
||||||
|
Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
||||||
|
UnsealResult::Success.into(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Err(SendError::HandlerError(keyholder::Error::InvalidKey)) => {
|
||||||
|
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||||
|
Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
||||||
|
UnsealResult::InvalidKey.into(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Err(SendError::HandlerError(err)) => {
|
||||||
|
error!(?err, "Keyholder failed to unseal key");
|
||||||
|
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||||
|
Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
||||||
|
UnsealResult::InvalidKey.into(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "Failed to send unseal request to keyholder");
|
||||||
|
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||||
|
Err(Status::internal("Vault is not available"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(?err, "Failed to decrypt unseal key");
|
error!(?err, "Failed to decrypt unseal key");
|
||||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||||
return Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
return Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
||||||
UnsealResult::InvalidKey.into(),
|
UnsealResult::InvalidKey.into(),
|
||||||
)));
|
)));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use kameo::actor::Spawn;
|
|||||||
use crate::{
|
use crate::{
|
||||||
actors::{
|
actors::{
|
||||||
bootstrap::Bootstrapper,
|
bootstrap::Bootstrapper,
|
||||||
|
keyholder::{self, KeyHolder},
|
||||||
user_agent::{HandleAuthChallengeRequest, HandleAuthChallengeSolution},
|
user_agent::{HandleAuthChallengeRequest, HandleAuthChallengeSolution},
|
||||||
},
|
},
|
||||||
db::{self, schema},
|
db::{self, schema},
|
||||||
@@ -24,12 +25,15 @@ pub async fn test_bootstrap_token_auth() {
|
|||||||
let db = db::create_test_pool().await;
|
let db = db::create_test_pool().await;
|
||||||
// explicitly not installing any user_agent pubkeys
|
// explicitly not installing any user_agent pubkeys
|
||||||
let bootstrapper = Bootstrapper::new(&db).await.unwrap(); // this will create bootstrap token
|
let bootstrapper = Bootstrapper::new(&db).await.unwrap(); // this will create bootstrap token
|
||||||
|
let keyholder = KeyHolder::new(db.clone()).await.unwrap();
|
||||||
let token = bootstrapper.get_token().unwrap();
|
let token = bootstrapper.get_token().unwrap();
|
||||||
|
|
||||||
let bootstrapper_ref = Bootstrapper::spawn(bootstrapper);
|
let bootstrapper_ref = Bootstrapper::spawn(bootstrapper);
|
||||||
|
let keyholder_ref = KeyHolder::spawn(keyholder);
|
||||||
let user_agent = UserAgentActor::new_manual(
|
let user_agent = UserAgentActor::new_manual(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
bootstrapper_ref,
|
bootstrapper_ref,
|
||||||
|
keyholder_ref,
|
||||||
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
||||||
);
|
);
|
||||||
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
||||||
@@ -78,11 +82,15 @@ pub async fn test_bootstrap_invalid_token_auth() {
|
|||||||
let db = db::create_test_pool().await;
|
let db = db::create_test_pool().await;
|
||||||
// explicitly not installing any user_agent pubkeys
|
// explicitly not installing any user_agent pubkeys
|
||||||
let bootstrapper = Bootstrapper::new(&db).await.unwrap(); // this will create bootstrap token
|
let bootstrapper = Bootstrapper::new(&db).await.unwrap(); // this will create bootstrap token
|
||||||
|
let keyholder = KeyHolder::new(db.clone()).await.unwrap();
|
||||||
|
|
||||||
let bootstrapper_ref = Bootstrapper::spawn(bootstrapper);
|
let bootstrapper_ref = Bootstrapper::spawn(bootstrapper);
|
||||||
|
let keyholder_ref = KeyHolder::spawn(keyholder);
|
||||||
|
|
||||||
let user_agent = UserAgentActor::new_manual(
|
let user_agent = UserAgentActor::new_manual(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
bootstrapper_ref,
|
bootstrapper_ref,
|
||||||
|
keyholder_ref,
|
||||||
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
||||||
);
|
);
|
||||||
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
||||||
@@ -126,9 +134,11 @@ pub async fn test_challenge_auth() {
|
|||||||
let db = db::create_test_pool().await;
|
let db = db::create_test_pool().await;
|
||||||
|
|
||||||
let bootstrapper_ref = Bootstrapper::spawn(Bootstrapper::new(&db).await.unwrap());
|
let bootstrapper_ref = Bootstrapper::spawn(Bootstrapper::new(&db).await.unwrap());
|
||||||
|
let keyholder_ref = KeyHolder::spawn(KeyHolder::new(db.clone()).await.unwrap());
|
||||||
let user_agent = UserAgentActor::new_manual(
|
let user_agent = UserAgentActor::new_manual(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
bootstrapper_ref,
|
bootstrapper_ref,
|
||||||
|
keyholder_ref,
|
||||||
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
tokio::sync::mpsc::channel(1).0, // dummy channel, we won't actually send responses in this test
|
||||||
);
|
);
|
||||||
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
let user_agent_ref = UserAgentActor::spawn(user_agent);
|
||||||
|
|||||||
Reference in New Issue
Block a user