diff --git a/protobufs/operator/vault/bootstrap.proto b/protobufs/operator/vault/bootstrap.proto index 97e376d..f1bee1d 100644 --- a/protobufs/operator/vault/bootstrap.proto +++ b/protobufs/operator/vault/bootstrap.proto @@ -8,15 +8,28 @@ message BootstrapEncryptedKey { bytes associated_data = 3; } +message DeclareCommittee { + uint32 count = 1; +} + +message ContributePassphrase { + bytes passphrase = 1; +} + enum BootstrapResult { BOOTSTRAP_RESULT_UNSPECIFIED = 0; BOOTSTRAP_RESULT_SUCCESS = 1; BOOTSTRAP_RESULT_ALREADY_BOOTSTRAPPED = 2; BOOTSTRAP_RESULT_INVALID_KEY = 3; + BOOTSTRAP_RESULT_AWAITING_CONTRIBUTIONS = 4; } message Request { - BootstrapEncryptedKey encrypted_key = 2; + oneof payload { + BootstrapEncryptedKey encrypted_key = 2; + DeclareCommittee declare_committee = 3; + ContributePassphrase contribute_passphrase = 4; + } } message Response { diff --git a/protobufs/operator/vault/unseal.proto b/protobufs/operator/vault/unseal.proto index 9378d5d..598c81c 100644 --- a/protobufs/operator/vault/unseal.proto +++ b/protobufs/operator/vault/unseal.proto @@ -15,17 +15,23 @@ message UnsealEncryptedKey { bytes associated_data = 3; } +message ContributePassphrase { + bytes passphrase = 1; +} + enum UnsealResult { UNSEAL_RESULT_UNSPECIFIED = 0; UNSEAL_RESULT_SUCCESS = 1; UNSEAL_RESULT_INVALID_KEY = 2; UNSEAL_RESULT_UNBOOTSTRAPPED = 3; + UNSEAL_RESULT_AWAITING_CONTRIBUTIONS = 4; } message Request { oneof payload { UnsealStart start = 1; UnsealEncryptedKey encrypted_key = 2; + ContributePassphrase contribute_passphrase = 3; } } diff --git a/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs b/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs index 6a08235..b0955eb 100644 --- a/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs +++ b/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs @@ -1,14 +1,16 @@ use crate::{ grpc::{Convert, TryConvert}, peers::operator::vault_gate::{ - self as vault_gate, HandleBootstrapEncryptedKey, HandleHandshake, HandleUnsealEncryptedKey, + self as vault_gate, HandleBootstrapEncryptedKey, HandleContributeBootstrapPassphrase, + HandleContributeUnsealPassphrase, HandleDeclareCommittee, HandleHandshake, + HandleUnsealEncryptedKey, }, }; use arbiter_proto::proto::operator::{ operator_request::Payload as OperatorRequestPayload, vault::{ self as proto_vault, - bootstrap::{self as proto_bootstrap}, + bootstrap::{self as proto_bootstrap, request::Payload as BootstrapRequestPayload}, request::Payload as VaultRequestPayload, unseal::{self as proto_unseal, request::Payload as UnsealRequestPayload}, }, @@ -73,6 +75,13 @@ impl TryConvert for UnsealRequestPayload { match self { Self::Start(start) => start.try_convert(), Self::EncryptedKey(key) => Ok(key.convert()), + Self::ContributePassphrase(cp) => Ok( + vault_gate::Inbound::HandleContributeUnsealPassphrase( + HandleContributeUnsealPassphrase { + passphrase: cp.passphrase, + }, + ), + ), } } } @@ -107,12 +116,35 @@ impl TryConvert for proto_bootstrap::Request { type Error = Status; fn try_convert(self) -> Result { - self.encrypted_key - .ok_or_else(|| Status::invalid_argument("Missing bootstrap encrypted key"))? + self.payload + .ok_or_else(|| Status::invalid_argument("Missing bootstrap payload"))? .try_convert() } } +impl TryConvert for BootstrapRequestPayload { + type Output = vault_gate::Inbound; + type Error = Status; + + fn try_convert(self) -> Result { + match self { + Self::EncryptedKey(key) => key.try_convert(), + Self::DeclareCommittee(dc) => Ok( + vault_gate::Inbound::HandleDeclareCommittee(HandleDeclareCommittee { + count: dc.count as usize, + }), + ), + Self::ContributePassphrase(cp) => Ok( + vault_gate::Inbound::HandleContributeBootstrapPassphrase( + HandleContributeBootstrapPassphrase { + passphrase: cp.passphrase, + }, + ), + ), + } + } +} + impl TryConvert for proto_bootstrap::BootstrapEncryptedKey { type Output = vault_gate::Inbound; type Error = Status; diff --git a/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs b/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs index 268b7d5..1e44ca4 100644 --- a/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs +++ b/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs @@ -46,7 +46,6 @@ impl Convert for VaultState { fn convert(self) -> OperatorResponsePayload { let proto_state = match self { Self::Unbootstrapped => ProtoVaultState::Unbootstrapped, - Self::Bootstrapping => ProtoVaultState::Boostrapping, Self::Sealed => ProtoVaultState::Sealed, Self::Unsealed => ProtoVaultState::Unsealed, }; @@ -111,6 +110,40 @@ impl TryConvert for vault_gate::Outbound { }; Ok(wrap_bootstrap_response(proto_result)) } + Self::HandleDeclareCommittee(result) => { + let proto_result = match result { + Ok(()) => ProtoBootstrapResult::Success, + Err(err) => { + warn!(?err, "declare committee failed"); + return Err(Status::internal("Failed to declare committee")); + } + }; + Ok(wrap_bootstrap_response(proto_result)) + } + Self::HandleContributeBootstrapPassphrase(result) => { + let proto_result = match result { + Ok(true) => ProtoBootstrapResult::Success, + Ok(false) => ProtoBootstrapResult::AwaitingContributions, + Err(err) => { + warn!(?err, "contribute bootstrap passphrase failed"); + return Err(Status::internal("Failed to contribute bootstrap passphrase")); + } + }; + Ok(wrap_bootstrap_response(proto_result)) + } + Self::HandleContributeUnsealPassphrase(result) => { + let proto_result = match result { + Ok(true) => ProtoUnsealResult::Success, + Ok(false) => ProtoUnsealResult::AwaitingContributions, + Err(err) => { + warn!(?err, "contribute unseal passphrase failed"); + return Err(Status::internal("Failed to contribute unseal passphrase")); + } + }; + Ok(wrap_unseal_response(UnsealResponsePayload::Result( + proto_result.into(), + ))) + } } } } diff --git a/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs b/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs index 6a8a265..b74ff3e 100644 --- a/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs +++ b/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs @@ -3,6 +3,7 @@ use crate::{ actors::{ GlobalActors, vault::{self, Bootstrap, GetState, TryUnseal, VaultState, events}, + vault_coordinator::{ContributeBootstrap, ContributeUnseal, StartBootstrap}, }, crypto::integrity::{self}, db::DatabasePool, @@ -17,6 +18,9 @@ use tokio::sync::oneshot; use tracing::{error, info}; use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret}; +pub use VaultGateMessage as Inbound; +pub use VaultGateMessageReply as Outbound; + pub mod state; #[derive(Debug, thiserror::Error)] @@ -118,8 +122,7 @@ impl VaultGate { } } } - -#[messages(messages = Inbound, replies = Outbound)] +#[messages(enum)] impl VaultGate { #[message] pub fn handle_handshake( @@ -234,6 +237,52 @@ impl VaultGate { Ok(answer) } + + #[message] + pub async fn handle_declare_committee(&mut self, count: usize) -> Result<(), Error> { + self.actors + .vault_coordinator + .ask(StartBootstrap { + operator_id: self.auth_creds.id, + declared_count: count, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } + + #[message] + pub async fn handle_contribute_bootstrap_passphrase( + &mut self, + passphrase: Vec, + ) -> Result { + use arbiter_crypto::safecell::SafeCell; + let passphrase_cell = SafeCell::new(passphrase); + self.actors + .vault_coordinator + .ask(ContributeBootstrap { + operator_id: self.auth_creds.id, + passphrase: passphrase_cell, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } + + #[message] + pub async fn handle_contribute_unseal_passphrase( + &mut self, + passphrase: Vec, + ) -> Result { + use arbiter_crypto::safecell::SafeCell; + let passphrase_cell = SafeCell::new(passphrase); + self.actors + .vault_coordinator + .ask(ContributeUnseal { + operator_id: self.auth_creds.id, + passphrase: passphrase_cell, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } } impl Message for VaultGate {