feat(server::grpc): wire Shamir committee bootstrap and unseal proto messages
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:
@@ -8,15 +8,28 @@ message BootstrapEncryptedKey {
|
|||||||
bytes associated_data = 3;
|
bytes associated_data = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message DeclareCommittee {
|
||||||
|
uint32 count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContributePassphrase {
|
||||||
|
bytes passphrase = 1;
|
||||||
|
}
|
||||||
|
|
||||||
enum BootstrapResult {
|
enum BootstrapResult {
|
||||||
BOOTSTRAP_RESULT_UNSPECIFIED = 0;
|
BOOTSTRAP_RESULT_UNSPECIFIED = 0;
|
||||||
BOOTSTRAP_RESULT_SUCCESS = 1;
|
BOOTSTRAP_RESULT_SUCCESS = 1;
|
||||||
BOOTSTRAP_RESULT_ALREADY_BOOTSTRAPPED = 2;
|
BOOTSTRAP_RESULT_ALREADY_BOOTSTRAPPED = 2;
|
||||||
BOOTSTRAP_RESULT_INVALID_KEY = 3;
|
BOOTSTRAP_RESULT_INVALID_KEY = 3;
|
||||||
|
BOOTSTRAP_RESULT_AWAITING_CONTRIBUTIONS = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Request {
|
message Request {
|
||||||
BootstrapEncryptedKey encrypted_key = 2;
|
oneof payload {
|
||||||
|
BootstrapEncryptedKey encrypted_key = 2;
|
||||||
|
DeclareCommittee declare_committee = 3;
|
||||||
|
ContributePassphrase contribute_passphrase = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Response {
|
message Response {
|
||||||
|
|||||||
@@ -15,17 +15,23 @@ message UnsealEncryptedKey {
|
|||||||
bytes associated_data = 3;
|
bytes associated_data = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ContributePassphrase {
|
||||||
|
bytes passphrase = 1;
|
||||||
|
}
|
||||||
|
|
||||||
enum UnsealResult {
|
enum UnsealResult {
|
||||||
UNSEAL_RESULT_UNSPECIFIED = 0;
|
UNSEAL_RESULT_UNSPECIFIED = 0;
|
||||||
UNSEAL_RESULT_SUCCESS = 1;
|
UNSEAL_RESULT_SUCCESS = 1;
|
||||||
UNSEAL_RESULT_INVALID_KEY = 2;
|
UNSEAL_RESULT_INVALID_KEY = 2;
|
||||||
UNSEAL_RESULT_UNBOOTSTRAPPED = 3;
|
UNSEAL_RESULT_UNBOOTSTRAPPED = 3;
|
||||||
|
UNSEAL_RESULT_AWAITING_CONTRIBUTIONS = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Request {
|
message Request {
|
||||||
oneof payload {
|
oneof payload {
|
||||||
UnsealStart start = 1;
|
UnsealStart start = 1;
|
||||||
UnsealEncryptedKey encrypted_key = 2;
|
UnsealEncryptedKey encrypted_key = 2;
|
||||||
|
ContributePassphrase contribute_passphrase = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
grpc::{Convert, TryConvert},
|
grpc::{Convert, TryConvert},
|
||||||
peers::operator::vault_gate::{
|
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::{
|
use arbiter_proto::proto::operator::{
|
||||||
operator_request::Payload as OperatorRequestPayload,
|
operator_request::Payload as OperatorRequestPayload,
|
||||||
vault::{
|
vault::{
|
||||||
self as proto_vault,
|
self as proto_vault,
|
||||||
bootstrap::{self as proto_bootstrap},
|
bootstrap::{self as proto_bootstrap, request::Payload as BootstrapRequestPayload},
|
||||||
request::Payload as VaultRequestPayload,
|
request::Payload as VaultRequestPayload,
|
||||||
unseal::{self as proto_unseal, request::Payload as UnsealRequestPayload},
|
unseal::{self as proto_unseal, request::Payload as UnsealRequestPayload},
|
||||||
},
|
},
|
||||||
@@ -73,6 +75,13 @@ impl TryConvert for UnsealRequestPayload {
|
|||||||
match self {
|
match self {
|
||||||
Self::Start(start) => start.try_convert(),
|
Self::Start(start) => start.try_convert(),
|
||||||
Self::EncryptedKey(key) => Ok(key.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;
|
type Error = Status;
|
||||||
|
|
||||||
fn try_convert(self) -> Result<vault_gate::Inbound, Status> {
|
fn try_convert(self) -> Result<vault_gate::Inbound, Status> {
|
||||||
self.encrypted_key
|
self.payload
|
||||||
.ok_or_else(|| Status::invalid_argument("Missing bootstrap encrypted key"))?
|
.ok_or_else(|| Status::invalid_argument("Missing bootstrap payload"))?
|
||||||
.try_convert()
|
.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 {
|
impl TryConvert for proto_bootstrap::BootstrapEncryptedKey {
|
||||||
type Output = vault_gate::Inbound;
|
type Output = vault_gate::Inbound;
|
||||||
type Error = Status;
|
type Error = Status;
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ impl Convert for VaultState {
|
|||||||
fn convert(self) -> OperatorResponsePayload {
|
fn convert(self) -> OperatorResponsePayload {
|
||||||
let proto_state = match self {
|
let proto_state = match self {
|
||||||
Self::Unbootstrapped => ProtoVaultState::Unbootstrapped,
|
Self::Unbootstrapped => ProtoVaultState::Unbootstrapped,
|
||||||
Self::Bootstrapping => ProtoVaultState::Boostrapping,
|
|
||||||
Self::Sealed => ProtoVaultState::Sealed,
|
Self::Sealed => ProtoVaultState::Sealed,
|
||||||
Self::Unsealed => ProtoVaultState::Unsealed,
|
Self::Unsealed => ProtoVaultState::Unsealed,
|
||||||
};
|
};
|
||||||
@@ -111,6 +110,40 @@ impl TryConvert for vault_gate::Outbound {
|
|||||||
};
|
};
|
||||||
Ok(wrap_bootstrap_response(proto_result))
|
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(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::{
|
|||||||
actors::{
|
actors::{
|
||||||
GlobalActors,
|
GlobalActors,
|
||||||
vault::{self, Bootstrap, GetState, TryUnseal, VaultState, events},
|
vault::{self, Bootstrap, GetState, TryUnseal, VaultState, events},
|
||||||
|
vault_coordinator::{ContributeBootstrap, ContributeUnseal, StartBootstrap},
|
||||||
},
|
},
|
||||||
crypto::integrity::{self},
|
crypto::integrity::{self},
|
||||||
db::DatabasePool,
|
db::DatabasePool,
|
||||||
@@ -17,6 +18,9 @@ use tokio::sync::oneshot;
|
|||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
|
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
|
||||||
|
|
||||||
|
pub use VaultGateMessage as Inbound;
|
||||||
|
pub use VaultGateMessageReply as Outbound;
|
||||||
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
@@ -118,8 +122,7 @@ impl VaultGate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[messages(enum)]
|
||||||
#[messages(messages = Inbound, replies = Outbound)]
|
|
||||||
impl VaultGate {
|
impl VaultGate {
|
||||||
#[message]
|
#[message]
|
||||||
pub fn handle_handshake(
|
pub fn handle_handshake(
|
||||||
@@ -234,6 +237,52 @@ impl VaultGate {
|
|||||||
|
|
||||||
Ok(answer)
|
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 {
|
impl Message<events::Bootstrapped> for VaultGate {
|
||||||
|
|||||||
Reference in New Issue
Block a user