feat(server::grpc): wire Shamir committee bootstrap and unseal proto messages
Some checks failed
ci/woodpecker/pr/server-audit Pipeline was successful
ci/woodpecker/pr/server-vet Pipeline failed
ci/woodpecker/pr/server-lint Pipeline failed
ci/woodpecker/pr/server-test Pipeline failed

Adds DeclareCommittee and ContributePassphrase variants to bootstrap.proto,
ContributePassphrase to unseal.proto, and AwaitingContributions result codes
to both. Implements corresponding inbound converters and outbound reply
mappings. VaultGate handlers delegate to VaultCoordinator.
This commit is contained in:
CleverWild
2026-06-12 19:43:17 +02:00
parent 6f65c907a3
commit 0d364d1951
5 changed files with 141 additions and 8 deletions

View File

@@ -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 {
oneof payload {
BootstrapEncryptedKey encrypted_key = 2;
DeclareCommittee declare_committee = 3;
ContributePassphrase contribute_passphrase = 4;
}
}
message Response {

View File

@@ -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;
}
}

View File

@@ -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<vault_gate::Inbound, Status> {
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<vault_gate::Inbound, Status> {
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;

View File

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

View File

@@ -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<u8>,
) -> Result<bool, Error> {
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<u8>,
) -> Result<bool, Error> {
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<events::Bootstrapped> for VaultGate {