fix(server::user_agent): useragents now self-sign themselves on bootstrap

This commit is contained in:
hdbg
2026-04-08 12:34:32 +02:00
parent 1585f90cae
commit 6b8da567dd
36 changed files with 352 additions and 229 deletions

View File

@@ -2,17 +2,9 @@ syntax = "proto3";
package arbiter.user_agent.auth; package arbiter.user_agent.auth;
enum KeyType {
KEY_TYPE_UNSPECIFIED = 0;
KEY_TYPE_ED25519 = 1;
KEY_TYPE_ECDSA_SECP256K1 = 2;
KEY_TYPE_RSA = 3;
}
message AuthChallengeRequest { message AuthChallengeRequest {
bytes pubkey = 1; bytes pubkey = 1;
optional string bootstrap_token = 2; optional string bootstrap_token = 2;
KeyType key_type = 3;
} }
message AuthChallenge { message AuthChallenge {

View File

@@ -8,7 +8,10 @@ use kameo::{
use crate::{ use crate::{
actors::flow_coordinator::ApprovalError, actors::flow_coordinator::ApprovalError,
peers::{client::ClientProfile, user_agent::{UserAgentSession, session::BeginNewClientApproval}}, peers::{
client::ClientProfile,
user_agent::{UserAgentSession, session::BeginNewClientApproval},
},
}; };
pub struct Args { pub struct Args {

View File

@@ -9,7 +9,13 @@ use kameo::{
}; };
use tracing::info; use tracing::info;
use crate::{actors::flow_coordinator::client_connect_approval::ClientApprovalController, peers::{client::{ClientProfile, session::ClientSession}, user_agent::UserAgentSession}}; use crate::{
actors::flow_coordinator::client_connect_approval::ClientApprovalController,
peers::{
client::{ClientProfile, session::ClientSession},
user_agent::UserAgentSession,
},
};
pub mod client_connect_approval; pub mod client_connect_approval;

View File

@@ -4,8 +4,7 @@ use thiserror::Error;
use crate::{ use crate::{
actors::{ actors::{
bootstrap::Bootstrapper, evm::EvmActor, flow_coordinator::FlowCoordinator, bootstrap::Bootstrapper, evm::EvmActor, flow_coordinator::FlowCoordinator, vault::Vault,
vault::Vault,
}, },
db, db,
}; };

View File

@@ -147,7 +147,7 @@ impl Vault {
Ok(nonce) Ok(nonce)
} }
fn expect_unsealed<'a>(state: &'a mut State) -> Result<&'a mut Unsealed, Error> { fn expect_unsealed(state: &mut State) -> Result<&mut Unsealed, Error> {
match state { match state {
State::Unsealed(unsealed) => Ok(unsealed), State::Unsealed(unsealed) => Ok(unsealed),
State::Unbootstrapped => Err(Error::NotBootstrapped), State::Unbootstrapped => Err(Error::NotBootstrapped),

View File

@@ -1,6 +1,8 @@
use crate::{actors::vault, crypto::integrity::hashing::Hashable}; use crate::{
use arbiter_crypto::safecell::SafeCellHandle as _; actors::vault::{self, GetState},
use hmac::{Hmac, Mac as _}; crypto::integrity::hashing::Hashable,
};
use hmac::Hmac;
use sha2::Sha256; use sha2::Sha256;
use diesel::{ExpressionMethods as _, QueryDsl, dsl::insert_into, sqlite::Sqlite}; use diesel::{ExpressionMethods as _, QueryDsl, dsl::insert_into, sqlite::Sqlite};
@@ -199,6 +201,11 @@ pub async fn verify_entity<E: Integrable>(
} }
} }
pub async fn is_signing_available(vault: &ActorRef<Vault>) -> Result<bool, Error> {
let state = vault.ask(GetState).await.map_err(|_| Error::VaultSend)?;
Ok(matches!(state, vault::VaultState::Unsealed))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use diesel::{ExpressionMethods as _, QueryDsl}; use diesel::{ExpressionMethods as _, QueryDsl};
@@ -208,7 +215,10 @@ mod tests {
use sha2::Digest; use sha2::Digest;
use crate::{ use crate::{
actors::{GlobalActors, vault::{Bootstrap, Vault}}, actors::{
GlobalActors,
vault::{Bootstrap, Vault},
},
db::{self, schema}, db::{self, schema},
}; };
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};

View File

@@ -10,8 +10,8 @@ use tonic::Status;
use tracing::{info, warn}; use tracing::{info, warn};
use crate::{ use crate::{
peers::client::{ClientConnection, session::ClientSession},
grpc::request_tracker::RequestTracker, grpc::request_tracker::RequestTracker,
peers::client::{ClientConnection, session::ClientSession},
}; };
mod auth; mod auth;

View File

@@ -22,8 +22,8 @@ use tonic::Status;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
peers::client::{self, ClientConnection, auth},
grpc::request_tracker::RequestTracker, grpc::request_tracker::RequestTracker,
peers::client::{self, ClientConnection, auth},
}; };
pub struct AuthTransportAdapter<'a> { pub struct AuthTransportAdapter<'a> {

View File

@@ -16,11 +16,11 @@ use tonic::Status;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
peers::client::session::{ClientSession, HandleSignTransaction, SignTransactionRpcError},
grpc::{ grpc::{
Convert, TryConvert, Convert, TryConvert,
common::inbound::{RawEvmAddress, RawEvmTransaction}, common::inbound::{RawEvmAddress, RawEvmTransaction},
}, },
peers::client::session::{ClientSession, HandleSignTransaction, SignTransactionRpcError},
}; };
fn wrap_response(payload: EvmResponsePayload) -> ClientResponsePayload { fn wrap_response(payload: EvmResponsePayload) -> ClientResponsePayload {

View File

@@ -13,8 +13,8 @@ use tonic::Status;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
peers::client::session::{ClientSession, Error, HandleQueryVaultState},
actors::vault::VaultState, actors::vault::VaultState,
peers::client::session::{ClientSession, Error, HandleQueryVaultState},
}; };
pub(super) async fn dispatch( pub(super) async fn dispatch(

View File

@@ -10,8 +10,8 @@ use tonic::{Request, Response, Status, async_trait};
use tracing::info; use tracing::info;
use crate::{ use crate::{
peers::{client::ClientConnection, user_agent::UserAgentConnection},
grpc::user_agent::start, grpc::user_agent::start,
peers::{client::ClientConnection, user_agent::UserAgentConnection},
}; };
mod request_tracker; mod request_tracker;

View File

@@ -14,8 +14,8 @@ use tonic::Status;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use crate::{ use crate::{
peers::user_agent::{OutOfBand, UserAgentConnection, UserAgentSession},
grpc::request_tracker::RequestTracker, grpc::request_tracker::RequestTracker,
peers::user_agent::{OutOfBand, UserAgentConnection, UserAgentSession},
}; };
mod auth; mod auth;
@@ -124,7 +124,7 @@ pub async fn start(
) { ) {
let mut request_tracker = RequestTracker::default(); let mut request_tracker = RequestTracker::default();
let pubkey = match auth::start(&mut conn, &mut bi, &mut request_tracker).await { let (id, pubkey) = match auth::start(&mut conn, &mut bi, &mut request_tracker).await {
Ok(pubkey) => pubkey, Ok(pubkey) => pubkey,
Err(e) => { Err(e) => {
warn!(error = ?e, "Authentication failed"); warn!(error = ?e, "Authentication failed");
@@ -132,13 +132,19 @@ pub async fn start(
} }
}; };
info!(?pubkey, "User authenticated successfully");
let (oob_sender, oob_receiver) = mpsc::channel(16); let (oob_sender, oob_receiver) = mpsc::channel(16);
let oob_adapter = OutOfBandAdapter(oob_sender); let oob_adapter = OutOfBandAdapter(oob_sender);
let actor = UserAgentSession::spawn(UserAgentSession::new(conn, Box::new(oob_adapter))); let actor = UserAgentSession::spawn(UserAgentSession::new(
conn,
id,
pubkey,
Box::new(oob_adapter),
));
let actor_for_cleanup = actor.clone(); let actor_for_cleanup = actor.clone();
info!(?pubkey, "User authenticated successfully");
dispatch_loop(bi, actor, oob_receiver, request_tracker).await; dispatch_loop(bi, actor, oob_receiver, request_tracker).await;
actor_for_cleanup.kill(); actor_for_cleanup.kill();
} }

View File

@@ -18,8 +18,8 @@ use tonic::Status;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
peers::user_agent::{UserAgentConnection, auth},
grpc::request_tracker::RequestTracker, grpc::request_tracker::RequestTracker,
peers::user_agent::{UserAgentConnection, auth},
}; };
pub struct AuthTransportAdapter<'a> { pub struct AuthTransportAdapter<'a> {
@@ -140,7 +140,6 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
AuthRequestPayload::ChallengeRequest(ProtoAuthChallengeRequest { AuthRequestPayload::ChallengeRequest(ProtoAuthChallengeRequest {
pubkey, pubkey,
bootstrap_token, bootstrap_token,
key_type: _,
}) => { }) => {
let Ok(pubkey) = authn::PublicKey::try_from(pubkey.as_slice()) else { let Ok(pubkey) = authn::PublicKey::try_from(pubkey.as_slice()) else {
warn!( warn!(
@@ -168,7 +167,7 @@ pub async fn start(
conn: &mut UserAgentConnection, conn: &mut UserAgentConnection,
bi: &mut GrpcBi<UserAgentRequest, UserAgentResponse>, bi: &mut GrpcBi<UserAgentRequest, UserAgentResponse>,
request_tracker: &mut RequestTracker, request_tracker: &mut RequestTracker,
) -> Result<authn::PublicKey, auth::Error> { ) -> Result<(i32, authn::PublicKey), auth::Error> {
let transport = AuthTransportAdapter::new(bi, request_tracker); let transport = AuthTransportAdapter::new(bi, request_tracker);
auth::authenticate(conn, transport).await auth::authenticate(conn, transport).await
} }

View File

@@ -23,6 +23,10 @@ use tonic::Status;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
grpc::{
Convert, TryConvert,
common::inbound::{RawEvmAddress, RawEvmTransaction},
},
peers::user_agent::{ peers::user_agent::{
UserAgentSession, UserAgentSession,
session::connection::{ session::connection::{
@@ -31,10 +35,6 @@ use crate::{
SignTransactionError as SessionSignTransactionError, SignTransactionError as SessionSignTransactionError,
}, },
}, },
grpc::{
Convert, TryConvert,
common::inbound::{RawEvmAddress, RawEvmTransaction},
},
}; };
fn wrap_evm_response(payload: EvmResponsePayload) -> UserAgentResponsePayload { fn wrap_evm_response(payload: EvmResponsePayload) -> UserAgentResponsePayload {

View File

@@ -21,6 +21,8 @@ use tonic::Status;
use tracing::{info, warn}; use tracing::{info, warn};
use crate::{ use crate::{
db::models::NewEvmWalletAccess,
grpc::Convert,
peers::user_agent::{ peers::user_agent::{
OutOfBand, UserAgentSession, OutOfBand, UserAgentSession,
session::connection::{ session::connection::{
@@ -28,8 +30,6 @@ use crate::{
HandleRevokeEvmWalletAccess, HandleSdkClientList, HandleRevokeEvmWalletAccess, HandleSdkClientList,
}, },
}, },
db::models::NewEvmWalletAccess,
grpc::Convert,
}; };
fn wrap_sdk_client_response(payload: SdkClientResponsePayload) -> UserAgentResponsePayload { fn wrap_sdk_client_response(payload: SdkClientResponsePayload) -> UserAgentResponsePayload {

View File

@@ -2,12 +2,12 @@
use crate::context::ServerContext; use crate::context::ServerContext;
pub mod actors; pub mod actors;
pub mod peers;
pub mod context; pub mod context;
pub mod crypto; pub mod crypto;
pub mod db; pub mod db;
pub mod evm; pub mod evm;
pub mod grpc; pub mod grpc;
pub mod peers;
pub mod utils; pub mod utils;
pub struct Server { pub struct Server {

View File

@@ -14,7 +14,9 @@ use tracing::error;
use crate::{ use crate::{
actors::{ actors::{
GlobalActors, flow_coordinator::{self, RequestClientApproval}, vault::Vault GlobalActors,
flow_coordinator::{self, RequestClientApproval},
vault::Vault,
}, },
crypto::integrity::{self, AttestationStatus}, crypto::integrity::{self, AttestationStatus},
db::{ db::{
@@ -187,10 +189,7 @@ async fn create_nonce(
.await .await
} }
async fn approve_new_client( async fn approve_new_client(actors: &GlobalActors, profile: ClientProfile) -> Result<(), Error> {
actors: &GlobalActors,
profile: ClientProfile,
) -> Result<(), Error> {
let result = actors let result = actors
.flow_coordinator .flow_coordinator
.ask(RequestClientApproval { client: profile }) .ask(RequestClientApproval { client: profile })

View File

@@ -6,7 +6,8 @@ use tracing::{error, info};
use crate::{ use crate::{
actors::GlobalActors, actors::GlobalActors,
crypto::integrity::{Integrable, hashing::Hashable}, crypto::integrity::{Integrable, hashing::Hashable},
db, peers::client::session::ClientSession, db,
peers::client::session::ClientSession,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -1,2 +1,2 @@
pub mod user_agent;
pub mod client; pub mod client;
pub mod user_agent;

View File

@@ -69,7 +69,7 @@ fn parse_auth_event(payload: Inbound) -> AuthEvents {
pub async fn authenticate<T>( pub async fn authenticate<T>(
props: &mut UserAgentConnection, props: &mut UserAgentConnection,
transport: T, transport: T,
) -> Result<authn::PublicKey, Error> ) -> Result<(i32, authn::PublicKey), Error>
where where
T: Bi<Inbound, Result<Outbound, Error>> + Send, T: Bi<Inbound, Result<Outbound, Error>> + Send,
{ {
@@ -82,7 +82,7 @@ where
}; };
match state.process_event(parse_auth_event(payload)).await { match state.process_event(parse_auth_event(payload)).await {
Ok(AuthStates::AuthOk(key)) => return Ok(key.clone()), Ok(AuthStates::AuthOk(result)) => return Ok((result.id, result.pubkey.clone())),
Err(AuthError::ActionFailed(err)) => { Err(AuthError::ActionFailed(err)) => {
error!(?err, "State machine action failed"); error!(?err, "State machine action failed");
return Err(err); return Err(err);

View File

@@ -1,18 +1,15 @@
use super::super::{UserAgentConnection, UserAgentCredentials};
use arbiter_crypto::authn::{self, USERAGENT_CONTEXT}; use arbiter_crypto::authn::{self, USERAGENT_CONTEXT};
use arbiter_proto::transport::Bi; use arbiter_proto::transport::Bi;
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, update}; use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, update};
use diesel_async::{AsyncConnection, RunQueryDsl}; use diesel_async::{AsyncConnection, RunQueryDsl};
use kameo::actor::ActorRef; use kameo::actor::ActorRef;
use tracing::error; use tracing::error;
use super::super::{UserAgentCredentials, UserAgentConnection};
use super::Error; use super::Error;
use crate::peers::user_agent::auth::Outbound; use crate::peers::user_agent::auth::Outbound;
use crate::{ use crate::{
actors::{ actors::{bootstrap::ConsumeToken, vault::Vault},
bootstrap::ConsumeToken,
vault::Vault,
},
crypto::integrity, crypto::integrity,
db::{DatabasePool, schema::useragent_client}, db::{DatabasePool, schema::useragent_client},
}; };
@@ -27,6 +24,7 @@ pub struct BootstrapAuthRequest {
} }
pub struct ChallengeContext { pub struct ChallengeContext {
pub id: i32,
pub challenge_nonce: i32, pub challenge_nonce: i32,
pub key: authn::PublicKey, pub key: authn::PublicKey,
} }
@@ -35,13 +33,18 @@ pub struct ChallengeSolution {
pub solution: Vec<u8>, pub solution: Vec<u8>,
} }
pub struct AuthOk {
pub id: i32,
pub pubkey: authn::PublicKey,
}
smlang::statemachine!( smlang::statemachine!(
name: Auth, name: Auth,
custom_error: true, custom_error: true,
transitions: { transitions: {
*Init + AuthRequest(ChallengeRequest) / async prepare_challenge = SentChallenge(ChallengeContext), *Init + AuthRequest(ChallengeRequest) / async prepare_challenge = SentChallenge(ChallengeContext),
Init + BootstrapAuthRequest(BootstrapAuthRequest) / async verify_bootstrap_token = AuthOk(authn::PublicKey), Init + BootstrapAuthRequest(BootstrapAuthRequest) / async verify_bootstrap_token = AuthOk(AuthOk),
SentChallenge(ChallengeContext) + ReceivedSolution(ChallengeSolution) / async verify_solution = AuthOk(authn::PublicKey), SentChallenge(ChallengeContext) + ReceivedSolution(ChallengeSolution) / async verify_solution = AuthOk(AuthOk),
} }
); );
@@ -110,12 +113,12 @@ async fn create_nonce(
db: &DatabasePool, db: &DatabasePool,
vault: &ActorRef<Vault>, vault: &ActorRef<Vault>,
pubkey: &authn::PublicKey, pubkey: &authn::PublicKey,
) -> Result<i32, Error> { ) -> Result<(i32, i32), Error> {
let mut db_conn = db.get().await.map_err(|e| { let mut db_conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error"); error!(error = ?e, "Database pool error");
Error::internal("Database unavailable") Error::internal("Database unavailable")
})?; })?;
let new_nonce = db_conn let (id, new_nonce) = db_conn
.exclusive_transaction(|conn| { .exclusive_transaction(|conn| {
Box::pin(async move { Box::pin(async move {
let (id, new_nonce): (i32, i32) = update(useragent_client::table) let (id, new_nonce): (i32, i32) = update(useragent_client::table)
@@ -144,59 +147,36 @@ async fn create_nonce(
Error::internal("Database error") Error::internal("Database error")
})?; })?;
Result::<_, Error>::Ok(new_nonce) Result::<_, Error>::Ok((id, new_nonce))
}) })
}) })
.await?; .await?;
Ok(new_nonce) Ok((id, new_nonce))
} }
async fn register_key( async fn register_key(db: &DatabasePool, pubkey: &authn::PublicKey) -> Result<i32, Error> {
db: &DatabasePool,
vault: &ActorRef<Vault>,
pubkey: &authn::PublicKey,
) -> Result<(), Error> {
let pubkey_bytes = pubkey.to_bytes(); let pubkey_bytes = pubkey.to_bytes();
let mut conn = db.get().await.map_err(|e| { let mut conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error"); error!(error = ?e, "Database pool error");
Error::internal("Database unavailable") Error::internal("Database unavailable")
})?; })?;
conn.transaction(|conn| { const NONCE_START: i32 = 1;
Box::pin(async move {
const NONCE_START: i32 = 1;
let id: i32 = diesel::insert_into(useragent_client::table) let id: i32 = diesel::insert_into(useragent_client::table)
.values(( .values((
useragent_client::public_key.eq(pubkey_bytes), useragent_client::public_key.eq(pubkey_bytes),
useragent_client::nonce.eq(NONCE_START), useragent_client::nonce.eq(NONCE_START),
)) ))
.returning(useragent_client::id) .returning(useragent_client::id)
.get_result(conn) .get_result(&mut conn)
.await .await
.map_err(|e| { .map_err(|e| {
error!(error = ?e, "Database error"); error!(error = ?e, "Database error");
Error::internal("Database operation failed") Error::internal("Database operation failed")
})?; })?;
let entity = UserAgentCredentials { Ok(id)
pubkey: pubkey.clone(),
nonce: NONCE_START,
};
integrity::sign_entity(conn, vault, &entity, id)
.await
.map_err(|e| {
error!(error = ?e, "Failed to sign integrity tag for new user-agent key");
Error::internal("Failed to register public key")
})?;
Result::<_, Error>::Ok(())
})
})
.await?;
Ok(())
} }
pub struct AuthContext<'a, T> { pub struct AuthContext<'a, T> {
@@ -222,7 +202,7 @@ where
) -> Result<ChallengeContext, Self::Error> { ) -> Result<ChallengeContext, Self::Error> {
verify_integrity(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; verify_integrity(&self.conn.db, &self.conn.actors.vault, &pubkey).await?;
let nonce = create_nonce(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; let (id, nonce) = create_nonce(&self.conn.db, &self.conn.actors.vault, &pubkey).await?;
self.transport self.transport
.send(Ok(Outbound::AuthChallenge { nonce })) .send(Ok(Outbound::AuthChallenge { nonce }))
@@ -233,6 +213,7 @@ where
})?; })?;
Ok(ChallengeContext { Ok(ChallengeContext {
id,
challenge_nonce: nonce, challenge_nonce: nonce,
key: pubkey, key: pubkey,
}) })
@@ -243,7 +224,7 @@ where
async fn verify_bootstrap_token( async fn verify_bootstrap_token(
&mut self, &mut self,
BootstrapAuthRequest { pubkey, token }: BootstrapAuthRequest, BootstrapAuthRequest { pubkey, token }: BootstrapAuthRequest,
) -> Result<authn::PublicKey, Self::Error> { ) -> Result<AuthOk, Self::Error> {
let token_ok: bool = self let token_ok: bool = self
.conn .conn
.actors .actors
@@ -264,12 +245,12 @@ where
match token_ok { match token_ok {
true => { true => {
register_key(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; let id = register_key(&self.conn.db, &pubkey).await?;
self.transport self.transport
.send(Ok(Outbound::AuthSuccess)) .send(Ok(Outbound::AuthSuccess))
.await .await
.map_err(|_| Error::Transport)?; .map_err(|_| Error::Transport)?;
Ok(pubkey) Ok(AuthOk { id, pubkey })
} }
false => { false => {
error!("Invalid bootstrap token provided"); error!("Invalid bootstrap token provided");
@@ -287,11 +268,12 @@ where
async fn verify_solution( async fn verify_solution(
&mut self, &mut self,
ChallengeContext { ChallengeContext {
id,
challenge_nonce, challenge_nonce,
key, key,
}: &ChallengeContext, }: &ChallengeContext,
ChallengeSolution { solution }: ChallengeSolution, ChallengeSolution { solution }: ChallengeSolution,
) -> Result<authn::PublicKey, Self::Error> { ) -> Result<AuthOk, Self::Error> {
let signature = authn::Signature::try_from(solution.as_slice()).map_err(|_| { let signature = authn::Signature::try_from(solution.as_slice()).map_err(|_| {
error!("Failed to decode signature in challenge solution"); error!("Failed to decode signature in challenge solution");
Error::InvalidChallengeSolution Error::InvalidChallengeSolution
@@ -305,7 +287,7 @@ where
.send(Ok(Outbound::AuthSuccess)) .send(Ok(Outbound::AuthSuccess))
.await .await
.map_err(|_| Error::Transport)?; .map_err(|_| Error::Transport)?;
Ok(key.clone()) Ok(AuthOk { id: *id, pubkey: key.clone() })
} }
false => { false => {
self.transport self.transport

View File

@@ -1,7 +1,5 @@
use crate::{ use crate::{
actors::GlobalActors, actors::GlobalActors, crypto::integrity::Integrable, db, peers::client::ClientProfile,
crypto::integrity::Integrable,
db, peers::client::ClientProfile,
}; };
use arbiter_crypto::authn; use arbiter_crypto::authn;

View File

@@ -1,14 +1,22 @@
use arbiter_crypto::authn; use arbiter_crypto::authn;
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::{AsyncConnection, RunQueryDsl};
use kameo_actors::message_bus::Register;
use std::{borrow::Cow, collections::HashMap}; use std::{borrow::Cow, collections::HashMap};
use arbiter_proto::transport::Sender; use arbiter_proto::transport::Sender;
use async_trait::async_trait; use async_trait::async_trait;
use kameo::{Actor, actor::ActorRef, messages}; use kameo::{Actor, actor::ActorRef, messages, prelude::Message};
use thiserror::Error; use thiserror::Error;
use tracing::error; use tracing::error;
use crate::{actors::flow_coordinator::{RegisterUserAgent, client_connect_approval::ClientApprovalController}, peers::client::ClientProfile}; use crate::{
actors::{
flow_coordinator::{RegisterUserAgent, client_connect_approval::ClientApprovalController},
vault::events,
}, crypto::integrity, db::schema::useragent_client, peers::{client::ClientProfile, user_agent::UserAgentCredentials}
};
mod state; mod state;
use state::{DummyContext, UserAgentEvents, UserAgentStateMachine}; use state::{DummyContext, UserAgentEvents, UserAgentStateMachine};
@@ -50,6 +58,8 @@ pub struct PendingClientApproval {
} }
pub struct UserAgentSession { pub struct UserAgentSession {
id: i32,
pubkey: authn::PublicKey,
props: UserAgentConnection, props: UserAgentConnection,
state: UserAgentStateMachine<DummyContext>, state: UserAgentStateMachine<DummyContext>,
sender: Box<dyn Sender<OutOfBand>>, sender: Box<dyn Sender<OutOfBand>>,
@@ -60,31 +70,22 @@ pub struct UserAgentSession {
pub mod connection; pub mod connection;
impl UserAgentSession { impl UserAgentSession {
pub(crate) fn new(props: UserAgentConnection, sender: Box<dyn Sender<OutOfBand>>) -> Self { pub(crate) fn new(
props: UserAgentConnection,
id: i32,
pubkey: authn::PublicKey,
sender: Box<dyn Sender<OutOfBand>>,
) -> Self {
Self { Self {
id,
props, props,
pubkey,
state: UserAgentStateMachine::new(DummyContext), state: UserAgentStateMachine::new(DummyContext),
sender, sender,
pending_client_approvals: Default::default(), pending_client_approvals: Default::default(),
} }
} }
pub fn new_test(db: crate::db::DatabasePool, actors: crate::actors::GlobalActors) -> Self {
struct DummySender;
#[async_trait]
impl Sender<OutOfBand> for DummySender {
async fn send(
&mut self,
_item: OutOfBand,
) -> Result<(), arbiter_proto::transport::Error> {
Ok(())
}
}
Self::new(UserAgentConnection::new(db, actors), Box::new(DummySender))
}
fn transition(&mut self, event: UserAgentEvents) -> Result<(), Error> { fn transition(&mut self, event: UserAgentEvents) -> Result<(), Error> {
self.state.process_event(event).map_err(|e| { self.state.process_event(event).map_err(|e| {
error!(?e, "State transition failed"); error!(?e, "State transition failed");
@@ -127,6 +128,61 @@ impl UserAgentSession {
} }
} }
impl Message<events::VaultBootstrapped> for UserAgentSession {
type Reply = Result<(), Error>;
async fn handle(
&mut self,
_: events::VaultBootstrapped,
ctx: &mut kameo::prelude::Context<Self, Self::Reply>,
) -> Self::Reply {
let Ok(mut conn) = self.props.db.get().await else {
error!("Failed to get database connection for vault bootstrapped event");
ctx.stop();
return Err(Error::internal("Failed to get database connection"));
};
let result = conn.exclusive_transaction(|conn| {
Box::pin(async {
let nonce: i32 = useragent_client::table
.filter(useragent_client::id.eq(self.id))
.select(useragent_client::nonce)
.first::<i32>(conn)
.await
.map_err(|e| {
error!(?e, "Failed to get nonce for useragent bootstrapping");
Error::internal("Failed to sign user agent credentials")
})?;
let entity = UserAgentCredentials {
pubkey: self.pubkey.clone(),
nonce,
};
integrity::sign_entity(conn, &self.props.actors.vault, &entity, self.id)
.await
.map_err(|e| {
error!(?e, "Failed to sign user agent credentials during vault bootstrapping");
Error::internal("Failed to sign user agent credentials")
})?;
Result::<_, Error>::Ok(())
})
}).await;
match result {
Ok(_) => Ok(()),
Err(err) => {
error!(?err, "Error during vault bootstrapping");
ctx.stop();
Err(err)
},
}
}
}
impl Actor for UserAgentSession { impl Actor for UserAgentSession {
type Args = Self; type Args = Self;
@@ -136,6 +192,21 @@ impl Actor for UserAgentSession {
args: Self::Args, args: Self::Args,
this: kameo::prelude::ActorRef<Self>, this: kameo::prelude::ActorRef<Self>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
args.props
.actors
.events
.tell(Register(
this.clone().recipient::<events::VaultBootstrapped>(),
))
.await
.map_err(|err| {
error!(
?err,
"Failed to register user agent connection with event bus"
);
Error::internal("Failed to register user agent connection with event bus")
})?;
args.props args.props
.actors .actors
.flow_coordinator .flow_coordinator

View File

@@ -14,7 +14,7 @@ use kameo::prelude::Context;
use tracing::{error, info}; use tracing::{error, info};
use x25519_dalek::{EphemeralSecret, PublicKey}; use x25519_dalek::{EphemeralSecret, PublicKey};
use crate::{actors::vault::VaultState, peers::user_agent::session::state::{UnsealContext, UserAgentEvents}}; use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer;
use crate::actors::{ use crate::actors::{
evm::{ evm::{
ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError, ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError,
@@ -27,10 +27,11 @@ use crate::db::models::{
}; };
use crate::evm::policies::{Grant, SpecificGrant}; use crate::evm::policies::{Grant, SpecificGrant};
use crate::{ use crate::{
actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer, actors::vault::VaultState,
peers::user_agent::session::state::{UnsealContext, UserAgentEvents},
}; };
use super::{UserAgentSession, state, Error}; use super::{Error, UserAgentSession, state};
impl UserAgentSession { impl UserAgentSession {
fn take_unseal_secret(&mut self) -> Result<(EphemeralSecret, PublicKey), Error> { fn take_unseal_secret(&mut self) -> Result<(EphemeralSecret, PublicKey), Error> {

View File

@@ -5,14 +5,10 @@ use arbiter_crypto::{
use arbiter_proto::ClientMetadata; use arbiter_proto::ClientMetadata;
use arbiter_proto::transport::{Receiver, Sender}; use arbiter_proto::transport::{Receiver, Sender};
use arbiter_server::{ use arbiter_server::{
actors::{ actors::{GlobalActors, vault::Bootstrap},
GlobalActors,
vault::Bootstrap,
},
peers::client::{ClientConnection, ClientCredentials, auth, connect_client},
crypto::integrity, crypto::integrity,
db::{self, schema}, db::{self, schema},
peers::client::{ClientConnection, ClientCredentials, auth, connect_client},
}; };
use diesel::{ExpressionMethods as _, NullableExpressionMethods as _, QueryDsl as _, insert_into}; use diesel::{ExpressionMethods as _, NullableExpressionMethods as _, QueryDsl as _, insert_into};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;

View File

@@ -12,7 +12,9 @@ use tokio::sync::mpsc;
#[allow(dead_code)] #[allow(dead_code)]
pub async fn bootstrapped_vault(db: &db::DatabasePool) -> Vault { pub async fn bootstrapped_vault(db: &db::DatabasePool) -> Vault {
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
.await
.unwrap();
actor actor
.bootstrap(SafeCell::new(b"test-seal-key".to_vec())) .bootstrap(SafeCell::new(b"test-seal-key".to_vec()))
.await .await

View File

@@ -5,15 +5,10 @@ use arbiter_crypto::{
use arbiter_proto::transport::{Receiver, Sender}; use arbiter_proto::transport::{Receiver, Sender};
use arbiter_server::{ use arbiter_server::{
actors::{ actors::{GlobalActors, bootstrap::GetToken, vault::Bootstrap},
GlobalActors,
bootstrap::GetToken,
vault::Bootstrap,
},
peers::user_agent::{UserAgentConnection, UserAgentCredentials, auth},
crypto::integrity, crypto::integrity,
db::{self, schema}, db::{self, schema},
peers::user_agent::{UserAgentConnection, UserAgentCredentials, auth},
}; };
use diesel::{ExpressionMethods as _, QueryDsl, insert_into}; use diesel::{ExpressionMethods as _, QueryDsl, insert_into};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;

View File

@@ -3,13 +3,12 @@ use arbiter_server::{
actors::{ actors::{
GlobalActors, GlobalActors,
vault::{Bootstrap, Seal}, vault::{Bootstrap, Seal},
}, },
peers::user_agent::{
UserAgentSession,
session::connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError},
},
db, db,
peers::user_agent::{
UserAgentSession,
session::connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError},
},
}; };
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit}; use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};

View File

@@ -2,7 +2,10 @@ use std::collections::{HashMap, HashSet};
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
use arbiter_server::{ use arbiter_server::{
actors::{GlobalActors, vault::{CreateNew, Error, Vault}}, actors::{
GlobalActors,
vault::{CreateNew, Error, Vault},
},
db::{self, models, schema}, db::{self, models, schema},
}; };
@@ -161,7 +164,9 @@ async fn decrypt_roundtrip_after_high_concurrency() {
let writes = write_concurrently(actor, "roundtrip", 40).await; let writes = write_concurrently(actor, "roundtrip", 40).await;
let expected: HashMap<i32, Vec<u8>> = writes.into_iter().collect(); let expected: HashMap<i32, Vec<u8>> = writes.into_iter().collect();
let mut decryptor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let mut decryptor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
.await
.unwrap();
decryptor decryptor
.try_unseal(SafeCell::new(b"test-seal-key".to_vec())) .try_unseal(SafeCell::new(b"test-seal-key".to_vec()))
.await .await

View File

@@ -1,12 +1,11 @@
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
use arbiter_server::{ use arbiter_server::{
actors::{GlobalActors, vault::{Error, Vault}}, actors::{
GlobalActors,
vault::{Error, Vault},
},
crypto::encryption::v1::{Nonce, ROOT_KEY_TAG}, crypto::encryption::v1::{Nonce, ROOT_KEY_TAG},
db::{self, models, schema}, db::{self, models, schema},
peers::user_agent::{
UserAgentSession,
session::connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError},
},
}; };
use diesel::{QueryDsl, SelectableHelper}; use diesel::{QueryDsl, SelectableHelper};
@@ -18,7 +17,9 @@ use crate::common;
#[test_log::test] #[test_log::test]
async fn test_bootstrap() { async fn test_bootstrap() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
.await
.unwrap();
let seal_key = SafeCell::new(b"test-seal-key".to_vec()); let seal_key = SafeCell::new(b"test-seal-key".to_vec());
actor.bootstrap(seal_key).await.unwrap(); actor.bootstrap(seal_key).await.unwrap();
@@ -52,7 +53,9 @@ async fn test_bootstrap_rejects_double() {
#[test_log::test] #[test_log::test]
async fn test_create_new_before_bootstrap_fails() { async fn test_create_new_before_bootstrap_fails() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
let mut actor = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db, GlobalActors::spawn_message_bus())
.await
.unwrap();
let err = actor let err = actor
.create_new(SafeCell::new(b"data".to_vec())) .create_new(SafeCell::new(b"data".to_vec()))
@@ -65,7 +68,9 @@ async fn test_create_new_before_bootstrap_fails() {
#[test_log::test] #[test_log::test]
async fn test_decrypt_before_bootstrap_fails() { async fn test_decrypt_before_bootstrap_fails() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
let mut actor = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db, GlobalActors::spawn_message_bus())
.await
.unwrap();
let err = actor.decrypt(1).await.unwrap_err(); let err = actor.decrypt(1).await.unwrap_err();
assert!(matches!(err, Error::NotBootstrapped)); assert!(matches!(err, Error::NotBootstrapped));
@@ -78,7 +83,9 @@ async fn test_new_restores_sealed_state() {
let actor = common::bootstrapped_vault(&db).await; let actor = common::bootstrapped_vault(&db).await;
drop(actor); drop(actor);
let mut actor2 = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor2 = Vault::new(db, GlobalActors::spawn_message_bus())
.await
.unwrap();
let err = actor2.decrypt(1).await.unwrap_err(); let err = actor2.decrypt(1).await.unwrap_err();
assert!(matches!(err, Error::NotBootstrapped)); assert!(matches!(err, Error::NotBootstrapped));
} }
@@ -96,7 +103,9 @@ async fn test_unseal_correct_password() {
.unwrap(); .unwrap();
drop(actor); drop(actor);
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
.await
.unwrap();
let seal_key = SafeCell::new(b"test-seal-key".to_vec()); let seal_key = SafeCell::new(b"test-seal-key".to_vec());
actor.try_unseal(seal_key).await.unwrap(); actor.try_unseal(seal_key).await.unwrap();
@@ -117,7 +126,9 @@ async fn test_unseal_wrong_then_correct_password() {
.unwrap(); .unwrap();
drop(actor); drop(actor);
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
.await
.unwrap();
let bad_key = SafeCell::new(b"wrong-password".to_vec()); let bad_key = SafeCell::new(b"wrong-password".to_vec());
let err = actor.try_unseal(bad_key).await.unwrap_err(); let err = actor.try_unseal(bad_key).await.unwrap_err();

View File

@@ -61,11 +61,6 @@ Future<Connection> connectAndAuthorize(
final req = ua_auth.AuthChallengeRequest( final req = ua_auth.AuthChallengeRequest(
pubkey: pubkey, pubkey: pubkey,
bootstrapToken: bootstrapToken, bootstrapToken: bootstrapToken,
keyType: switch (key.alg) {
KeyAlgorithm.rsa => ua_auth.KeyType.KEY_TYPE_RSA,
KeyAlgorithm.ecdsa => ua_auth.KeyType.KEY_TYPE_ECDSA_SECP256K1,
KeyAlgorithm.ed25519 => ua_auth.KeyType.KEY_TYPE_ED25519,
},
); );
final response = await connection.ask( final response = await connection.ask(
UserAgentRequest(auth: ua_auth.Request(challengeRequest: req)), UserAgentRequest(auth: ua_auth.Request(challengeRequest: req)),

View File

@@ -414,6 +414,79 @@ class GasLimitExceededViolation extends $pb.GeneratedMessage {
void clearMaxPriorityFeePerGas() => $_clearField(2); void clearMaxPriorityFeePerGas() => $_clearField(2);
} }
class EvalViolation_ChainIdMismatch extends $pb.GeneratedMessage {
factory EvalViolation_ChainIdMismatch({
$fixnum.Int64? expected,
$fixnum.Int64? actual,
}) {
final result = create();
if (expected != null) result.expected = expected;
if (actual != null) result.actual = actual;
return result;
}
EvalViolation_ChainIdMismatch._();
factory EvalViolation_ChainIdMismatch.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory EvalViolation_ChainIdMismatch.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'EvalViolation.ChainIdMismatch',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.shared.evm'),
createEmptyInstance: create)
..a<$fixnum.Int64>(
1, _omitFieldNames ? '' : 'expected', $pb.PbFieldType.OU6,
defaultOrMaker: $fixnum.Int64.ZERO)
..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'actual', $pb.PbFieldType.OU6,
defaultOrMaker: $fixnum.Int64.ZERO)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
EvalViolation_ChainIdMismatch clone() => deepCopy();
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
EvalViolation_ChainIdMismatch copyWith(
void Function(EvalViolation_ChainIdMismatch) updates) =>
super.copyWith(
(message) => updates(message as EvalViolation_ChainIdMismatch))
as EvalViolation_ChainIdMismatch;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static EvalViolation_ChainIdMismatch create() =>
EvalViolation_ChainIdMismatch._();
@$core.override
EvalViolation_ChainIdMismatch createEmptyInstance() => create();
@$core.pragma('dart2js:noInline')
static EvalViolation_ChainIdMismatch getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<EvalViolation_ChainIdMismatch>(create);
static EvalViolation_ChainIdMismatch? _defaultInstance;
@$pb.TagNumber(1)
$fixnum.Int64 get expected => $_getI64(0);
@$pb.TagNumber(1)
set expected($fixnum.Int64 value) => $_setInt64(0, value);
@$pb.TagNumber(1)
$core.bool hasExpected() => $_has(0);
@$pb.TagNumber(1)
void clearExpected() => $_clearField(1);
@$pb.TagNumber(2)
$fixnum.Int64 get actual => $_getI64(1);
@$pb.TagNumber(2)
set actual($fixnum.Int64 value) => $_setInt64(1, value);
@$pb.TagNumber(2)
$core.bool hasActual() => $_has(1);
@$pb.TagNumber(2)
void clearActual() => $_clearField(2);
}
enum EvalViolation_Kind { enum EvalViolation_Kind {
invalidTarget, invalidTarget,
gasLimitExceeded, gasLimitExceeded,
@@ -421,6 +494,7 @@ enum EvalViolation_Kind {
volumetricLimitExceeded, volumetricLimitExceeded,
invalidTime, invalidTime,
invalidTransactionType, invalidTransactionType,
chainIdMismatch,
notSet notSet
} }
@@ -432,6 +506,7 @@ class EvalViolation extends $pb.GeneratedMessage {
$0.Empty? volumetricLimitExceeded, $0.Empty? volumetricLimitExceeded,
$0.Empty? invalidTime, $0.Empty? invalidTime,
$0.Empty? invalidTransactionType, $0.Empty? invalidTransactionType,
EvalViolation_ChainIdMismatch? chainIdMismatch,
}) { }) {
final result = create(); final result = create();
if (invalidTarget != null) result.invalidTarget = invalidTarget; if (invalidTarget != null) result.invalidTarget = invalidTarget;
@@ -442,6 +517,7 @@ class EvalViolation extends $pb.GeneratedMessage {
if (invalidTime != null) result.invalidTime = invalidTime; if (invalidTime != null) result.invalidTime = invalidTime;
if (invalidTransactionType != null) if (invalidTransactionType != null)
result.invalidTransactionType = invalidTransactionType; result.invalidTransactionType = invalidTransactionType;
if (chainIdMismatch != null) result.chainIdMismatch = chainIdMismatch;
return result; return result;
} }
@@ -462,6 +538,7 @@ class EvalViolation extends $pb.GeneratedMessage {
4: EvalViolation_Kind.volumetricLimitExceeded, 4: EvalViolation_Kind.volumetricLimitExceeded,
5: EvalViolation_Kind.invalidTime, 5: EvalViolation_Kind.invalidTime,
6: EvalViolation_Kind.invalidTransactionType, 6: EvalViolation_Kind.invalidTransactionType,
7: EvalViolation_Kind.chainIdMismatch,
0: EvalViolation_Kind.notSet 0: EvalViolation_Kind.notSet
}; };
static final $pb.BuilderInfo _i = $pb.BuilderInfo( static final $pb.BuilderInfo _i = $pb.BuilderInfo(
@@ -469,7 +546,7 @@ class EvalViolation extends $pb.GeneratedMessage {
package: package:
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.shared.evm'), const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.shared.evm'),
createEmptyInstance: create) createEmptyInstance: create)
..oo(0, [1, 2, 3, 4, 5, 6]) ..oo(0, [1, 2, 3, 4, 5, 6, 7])
..a<$core.List<$core.int>>( ..a<$core.List<$core.int>>(
1, _omitFieldNames ? '' : 'invalidTarget', $pb.PbFieldType.OY) 1, _omitFieldNames ? '' : 'invalidTarget', $pb.PbFieldType.OY)
..aOM<GasLimitExceededViolation>( ..aOM<GasLimitExceededViolation>(
@@ -483,6 +560,9 @@ class EvalViolation extends $pb.GeneratedMessage {
subBuilder: $0.Empty.create) subBuilder: $0.Empty.create)
..aOM<$0.Empty>(6, _omitFieldNames ? '' : 'invalidTransactionType', ..aOM<$0.Empty>(6, _omitFieldNames ? '' : 'invalidTransactionType',
subBuilder: $0.Empty.create) subBuilder: $0.Empty.create)
..aOM<EvalViolation_ChainIdMismatch>(
7, _omitFieldNames ? '' : 'chainIdMismatch',
subBuilder: EvalViolation_ChainIdMismatch.create)
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -510,6 +590,7 @@ class EvalViolation extends $pb.GeneratedMessage {
@$pb.TagNumber(4) @$pb.TagNumber(4)
@$pb.TagNumber(5) @$pb.TagNumber(5)
@$pb.TagNumber(6) @$pb.TagNumber(6)
@$pb.TagNumber(7)
EvalViolation_Kind whichKind() => _EvalViolation_KindByTag[$_whichOneof(0)]!; EvalViolation_Kind whichKind() => _EvalViolation_KindByTag[$_whichOneof(0)]!;
@$pb.TagNumber(1) @$pb.TagNumber(1)
@$pb.TagNumber(2) @$pb.TagNumber(2)
@@ -517,6 +598,7 @@ class EvalViolation extends $pb.GeneratedMessage {
@$pb.TagNumber(4) @$pb.TagNumber(4)
@$pb.TagNumber(5) @$pb.TagNumber(5)
@$pb.TagNumber(6) @$pb.TagNumber(6)
@$pb.TagNumber(7)
void clearKind() => $_clearField($_whichOneof(0)); void clearKind() => $_clearField($_whichOneof(0));
@$pb.TagNumber(1) @$pb.TagNumber(1)
@@ -582,6 +664,18 @@ class EvalViolation extends $pb.GeneratedMessage {
void clearInvalidTransactionType() => $_clearField(6); void clearInvalidTransactionType() => $_clearField(6);
@$pb.TagNumber(6) @$pb.TagNumber(6)
$0.Empty ensureInvalidTransactionType() => $_ensure(5); $0.Empty ensureInvalidTransactionType() => $_ensure(5);
@$pb.TagNumber(7)
EvalViolation_ChainIdMismatch get chainIdMismatch => $_getN(6);
@$pb.TagNumber(7)
set chainIdMismatch(EvalViolation_ChainIdMismatch value) =>
$_setField(7, value);
@$pb.TagNumber(7)
$core.bool hasChainIdMismatch() => $_has(6);
@$pb.TagNumber(7)
void clearChainIdMismatch() => $_clearField(7);
@$pb.TagNumber(7)
EvalViolation_ChainIdMismatch ensureChainIdMismatch() => $_ensure(6);
} }
/// Transaction was classified but no grant covers it /// Transaction was classified but no grant covers it

View File

@@ -195,12 +195,31 @@ const EvalViolation$json = {
'9': 0, '9': 0,
'10': 'invalidTransactionType' '10': 'invalidTransactionType'
}, },
{
'1': 'chain_id_mismatch',
'3': 7,
'4': 1,
'5': 11,
'6': '.arbiter.shared.evm.EvalViolation.ChainIdMismatch',
'9': 0,
'10': 'chainIdMismatch'
},
], ],
'3': [EvalViolation_ChainIdMismatch$json],
'8': [ '8': [
{'1': 'kind'}, {'1': 'kind'},
], ],
}; };
@$core.Deprecated('Use evalViolationDescriptor instead')
const EvalViolation_ChainIdMismatch$json = {
'1': 'ChainIdMismatch',
'2': [
{'1': 'expected', '3': 1, '4': 1, '5': 4, '10': 'expected'},
{'1': 'actual', '3': 2, '4': 1, '5': 4, '10': 'actual'},
],
};
/// Descriptor for `EvalViolation`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `EvalViolation`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List evalViolationDescriptor = $convert.base64Decode( final $typed_data.Uint8List evalViolationDescriptor = $convert.base64Decode(
'Cg1FdmFsVmlvbGF0aW9uEicKDmludmFsaWRfdGFyZ2V0GAEgASgMSABSDWludmFsaWRUYXJnZX' 'Cg1FdmFsVmlvbGF0aW9uEicKDmludmFsaWRfdGFyZ2V0GAEgASgMSABSDWludmFsaWRUYXJnZX'
@@ -211,7 +230,10 @@ final $typed_data.Uint8List evalViolationDescriptor = $convert.base64Decode(
'YuRW1wdHlIAFIXdm9sdW1ldHJpY0xpbWl0RXhjZWVkZWQSOwoMaW52YWxpZF90aW1lGAUgASgL' 'YuRW1wdHlIAFIXdm9sdW1ldHJpY0xpbWl0RXhjZWVkZWQSOwoMaW52YWxpZF90aW1lGAUgASgL'
'MhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5SABSC2ludmFsaWRUaW1lElIKGGludmFsaWRfdHJhbn' 'MhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5SABSC2ludmFsaWRUaW1lElIKGGludmFsaWRfdHJhbn'
'NhY3Rpb25fdHlwZRgGIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eUgAUhZpbnZhbGlkVHJh' 'NhY3Rpb25fdHlwZRgGIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eUgAUhZpbnZhbGlkVHJh'
'bnNhY3Rpb25UeXBlQgYKBGtpbmQ='); 'bnNhY3Rpb25UeXBlEl8KEWNoYWluX2lkX21pc21hdGNoGAcgASgLMjEuYXJiaXRlci5zaGFyZW'
'QuZXZtLkV2YWxWaW9sYXRpb24uQ2hhaW5JZE1pc21hdGNoSABSD2NoYWluSWRNaXNtYXRjaBpF'
'Cg9DaGFpbklkTWlzbWF0Y2gSGgoIZXhwZWN0ZWQYASABKARSCGV4cGVjdGVkEhYKBmFjdHVhbB'
'gCIAEoBFIGYWN0dWFsQgYKBGtpbmQ=');
@$core.Deprecated('Use noMatchingGrantErrorDescriptor instead') @$core.Deprecated('Use noMatchingGrantErrorDescriptor instead')
const NoMatchingGrantError$json = { const NoMatchingGrantError$json = {

View File

@@ -24,12 +24,10 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
factory AuthChallengeRequest({ factory AuthChallengeRequest({
$core.List<$core.int>? pubkey, $core.List<$core.int>? pubkey,
$core.String? bootstrapToken, $core.String? bootstrapToken,
KeyType? keyType,
}) { }) {
final result = create(); final result = create();
if (pubkey != null) result.pubkey = pubkey; if (pubkey != null) result.pubkey = pubkey;
if (bootstrapToken != null) result.bootstrapToken = bootstrapToken; if (bootstrapToken != null) result.bootstrapToken = bootstrapToken;
if (keyType != null) result.keyType = keyType;
return result; return result;
} }
@@ -50,8 +48,6 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
..a<$core.List<$core.int>>( ..a<$core.List<$core.int>>(
1, _omitFieldNames ? '' : 'pubkey', $pb.PbFieldType.OY) 1, _omitFieldNames ? '' : 'pubkey', $pb.PbFieldType.OY)
..aOS(2, _omitFieldNames ? '' : 'bootstrapToken') ..aOS(2, _omitFieldNames ? '' : 'bootstrapToken')
..aE<KeyType>(3, _omitFieldNames ? '' : 'keyType',
enumValues: KeyType.values)
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -90,15 +86,6 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
$core.bool hasBootstrapToken() => $_has(1); $core.bool hasBootstrapToken() => $_has(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
void clearBootstrapToken() => $_clearField(2); void clearBootstrapToken() => $_clearField(2);
@$pb.TagNumber(3)
KeyType get keyType => $_getN(2);
@$pb.TagNumber(3)
set keyType(KeyType value) => $_setField(3, value);
@$pb.TagNumber(3)
$core.bool hasKeyType() => $_has(2);
@$pb.TagNumber(3)
void clearKeyType() => $_clearField(3);
} }
class AuthChallenge extends $pb.GeneratedMessage { class AuthChallenge extends $pb.GeneratedMessage {

View File

@@ -14,31 +14,6 @@ import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb; import 'package:protobuf/protobuf.dart' as $pb;
class KeyType extends $pb.ProtobufEnum {
static const KeyType KEY_TYPE_UNSPECIFIED =
KeyType._(0, _omitEnumNames ? '' : 'KEY_TYPE_UNSPECIFIED');
static const KeyType KEY_TYPE_ED25519 =
KeyType._(1, _omitEnumNames ? '' : 'KEY_TYPE_ED25519');
static const KeyType KEY_TYPE_ECDSA_SECP256K1 =
KeyType._(2, _omitEnumNames ? '' : 'KEY_TYPE_ECDSA_SECP256K1');
static const KeyType KEY_TYPE_RSA =
KeyType._(3, _omitEnumNames ? '' : 'KEY_TYPE_RSA');
static const $core.List<KeyType> values = <KeyType>[
KEY_TYPE_UNSPECIFIED,
KEY_TYPE_ED25519,
KEY_TYPE_ECDSA_SECP256K1,
KEY_TYPE_RSA,
];
static final $core.List<KeyType?> _byValue =
$pb.ProtobufEnum.$_initByValueList(values, 3);
static KeyType? valueOf($core.int value) =>
value < 0 || value >= _byValue.length ? null : _byValue[value];
const KeyType._(super.value, super.name);
}
class AuthResult extends $pb.ProtobufEnum { class AuthResult extends $pb.ProtobufEnum {
static const AuthResult AUTH_RESULT_UNSPECIFIED = static const AuthResult AUTH_RESULT_UNSPECIFIED =
AuthResult._(0, _omitEnumNames ? '' : 'AUTH_RESULT_UNSPECIFIED'); AuthResult._(0, _omitEnumNames ? '' : 'AUTH_RESULT_UNSPECIFIED');

View File

@@ -15,22 +15,6 @@ import 'dart:convert' as $convert;
import 'dart:core' as $core; import 'dart:core' as $core;
import 'dart:typed_data' as $typed_data; import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use keyTypeDescriptor instead')
const KeyType$json = {
'1': 'KeyType',
'2': [
{'1': 'KEY_TYPE_UNSPECIFIED', '2': 0},
{'1': 'KEY_TYPE_ED25519', '2': 1},
{'1': 'KEY_TYPE_ECDSA_SECP256K1', '2': 2},
{'1': 'KEY_TYPE_RSA', '2': 3},
],
};
/// Descriptor for `KeyType`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List keyTypeDescriptor = $convert.base64Decode(
'CgdLZXlUeXBlEhgKFEtFWV9UWVBFX1VOU1BFQ0lGSUVEEAASFAoQS0VZX1RZUEVfRUQyNTUxOR'
'ABEhwKGEtFWV9UWVBFX0VDRFNBX1NFQ1AyNTZLMRACEhAKDEtFWV9UWVBFX1JTQRAD');
@$core.Deprecated('Use authResultDescriptor instead') @$core.Deprecated('Use authResultDescriptor instead')
const AuthResult$json = { const AuthResult$json = {
'1': 'AuthResult', '1': 'AuthResult',
@@ -67,14 +51,6 @@ const AuthChallengeRequest$json = {
'10': 'bootstrapToken', '10': 'bootstrapToken',
'17': true '17': true
}, },
{
'1': 'key_type',
'3': 3,
'4': 1,
'5': 14,
'6': '.arbiter.user_agent.auth.KeyType',
'10': 'keyType'
},
], ],
'8': [ '8': [
{'1': '_bootstrap_token'}, {'1': '_bootstrap_token'},
@@ -84,9 +60,8 @@ const AuthChallengeRequest$json = {
/// Descriptor for `AuthChallengeRequest`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `AuthChallengeRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List authChallengeRequestDescriptor = $convert.base64Decode( final $typed_data.Uint8List authChallengeRequestDescriptor = $convert.base64Decode(
'ChRBdXRoQ2hhbGxlbmdlUmVxdWVzdBIWCgZwdWJrZXkYASABKAxSBnB1YmtleRIsCg9ib290c3' 'ChRBdXRoQ2hhbGxlbmdlUmVxdWVzdBIWCgZwdWJrZXkYASABKAxSBnB1YmtleRIsCg9ib290c3'
'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQESOwoIa2V5X3R5cGUYAyABKA4y' 'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQFCEgoQX2Jvb3RzdHJhcF90b2tl'
'IC5hcmJpdGVyLnVzZXJfYWdlbnQuYXV0aC5LZXlUeXBlUgdrZXlUeXBlQhIKEF9ib290c3RyYX' 'bg==');
'BfdG9rZW4=');
@$core.Deprecated('Use authChallengeDescriptor instead') @$core.Deprecated('Use authChallengeDescriptor instead')
const AuthChallenge$json = { const AuthChallenge$json = {