Compare commits
4 Commits
Client-key
...
366a58f5eb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
366a58f5eb | ||
|
|
5141ac4f55 | ||
|
|
f6f4f81acb | ||
|
|
805d5691d6 |
@@ -2,63 +2,24 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package arbiter.client;
|
package arbiter.client;
|
||||||
|
|
||||||
import "evm.proto";
|
import "client/auth.proto";
|
||||||
import "google/protobuf/empty.proto";
|
import "client/evm.proto";
|
||||||
|
import "client/vault.proto";
|
||||||
message ClientInfo {
|
|
||||||
string name = 1;
|
|
||||||
optional string description = 2;
|
|
||||||
optional string version = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallengeRequest {
|
|
||||||
bytes pubkey = 1;
|
|
||||||
ClientInfo client_info = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallenge {
|
|
||||||
bytes pubkey = 1;
|
|
||||||
int32 nonce = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallengeSolution {
|
|
||||||
bytes signature = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AuthResult {
|
|
||||||
AUTH_RESULT_UNSPECIFIED = 0;
|
|
||||||
AUTH_RESULT_SUCCESS = 1;
|
|
||||||
AUTH_RESULT_INVALID_KEY = 2;
|
|
||||||
AUTH_RESULT_INVALID_SIGNATURE = 3;
|
|
||||||
AUTH_RESULT_APPROVAL_DENIED = 4;
|
|
||||||
AUTH_RESULT_NO_USER_AGENTS_ONLINE = 5;
|
|
||||||
AUTH_RESULT_INTERNAL = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VaultState {
|
|
||||||
VAULT_STATE_UNSPECIFIED = 0;
|
|
||||||
VAULT_STATE_UNBOOTSTRAPPED = 1;
|
|
||||||
VAULT_STATE_SEALED = 2;
|
|
||||||
VAULT_STATE_UNSEALED = 3;
|
|
||||||
VAULT_STATE_ERROR = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ClientRequest {
|
message ClientRequest {
|
||||||
int32 request_id = 4;
|
int32 request_id = 4;
|
||||||
oneof payload {
|
oneof payload {
|
||||||
AuthChallengeRequest auth_challenge_request = 1;
|
auth.Request auth = 1;
|
||||||
AuthChallengeSolution auth_challenge_solution = 2;
|
vault.Request vault = 2;
|
||||||
google.protobuf.Empty query_vault_state = 3;
|
evm.Request evm = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message ClientResponse {
|
message ClientResponse {
|
||||||
optional int32 request_id = 7;
|
optional int32 request_id = 7;
|
||||||
oneof payload {
|
oneof payload {
|
||||||
AuthChallenge auth_challenge = 1;
|
auth.Response auth = 1;
|
||||||
AuthResult auth_result = 2;
|
vault.Response vault = 2;
|
||||||
arbiter.evm.EvmSignTransactionResponse evm_sign_transaction = 3;
|
evm.Response evm = 3;
|
||||||
arbiter.evm.EvmAnalyzeTransactionResponse evm_analyze_transaction = 4;
|
|
||||||
VaultState vault_state = 6;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
protobufs/client/auth.proto
Normal file
43
protobufs/client/auth.proto
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.client.auth;
|
||||||
|
|
||||||
|
import "shared/client.proto";
|
||||||
|
|
||||||
|
message AuthChallengeRequest {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
arbiter.shared.ClientInfo client_info = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthChallenge {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
int32 nonce = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthChallengeSolution {
|
||||||
|
bytes signature = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthResult {
|
||||||
|
AUTH_RESULT_UNSPECIFIED = 0;
|
||||||
|
AUTH_RESULT_SUCCESS = 1;
|
||||||
|
AUTH_RESULT_INVALID_KEY = 2;
|
||||||
|
AUTH_RESULT_INVALID_SIGNATURE = 3;
|
||||||
|
AUTH_RESULT_APPROVAL_DENIED = 4;
|
||||||
|
AUTH_RESULT_NO_USER_AGENTS_ONLINE = 5;
|
||||||
|
AUTH_RESULT_INTERNAL = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
AuthChallengeRequest challenge_request = 1;
|
||||||
|
AuthChallengeSolution challenge_solution = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
AuthChallenge challenge = 1;
|
||||||
|
AuthResult result = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
protobufs/client/evm.proto
Normal file
19
protobufs/client/evm.proto
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.client.evm;
|
||||||
|
|
||||||
|
import "evm.proto";
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
arbiter.evm.EvmSignTransactionRequest sign_transaction = 1;
|
||||||
|
arbiter.evm.EvmAnalyzeTransactionRequest analyze_transaction = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
arbiter.evm.EvmSignTransactionResponse sign_transaction = 1;
|
||||||
|
arbiter.evm.EvmAnalyzeTransactionResponse analyze_transaction = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
protobufs/client/vault.proto
Normal file
18
protobufs/client/vault.proto
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.client.vault;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "shared/vault.proto";
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
google.protobuf.Empty query_state = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
arbiter.shared.VaultState state = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package arbiter.evm;
|
|||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "shared/evm.proto";
|
||||||
|
|
||||||
enum EvmError {
|
enum EvmError {
|
||||||
EVM_ERROR_UNSPECIFIED = 0;
|
EVM_ERROR_UNSPECIFIED = 0;
|
||||||
@@ -74,70 +75,6 @@ message SpecificGrant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message EtherTransferMeaning {
|
|
||||||
bytes to = 1; // 20-byte Ethereum address
|
|
||||||
bytes value = 2; // U256 as big-endian bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
message TokenInfo {
|
|
||||||
string symbol = 1;
|
|
||||||
bytes address = 2; // 20-byte Ethereum address
|
|
||||||
uint64 chain_id = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mirror of token_transfers::Meaning
|
|
||||||
message TokenTransferMeaning {
|
|
||||||
TokenInfo token = 1;
|
|
||||||
bytes to = 2; // 20-byte Ethereum address
|
|
||||||
bytes value = 3; // U256 as big-endian bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mirror of policies::SpecificMeaning
|
|
||||||
message SpecificMeaning {
|
|
||||||
oneof meaning {
|
|
||||||
EtherTransferMeaning ether_transfer = 1;
|
|
||||||
TokenTransferMeaning token_transfer = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Eval error types ---
|
|
||||||
message GasLimitExceededViolation {
|
|
||||||
optional bytes max_gas_fee_per_gas = 1; // U256 as big-endian bytes
|
|
||||||
optional bytes max_priority_fee_per_gas = 2; // U256 as big-endian bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
message EvalViolation {
|
|
||||||
oneof kind {
|
|
||||||
bytes invalid_target = 1; // 20-byte Ethereum address
|
|
||||||
GasLimitExceededViolation gas_limit_exceeded = 2;
|
|
||||||
google.protobuf.Empty rate_limit_exceeded = 3;
|
|
||||||
google.protobuf.Empty volumetric_limit_exceeded = 4;
|
|
||||||
google.protobuf.Empty invalid_time = 5;
|
|
||||||
google.protobuf.Empty invalid_transaction_type = 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction was classified but no grant covers it
|
|
||||||
message NoMatchingGrantError {
|
|
||||||
SpecificMeaning meaning = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction was classified and a grant was found, but constraints were violated
|
|
||||||
message PolicyViolationsError {
|
|
||||||
SpecificMeaning meaning = 1;
|
|
||||||
repeated EvalViolation violations = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// top-level error returned when transaction evaluation fails
|
|
||||||
message TransactionEvalError {
|
|
||||||
oneof kind {
|
|
||||||
google.protobuf.Empty contract_creation_not_supported = 1;
|
|
||||||
google.protobuf.Empty unsupported_transaction_type = 2;
|
|
||||||
NoMatchingGrantError no_matching_grant = 3;
|
|
||||||
PolicyViolationsError policy_violations = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- UserAgent grant management ---
|
// --- UserAgent grant management ---
|
||||||
message EvmGrantCreateRequest {
|
message EvmGrantCreateRequest {
|
||||||
SharedSettings shared = 1;
|
SharedSettings shared = 1;
|
||||||
@@ -197,7 +134,7 @@ message EvmSignTransactionRequest {
|
|||||||
message EvmSignTransactionResponse {
|
message EvmSignTransactionResponse {
|
||||||
oneof result {
|
oneof result {
|
||||||
bytes signature = 1; // 65-byte signature: r[32] || s[32] || v[1]
|
bytes signature = 1; // 65-byte signature: r[32] || s[32] || v[1]
|
||||||
TransactionEvalError eval_error = 2;
|
arbiter.shared.evm.TransactionEvalError eval_error = 2;
|
||||||
EvmError error = 3;
|
EvmError error = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,8 +146,8 @@ message EvmAnalyzeTransactionRequest {
|
|||||||
|
|
||||||
message EvmAnalyzeTransactionResponse {
|
message EvmAnalyzeTransactionResponse {
|
||||||
oneof result {
|
oneof result {
|
||||||
SpecificMeaning meaning = 1;
|
arbiter.shared.evm.SpecificMeaning meaning = 1;
|
||||||
TransactionEvalError eval_error = 2;
|
arbiter.shared.evm.TransactionEvalError eval_error = 2;
|
||||||
EvmError error = 3;
|
EvmError error = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
protobufs/shared/client.proto
Normal file
9
protobufs/shared/client.proto
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.shared;
|
||||||
|
|
||||||
|
message ClientInfo {
|
||||||
|
string name = 1;
|
||||||
|
optional string description = 2;
|
||||||
|
optional string version = 3;
|
||||||
|
}
|
||||||
68
protobufs/shared/evm.proto
Normal file
68
protobufs/shared/evm.proto
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.shared.evm;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message EtherTransferMeaning {
|
||||||
|
bytes to = 1; // 20-byte Ethereum address
|
||||||
|
bytes value = 2; // U256 as big-endian bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
message TokenInfo {
|
||||||
|
string symbol = 1;
|
||||||
|
bytes address = 2; // 20-byte Ethereum address
|
||||||
|
uint64 chain_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror of token_transfers::Meaning
|
||||||
|
message TokenTransferMeaning {
|
||||||
|
TokenInfo token = 1;
|
||||||
|
bytes to = 2; // 20-byte Ethereum address
|
||||||
|
bytes value = 3; // U256 as big-endian bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror of policies::SpecificMeaning
|
||||||
|
message SpecificMeaning {
|
||||||
|
oneof meaning {
|
||||||
|
EtherTransferMeaning ether_transfer = 1;
|
||||||
|
TokenTransferMeaning token_transfer = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GasLimitExceededViolation {
|
||||||
|
optional bytes max_gas_fee_per_gas = 1; // U256 as big-endian bytes
|
||||||
|
optional bytes max_priority_fee_per_gas = 2; // U256 as big-endian bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
message EvalViolation {
|
||||||
|
oneof kind {
|
||||||
|
bytes invalid_target = 1; // 20-byte Ethereum address
|
||||||
|
GasLimitExceededViolation gas_limit_exceeded = 2;
|
||||||
|
google.protobuf.Empty rate_limit_exceeded = 3;
|
||||||
|
google.protobuf.Empty volumetric_limit_exceeded = 4;
|
||||||
|
google.protobuf.Empty invalid_time = 5;
|
||||||
|
google.protobuf.Empty invalid_transaction_type = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction was classified but no grant covers it
|
||||||
|
message NoMatchingGrantError {
|
||||||
|
SpecificMeaning meaning = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction was classified and a grant was found, but constraints were violated
|
||||||
|
message PolicyViolationsError {
|
||||||
|
SpecificMeaning meaning = 1;
|
||||||
|
repeated EvalViolation violations = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// top-level error returned when transaction evaluation fails
|
||||||
|
message TransactionEvalError {
|
||||||
|
oneof kind {
|
||||||
|
google.protobuf.Empty contract_creation_not_supported = 1;
|
||||||
|
google.protobuf.Empty unsupported_transaction_type = 2;
|
||||||
|
NoMatchingGrantError no_matching_grant = 3;
|
||||||
|
PolicyViolationsError policy_violations = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
protobufs/shared/vault.proto
Normal file
11
protobufs/shared/vault.proto
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.shared;
|
||||||
|
|
||||||
|
enum VaultState {
|
||||||
|
VAULT_STATE_UNSPECIFIED = 0;
|
||||||
|
VAULT_STATE_UNBOOTSTRAPPED = 1;
|
||||||
|
VAULT_STATE_SEALED = 2;
|
||||||
|
VAULT_STATE_UNSEALED = 3;
|
||||||
|
VAULT_STATE_ERROR = 4;
|
||||||
|
}
|
||||||
@@ -2,198 +2,27 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package arbiter.user_agent;
|
package arbiter.user_agent;
|
||||||
|
|
||||||
import "client.proto";
|
import "user_agent/auth.proto";
|
||||||
import "evm.proto";
|
import "user_agent/evm.proto";
|
||||||
import "google/protobuf/empty.proto";
|
import "user_agent/sdk_client.proto";
|
||||||
|
import "user_agent/vault/vault.proto";
|
||||||
enum KeyType {
|
|
||||||
KEY_TYPE_UNSPECIFIED = 0;
|
|
||||||
KEY_TYPE_ED25519 = 1;
|
|
||||||
KEY_TYPE_ECDSA_SECP256K1 = 2;
|
|
||||||
KEY_TYPE_RSA = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SDK client management ---
|
|
||||||
|
|
||||||
enum SdkClientError {
|
|
||||||
SDK_CLIENT_ERROR_UNSPECIFIED = 0;
|
|
||||||
SDK_CLIENT_ERROR_ALREADY_EXISTS = 1;
|
|
||||||
SDK_CLIENT_ERROR_NOT_FOUND = 2;
|
|
||||||
SDK_CLIENT_ERROR_HAS_RELATED_DATA = 3; // hard-delete blocked by FK (client has grants or transaction logs)
|
|
||||||
SDK_CLIENT_ERROR_INTERNAL = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientRevokeRequest {
|
|
||||||
int32 client_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientEntry {
|
|
||||||
int32 id = 1;
|
|
||||||
bytes pubkey = 2;
|
|
||||||
arbiter.client.ClientInfo info = 3;
|
|
||||||
int32 created_at = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientList {
|
|
||||||
repeated SdkClientEntry clients = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientRevokeResponse {
|
|
||||||
oneof result {
|
|
||||||
google.protobuf.Empty ok = 1;
|
|
||||||
SdkClientError error = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientListResponse {
|
|
||||||
oneof result {
|
|
||||||
SdkClientList clients = 1;
|
|
||||||
SdkClientError error = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallengeRequest {
|
|
||||||
bytes pubkey = 1;
|
|
||||||
optional string bootstrap_token = 2;
|
|
||||||
KeyType key_type = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallenge {
|
|
||||||
int32 nonce = 2;
|
|
||||||
reserved 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthChallengeSolution {
|
|
||||||
bytes signature = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AuthResult {
|
|
||||||
AUTH_RESULT_UNSPECIFIED = 0;
|
|
||||||
AUTH_RESULT_SUCCESS = 1;
|
|
||||||
AUTH_RESULT_INVALID_KEY = 2;
|
|
||||||
AUTH_RESULT_INVALID_SIGNATURE = 3;
|
|
||||||
AUTH_RESULT_BOOTSTRAP_REQUIRED = 4;
|
|
||||||
AUTH_RESULT_TOKEN_INVALID = 5;
|
|
||||||
AUTH_RESULT_INTERNAL = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsealStart {
|
|
||||||
bytes client_pubkey = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsealStartResponse {
|
|
||||||
bytes server_pubkey = 1;
|
|
||||||
}
|
|
||||||
message UnsealEncryptedKey {
|
|
||||||
bytes nonce = 1;
|
|
||||||
bytes ciphertext = 2;
|
|
||||||
bytes associated_data = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message BootstrapEncryptedKey {
|
|
||||||
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 BootstrapResult {
|
|
||||||
BOOTSTRAP_RESULT_UNSPECIFIED = 0;
|
|
||||||
BOOTSTRAP_RESULT_SUCCESS = 1;
|
|
||||||
BOOTSTRAP_RESULT_ALREADY_BOOTSTRAPPED = 2;
|
|
||||||
BOOTSTRAP_RESULT_INVALID_KEY = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VaultState {
|
|
||||||
VAULT_STATE_UNSPECIFIED = 0;
|
|
||||||
VAULT_STATE_UNBOOTSTRAPPED = 1;
|
|
||||||
VAULT_STATE_SEALED = 2;
|
|
||||||
VAULT_STATE_UNSEALED = 3;
|
|
||||||
VAULT_STATE_ERROR = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientConnectionRequest {
|
|
||||||
bytes pubkey = 1;
|
|
||||||
arbiter.client.ClientInfo info = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientConnectionResponse {
|
|
||||||
bool approved = 1;
|
|
||||||
bytes pubkey = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientConnectionCancel {
|
|
||||||
bytes pubkey = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message WalletAccess {
|
|
||||||
int32 wallet_id = 1;
|
|
||||||
int32 sdk_client_id = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientWalletAccess {
|
|
||||||
int32 id = 1;
|
|
||||||
WalletAccess access = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientGrantWalletAccess {
|
|
||||||
repeated WalletAccess accesses = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientRevokeWalletAccess {
|
|
||||||
repeated int32 accesses = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ListWalletAccessResponse {
|
|
||||||
repeated SdkClientWalletAccess accesses = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UserAgentRequest {
|
message UserAgentRequest {
|
||||||
int32 id = 16;
|
int32 id = 16;
|
||||||
oneof payload {
|
oneof payload {
|
||||||
AuthChallengeRequest auth_challenge_request = 1;
|
auth.Request auth = 1;
|
||||||
AuthChallengeSolution auth_challenge_solution = 2;
|
vault.Request vault = 2;
|
||||||
UnsealStart unseal_start = 3;
|
evm.Request evm = 3;
|
||||||
UnsealEncryptedKey unseal_encrypted_key = 4;
|
sdk_client.Request sdk_client = 4;
|
||||||
google.protobuf.Empty query_vault_state = 5;
|
|
||||||
google.protobuf.Empty evm_wallet_create = 6;
|
|
||||||
google.protobuf.Empty evm_wallet_list = 7;
|
|
||||||
arbiter.evm.EvmGrantCreateRequest evm_grant_create = 8;
|
|
||||||
arbiter.evm.EvmGrantDeleteRequest evm_grant_delete = 9;
|
|
||||||
arbiter.evm.EvmGrantListRequest evm_grant_list = 10;
|
|
||||||
SdkClientConnectionResponse sdk_client_connection_response = 11;
|
|
||||||
SdkClientRevokeRequest sdk_client_revoke = 12;
|
|
||||||
google.protobuf.Empty sdk_client_list = 13;
|
|
||||||
BootstrapEncryptedKey bootstrap_encrypted_key = 14;
|
|
||||||
SdkClientGrantWalletAccess grant_wallet_access = 15;
|
|
||||||
SdkClientRevokeWalletAccess revoke_wallet_access = 17;
|
|
||||||
google.protobuf.Empty list_wallet_access = 18;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserAgentResponse {
|
message UserAgentResponse {
|
||||||
optional int32 id = 16;
|
optional int32 id = 16;
|
||||||
oneof payload {
|
oneof payload {
|
||||||
AuthChallenge auth_challenge = 1;
|
auth.Response auth = 1;
|
||||||
AuthResult auth_result = 2;
|
vault.Response vault = 2;
|
||||||
UnsealStartResponse unseal_start_response = 3;
|
evm.Response evm = 3;
|
||||||
UnsealResult unseal_result = 4;
|
sdk_client.Response sdk_client = 4;
|
||||||
VaultState vault_state = 5;
|
|
||||||
arbiter.evm.WalletCreateResponse evm_wallet_create = 6;
|
|
||||||
arbiter.evm.WalletListResponse evm_wallet_list = 7;
|
|
||||||
arbiter.evm.EvmGrantCreateResponse evm_grant_create = 8;
|
|
||||||
arbiter.evm.EvmGrantDeleteResponse evm_grant_delete = 9;
|
|
||||||
arbiter.evm.EvmGrantListResponse evm_grant_list = 10;
|
|
||||||
SdkClientConnectionRequest sdk_client_connection_request = 11;
|
|
||||||
SdkClientConnectionCancel sdk_client_connection_cancel = 12;
|
|
||||||
SdkClientRevokeResponse sdk_client_revoke_response = 13;
|
|
||||||
SdkClientListResponse sdk_client_list_response = 14;
|
|
||||||
BootstrapResult bootstrap_result = 15;
|
|
||||||
ListWalletAccessResponse list_wallet_access_response = 17;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
48
protobufs/user_agent/auth.proto
Normal file
48
protobufs/user_agent/auth.proto
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
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 {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
optional string bootstrap_token = 2;
|
||||||
|
KeyType key_type = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthChallenge {
|
||||||
|
int32 nonce = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthChallengeSolution {
|
||||||
|
bytes signature = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthResult {
|
||||||
|
AUTH_RESULT_UNSPECIFIED = 0;
|
||||||
|
AUTH_RESULT_SUCCESS = 1;
|
||||||
|
AUTH_RESULT_INVALID_KEY = 2;
|
||||||
|
AUTH_RESULT_INVALID_SIGNATURE = 3;
|
||||||
|
AUTH_RESULT_BOOTSTRAP_REQUIRED = 4;
|
||||||
|
AUTH_RESULT_TOKEN_INVALID = 5;
|
||||||
|
AUTH_RESULT_INTERNAL = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
AuthChallengeRequest challenge_request = 1;
|
||||||
|
AuthChallengeSolution challenge_solution = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
AuthChallenge challenge = 1;
|
||||||
|
AuthResult result = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
protobufs/user_agent/evm.proto
Normal file
26
protobufs/user_agent/evm.proto
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.user_agent.evm;
|
||||||
|
|
||||||
|
import "evm.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
google.protobuf.Empty wallet_create = 1;
|
||||||
|
google.protobuf.Empty wallet_list = 2;
|
||||||
|
arbiter.evm.EvmGrantCreateRequest grant_create = 3;
|
||||||
|
arbiter.evm.EvmGrantDeleteRequest grant_delete = 4;
|
||||||
|
arbiter.evm.EvmGrantListRequest grant_list = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
arbiter.evm.WalletCreateResponse wallet_create = 1;
|
||||||
|
arbiter.evm.WalletListResponse wallet_list = 2;
|
||||||
|
arbiter.evm.EvmGrantCreateResponse grant_create = 3;
|
||||||
|
arbiter.evm.EvmGrantDeleteResponse grant_delete = 4;
|
||||||
|
arbiter.evm.EvmGrantListResponse grant_list = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
protobufs/user_agent/sdk_client.proto
Normal file
100
protobufs/user_agent/sdk_client.proto
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.user_agent.sdk_client;
|
||||||
|
|
||||||
|
import "shared/client.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
ERROR_UNSPECIFIED = 0;
|
||||||
|
ERROR_ALREADY_EXISTS = 1;
|
||||||
|
ERROR_NOT_FOUND = 2;
|
||||||
|
ERROR_HAS_RELATED_DATA = 3; // hard-delete blocked by FK (client has grants or transaction logs)
|
||||||
|
ERROR_INTERNAL = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RevokeRequest {
|
||||||
|
int32 client_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Entry {
|
||||||
|
int32 id = 1;
|
||||||
|
bytes pubkey = 2;
|
||||||
|
arbiter.shared.ClientInfo info = 3;
|
||||||
|
int32 created_at = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message List {
|
||||||
|
repeated Entry clients = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RevokeResponse {
|
||||||
|
oneof result {
|
||||||
|
google.protobuf.Empty ok = 1;
|
||||||
|
Error error = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListResponse {
|
||||||
|
oneof result {
|
||||||
|
List clients = 1;
|
||||||
|
Error error = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectionRequest {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
arbiter.shared.ClientInfo info = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectionResponse {
|
||||||
|
bool approved = 1;
|
||||||
|
bytes pubkey = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectionCancel {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WalletAccess {
|
||||||
|
int32 wallet_id = 1;
|
||||||
|
int32 sdk_client_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WalletAccessEntry {
|
||||||
|
int32 id = 1;
|
||||||
|
WalletAccess access = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GrantWalletAccess {
|
||||||
|
repeated WalletAccess accesses = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RevokeWalletAccess {
|
||||||
|
repeated int32 accesses = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListWalletAccessResponse {
|
||||||
|
repeated WalletAccessEntry accesses = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
ConnectionResponse connection_response = 1;
|
||||||
|
RevokeRequest revoke = 2;
|
||||||
|
google.protobuf.Empty list = 3;
|
||||||
|
GrantWalletAccess grant_wallet_access = 4;
|
||||||
|
RevokeWalletAccess revoke_wallet_access = 5;
|
||||||
|
google.protobuf.Empty list_wallet_access = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
ConnectionRequest connection_request = 1;
|
||||||
|
ConnectionCancel connection_cancel = 2;
|
||||||
|
RevokeResponse revoke = 3;
|
||||||
|
ListResponse list = 4;
|
||||||
|
ListWalletAccessResponse list_wallet_access = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
protobufs/user_agent/vault/bootstrap.proto
Normal file
24
protobufs/user_agent/vault/bootstrap.proto
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.user_agent.vault.bootstrap;
|
||||||
|
|
||||||
|
message BootstrapEncryptedKey {
|
||||||
|
bytes nonce = 1;
|
||||||
|
bytes ciphertext = 2;
|
||||||
|
bytes associated_data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BootstrapResult {
|
||||||
|
BOOTSTRAP_RESULT_UNSPECIFIED = 0;
|
||||||
|
BOOTSTRAP_RESULT_SUCCESS = 1;
|
||||||
|
BOOTSTRAP_RESULT_ALREADY_BOOTSTRAPPED = 2;
|
||||||
|
BOOTSTRAP_RESULT_INVALID_KEY = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
BootstrapEncryptedKey encrypted_key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
BootstrapResult result = 1;
|
||||||
|
}
|
||||||
37
protobufs/user_agent/vault/unseal.proto
Normal file
37
protobufs/user_agent/vault/unseal.proto
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.user_agent.vault.unseal;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
UnsealStart start = 1;
|
||||||
|
UnsealEncryptedKey encrypted_key = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
UnsealStartResponse start = 1;
|
||||||
|
UnsealResult result = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
protobufs/user_agent/vault/vault.proto
Normal file
24
protobufs/user_agent/vault/vault.proto
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package arbiter.user_agent.vault;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "shared/vault.proto";
|
||||||
|
import "user_agent/vault/bootstrap.proto";
|
||||||
|
import "user_agent/vault/unseal.proto";
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof payload {
|
||||||
|
google.protobuf.Empty query_state = 1;
|
||||||
|
unseal.Request unseal = 2;
|
||||||
|
bootstrap.Request bootstrap = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof payload {
|
||||||
|
arbiter.shared.VaultState state = 1;
|
||||||
|
unseal.Response unseal = 2;
|
||||||
|
bootstrap.Response bootstrap = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
ClientMetadata, format_challenge,
|
ClientMetadata, format_challenge,
|
||||||
proto::client::{
|
proto::{
|
||||||
AuthChallengeRequest, AuthChallengeSolution, AuthResult, ClientInfo as ProtoClientInfo,
|
client::{
|
||||||
ClientRequest, client_request::Payload as ClientRequestPayload,
|
ClientRequest,
|
||||||
client_response::Payload as ClientResponsePayload,
|
auth::{
|
||||||
|
self as proto_auth, AuthChallenge, AuthChallengeRequest, AuthChallengeSolution,
|
||||||
|
AuthResult, request::Payload as AuthRequestPayload,
|
||||||
|
response::Payload as AuthResponsePayload,
|
||||||
|
},
|
||||||
|
client_request::Payload as ClientRequestPayload,
|
||||||
|
client_response::Payload as ClientResponsePayload,
|
||||||
|
},
|
||||||
|
shared::ClientInfo as ProtoClientInfo,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ed25519_dalek::Signer as _;
|
use ed25519_dalek::Signer as _;
|
||||||
@@ -51,16 +59,16 @@ async fn send_auth_challenge_request(
|
|||||||
transport
|
transport
|
||||||
.send(ClientRequest {
|
.send(ClientRequest {
|
||||||
request_id: next_request_id(),
|
request_id: next_request_id(),
|
||||||
payload: Some(ClientRequestPayload::AuthChallengeRequest(
|
payload: Some(ClientRequestPayload::Auth(proto_auth::Request {
|
||||||
AuthChallengeRequest {
|
payload: Some(AuthRequestPayload::ChallengeRequest(AuthChallengeRequest {
|
||||||
pubkey: key.verifying_key().to_bytes().to_vec(),
|
pubkey: key.verifying_key().to_bytes().to_vec(),
|
||||||
client_info: Some(ProtoClientInfo {
|
client_info: Some(ProtoClientInfo {
|
||||||
name: metadata.name,
|
name: metadata.name,
|
||||||
description: metadata.description,
|
description: metadata.description,
|
||||||
version: metadata.version,
|
version: metadata.version,
|
||||||
}),
|
}),
|
||||||
},
|
})),
|
||||||
)),
|
})),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| AuthError::UnexpectedAuthResponse)
|
.map_err(|_| AuthError::UnexpectedAuthResponse)
|
||||||
@@ -68,7 +76,7 @@ async fn send_auth_challenge_request(
|
|||||||
|
|
||||||
async fn receive_auth_challenge(
|
async fn receive_auth_challenge(
|
||||||
transport: &mut ClientTransport,
|
transport: &mut ClientTransport,
|
||||||
) -> std::result::Result<arbiter_proto::proto::client::AuthChallenge, AuthError> {
|
) -> std::result::Result<AuthChallenge, AuthError> {
|
||||||
let response = transport
|
let response = transport
|
||||||
.recv()
|
.recv()
|
||||||
.await
|
.await
|
||||||
@@ -76,8 +84,11 @@ async fn receive_auth_challenge(
|
|||||||
|
|
||||||
let payload = response.payload.ok_or(AuthError::MissingAuthChallenge)?;
|
let payload = response.payload.ok_or(AuthError::MissingAuthChallenge)?;
|
||||||
match payload {
|
match payload {
|
||||||
ClientResponsePayload::AuthChallenge(challenge) => Ok(challenge),
|
ClientResponsePayload::Auth(response) => match response.payload {
|
||||||
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)),
|
Some(AuthResponsePayload::Challenge(challenge)) => Ok(challenge),
|
||||||
|
Some(AuthResponsePayload::Result(result)) => Err(map_auth_result(result)),
|
||||||
|
None => Err(AuthError::MissingAuthChallenge),
|
||||||
|
},
|
||||||
_ => Err(AuthError::UnexpectedAuthResponse),
|
_ => Err(AuthError::UnexpectedAuthResponse),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +96,7 @@ async fn receive_auth_challenge(
|
|||||||
async fn send_auth_challenge_solution(
|
async fn send_auth_challenge_solution(
|
||||||
transport: &mut ClientTransport,
|
transport: &mut ClientTransport,
|
||||||
key: &ed25519_dalek::SigningKey,
|
key: &ed25519_dalek::SigningKey,
|
||||||
challenge: arbiter_proto::proto::client::AuthChallenge,
|
challenge: AuthChallenge,
|
||||||
) -> std::result::Result<(), AuthError> {
|
) -> std::result::Result<(), AuthError> {
|
||||||
let challenge_payload = format_challenge(challenge.nonce, &challenge.pubkey);
|
let challenge_payload = format_challenge(challenge.nonce, &challenge.pubkey);
|
||||||
let signature = key.sign(&challenge_payload).to_bytes().to_vec();
|
let signature = key.sign(&challenge_payload).to_bytes().to_vec();
|
||||||
@@ -93,9 +104,11 @@ async fn send_auth_challenge_solution(
|
|||||||
transport
|
transport
|
||||||
.send(ClientRequest {
|
.send(ClientRequest {
|
||||||
request_id: next_request_id(),
|
request_id: next_request_id(),
|
||||||
payload: Some(ClientRequestPayload::AuthChallengeSolution(
|
payload: Some(ClientRequestPayload::Auth(proto_auth::Request {
|
||||||
AuthChallengeSolution { signature },
|
payload: Some(AuthRequestPayload::ChallengeSolution(
|
||||||
)),
|
AuthChallengeSolution { signature },
|
||||||
|
)),
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| AuthError::UnexpectedAuthResponse)
|
.map_err(|_| AuthError::UnexpectedAuthResponse)
|
||||||
@@ -113,12 +126,15 @@ async fn receive_auth_confirmation(
|
|||||||
.payload
|
.payload
|
||||||
.ok_or(AuthError::UnexpectedAuthResponse)?;
|
.ok_or(AuthError::UnexpectedAuthResponse)?;
|
||||||
match payload {
|
match payload {
|
||||||
ClientResponsePayload::AuthResult(result)
|
ClientResponsePayload::Auth(response) => match response.payload {
|
||||||
if AuthResult::try_from(result).ok() == Some(AuthResult::Success) =>
|
Some(AuthResponsePayload::Result(result))
|
||||||
{
|
if AuthResult::try_from(result).ok() == Some(AuthResult::Success) =>
|
||||||
Ok(())
|
{
|
||||||
}
|
Ok(())
|
||||||
ClientResponsePayload::AuthResult(result) => Err(map_auth_result(result)),
|
}
|
||||||
|
Some(AuthResponsePayload::Result(result)) => Err(map_auth_result(result)),
|
||||||
|
_ => Err(AuthError::UnexpectedAuthResponse),
|
||||||
|
},
|
||||||
_ => Err(AuthError::UnexpectedAuthResponse),
|
_ => Err(AuthError::UnexpectedAuthResponse),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,56 @@ use base64::{Engine, prelude::BASE64_STANDARD};
|
|||||||
pub mod proto {
|
pub mod proto {
|
||||||
tonic::include_proto!("arbiter");
|
tonic::include_proto!("arbiter");
|
||||||
|
|
||||||
|
pub mod shared {
|
||||||
|
tonic::include_proto!("arbiter.shared");
|
||||||
|
|
||||||
|
pub mod evm {
|
||||||
|
tonic::include_proto!("arbiter.shared.evm");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod user_agent {
|
pub mod user_agent {
|
||||||
tonic::include_proto!("arbiter.user_agent");
|
tonic::include_proto!("arbiter.user_agent");
|
||||||
|
|
||||||
|
pub mod auth {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.auth");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod evm {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.evm");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod sdk_client {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.sdk_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod vault {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.vault");
|
||||||
|
|
||||||
|
pub mod bootstrap {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.vault.bootstrap");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod unseal {
|
||||||
|
tonic::include_proto!("arbiter.user_agent.vault.unseal");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod client {
|
pub mod client {
|
||||||
tonic::include_proto!("arbiter.client");
|
tonic::include_proto!("arbiter.client");
|
||||||
|
|
||||||
|
pub mod auth {
|
||||||
|
tonic::include_proto!("arbiter.client.auth");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod evm {
|
||||||
|
tonic::include_proto!("arbiter.client.evm");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod vault {
|
||||||
|
tonic::include_proto!("arbiter.client.vault");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod evm {
|
pub mod evm {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
proto::client::{
|
proto::{
|
||||||
ClientRequest, ClientResponse, VaultState as ProtoVaultState,
|
client::{
|
||||||
client_request::Payload as ClientRequestPayload,
|
ClientRequest, ClientResponse,
|
||||||
client_response::Payload as ClientResponsePayload,
|
client_request::Payload as ClientRequestPayload,
|
||||||
|
client_response::Payload as ClientResponsePayload,
|
||||||
|
vault::{self as proto_vault, request::Payload as VaultRequestPayload, response::Payload as VaultResponsePayload},
|
||||||
|
},
|
||||||
|
shared::VaultState as ProtoVaultState,
|
||||||
},
|
},
|
||||||
transport::{Receiver, Sender, grpc::GrpcBi},
|
transport::{Receiver, Sender, grpc::GrpcBi},
|
||||||
};
|
};
|
||||||
@@ -79,7 +83,24 @@ async fn dispatch_inner(
|
|||||||
payload: ClientRequestPayload,
|
payload: ClientRequestPayload,
|
||||||
) -> Result<ClientResponsePayload, Status> {
|
) -> Result<ClientResponsePayload, Status> {
|
||||||
match payload {
|
match payload {
|
||||||
ClientRequestPayload::QueryVaultState(_) => {
|
ClientRequestPayload::Vault(req) => dispatch_vault_request(actor, req).await,
|
||||||
|
payload => {
|
||||||
|
warn!(?payload, "Unsupported post-auth client request");
|
||||||
|
Err(Status::invalid_argument("Unsupported client request"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dispatch_vault_request(
|
||||||
|
actor: &ActorRef<ClientSession>,
|
||||||
|
req: proto_vault::Request,
|
||||||
|
) -> Result<ClientResponsePayload, Status> {
|
||||||
|
let Some(payload) = req.payload else {
|
||||||
|
return Err(Status::invalid_argument("Missing client vault request payload"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match payload {
|
||||||
|
VaultRequestPayload::QueryState(_) => {
|
||||||
let state = match actor.ask(HandleQueryVaultState {}).await {
|
let state = match actor.ask(HandleQueryVaultState {}).await {
|
||||||
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
||||||
Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed,
|
Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed,
|
||||||
@@ -90,11 +111,9 @@ async fn dispatch_inner(
|
|||||||
ProtoVaultState::Error
|
ProtoVaultState::Error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(ClientResponsePayload::VaultState(state.into()))
|
Ok(ClientResponsePayload::Vault(proto_vault::Response {
|
||||||
}
|
payload: Some(VaultResponsePayload::State(state.into())),
|
||||||
payload => {
|
}))
|
||||||
warn!(?payload, "Unsupported post-auth client request");
|
|
||||||
Err(Status::invalid_argument("Unsupported client request"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
ClientMetadata, proto::client::{
|
ClientMetadata,
|
||||||
AuthChallenge as ProtoAuthChallenge, AuthChallengeRequest as ProtoAuthChallengeRequest,
|
proto::{
|
||||||
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
|
client::{
|
||||||
ClientInfo as ProtoClientInfo, ClientRequest, ClientResponse,
|
ClientRequest, ClientResponse,
|
||||||
client_request::Payload as ClientRequestPayload,
|
auth::{
|
||||||
client_response::Payload as ClientResponsePayload,
|
self as proto_auth, AuthChallenge as ProtoAuthChallenge,
|
||||||
}, transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi}
|
AuthChallengeRequest as ProtoAuthChallengeRequest,
|
||||||
|
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
|
||||||
|
request::Payload as AuthRequestPayload, response::Payload as AuthResponsePayload,
|
||||||
|
},
|
||||||
|
client_request::Payload as ClientRequestPayload,
|
||||||
|
client_response::Payload as ClientResponsePayload,
|
||||||
|
},
|
||||||
|
shared::ClientInfo as ProtoClientInfo,
|
||||||
|
},
|
||||||
|
transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi}
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
@@ -32,22 +41,20 @@ impl<'a> AuthTransportAdapter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn response_to_proto(response: auth::Outbound) -> ClientResponsePayload {
|
fn response_to_proto(response: auth::Outbound) -> AuthResponsePayload {
|
||||||
match response {
|
match response {
|
||||||
auth::Outbound::AuthChallenge { pubkey, nonce } => {
|
auth::Outbound::AuthChallenge { pubkey, nonce } => {
|
||||||
ClientResponsePayload::AuthChallenge(ProtoAuthChallenge {
|
AuthResponsePayload::Challenge(ProtoAuthChallenge {
|
||||||
pubkey: pubkey.to_bytes().to_vec(),
|
pubkey: pubkey.to_bytes().to_vec(),
|
||||||
nonce,
|
nonce,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
auth::Outbound::AuthSuccess => {
|
auth::Outbound::AuthSuccess => AuthResponsePayload::Result(ProtoAuthResult::Success.into()),
|
||||||
ClientResponsePayload::AuthResult(ProtoAuthResult::Success.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_to_proto(error: auth::Error) -> ClientResponsePayload {
|
fn error_to_proto(error: auth::Error) -> AuthResponsePayload {
|
||||||
ClientResponsePayload::AuthResult(
|
AuthResponsePayload::Result(
|
||||||
match error {
|
match error {
|
||||||
auth::Error::InvalidChallengeSolution => ProtoAuthResult::InvalidSignature,
|
auth::Error::InvalidChallengeSolution => ProtoAuthResult::InvalidSignature,
|
||||||
auth::Error::ApproveError(auth::ApproveError::Denied) => {
|
auth::Error::ApproveError(auth::ApproveError::Denied) => {
|
||||||
@@ -67,18 +74,20 @@ impl<'a> AuthTransportAdapter<'a> {
|
|||||||
|
|
||||||
async fn send_client_response(
|
async fn send_client_response(
|
||||||
&mut self,
|
&mut self,
|
||||||
payload: ClientResponsePayload,
|
payload: AuthResponsePayload,
|
||||||
) -> Result<(), TransportError> {
|
) -> Result<(), TransportError> {
|
||||||
self.bi
|
self.bi
|
||||||
.send(Ok(ClientResponse {
|
.send(Ok(ClientResponse {
|
||||||
request_id: Some(self.request_tracker.current_request_id()),
|
request_id: Some(self.request_tracker.current_request_id()),
|
||||||
payload: Some(payload),
|
payload: Some(ClientResponsePayload::Auth(proto_auth::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
})),
|
||||||
}))
|
}))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_auth_result(&mut self, result: ProtoAuthResult) -> Result<(), TransportError> {
|
async fn send_auth_result(&mut self, result: ProtoAuthResult) -> Result<(), TransportError> {
|
||||||
self.send_client_response(ClientResponsePayload::AuthResult(result.into()))
|
self.send_client_response(AuthResponsePayload::Result(result.into()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,9 +126,25 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let payload = request.payload?;
|
let payload = request.payload?;
|
||||||
|
let ClientRequestPayload::Auth(auth_request) = payload else {
|
||||||
|
let _ = self
|
||||||
|
.bi
|
||||||
|
.send(Err(Status::invalid_argument(
|
||||||
|
"Unsupported client auth request",
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let Some(payload) = auth_request.payload else {
|
||||||
|
let _ = self
|
||||||
|
.bi
|
||||||
|
.send(Err(Status::invalid_argument("Missing client auth request payload")))
|
||||||
|
.await;
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
ClientRequestPayload::AuthChallengeRequest(ProtoAuthChallengeRequest {
|
AuthRequestPayload::ChallengeRequest(ProtoAuthChallengeRequest {
|
||||||
pubkey,
|
pubkey,
|
||||||
client_info,
|
client_info,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -143,7 +168,7 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
|||||||
metadata: client_metadata_from_proto(client_info),
|
metadata: client_metadata_from_proto(client_info),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ClientRequestPayload::AuthChallengeSolution(ProtoAuthChallengeSolution {
|
AuthRequestPayload::ChallengeSolution(ProtoAuthChallengeSolution {
|
||||||
signature,
|
signature,
|
||||||
}) => {
|
}) => {
|
||||||
let Ok(signature) = ed25519_dalek::Signature::try_from(signature.as_slice()) else {
|
let Ok(signature) = ed25519_dalek::Signature::try_from(signature.as_slice()) else {
|
||||||
@@ -154,15 +179,6 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
|||||||
};
|
};
|
||||||
Some(auth::Inbound::AuthChallengeSolution { signature })
|
Some(auth::Inbound::AuthChallengeSolution { signature })
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
let _ = self
|
|
||||||
.bi
|
|
||||||
.send(Err(Status::invalid_argument(
|
|
||||||
"Unsupported client auth request",
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,8 @@ use tokio::sync::mpsc;
|
|||||||
|
|
||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
proto::{
|
proto::{
|
||||||
client::ClientInfo as ProtoClientMetadata,
|
|
||||||
evm::{
|
|
||||||
EvmError as ProtoEvmError, EvmGrantCreateRequest, EvmGrantCreateResponse,
|
|
||||||
EvmGrantDeleteRequest, EvmGrantDeleteResponse, EvmGrantList, EvmGrantListResponse,
|
|
||||||
GrantEntry, WalletCreateResponse, WalletEntry, WalletList, WalletListResponse,
|
|
||||||
evm_grant_create_response::Result as EvmGrantCreateResult,
|
|
||||||
evm_grant_delete_response::Result as EvmGrantDeleteResult,
|
|
||||||
evm_grant_list_response::Result as EvmGrantListResult,
|
|
||||||
wallet_create_response::Result as WalletCreateResult,
|
|
||||||
wallet_list_response::Result as WalletListResult,
|
|
||||||
},
|
|
||||||
user_agent::{
|
user_agent::{
|
||||||
BootstrapEncryptedKey as ProtoBootstrapEncryptedKey,
|
UserAgentRequest, UserAgentResponse,
|
||||||
BootstrapResult as ProtoBootstrapResult, ListWalletAccessResponse,
|
|
||||||
SdkClientConnectionCancel as ProtoSdkClientConnectionCancel,
|
|
||||||
SdkClientConnectionRequest as ProtoSdkClientConnectionRequest,
|
|
||||||
SdkClientEntry as ProtoSdkClientEntry, SdkClientError as ProtoSdkClientError,
|
|
||||||
SdkClientGrantWalletAccess, SdkClientList as ProtoSdkClientList,
|
|
||||||
SdkClientListResponse as ProtoSdkClientListResponse, SdkClientRevokeWalletAccess,
|
|
||||||
SdkClientWalletAccess, UnsealEncryptedKey as ProtoUnsealEncryptedKey,
|
|
||||||
UnsealResult as ProtoUnsealResult, UnsealStart, UserAgentRequest, UserAgentResponse,
|
|
||||||
VaultState as ProtoVaultState,
|
|
||||||
sdk_client_list_response::Result as ProtoSdkClientListResult,
|
|
||||||
user_agent_request::Payload as UserAgentRequestPayload,
|
user_agent_request::Payload as UserAgentRequestPayload,
|
||||||
user_agent_response::Payload as UserAgentResponsePayload,
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
},
|
},
|
||||||
@@ -32,33 +11,20 @@ use arbiter_proto::{
|
|||||||
transport::{Error as TransportError, Receiver, Sender, grpc::GrpcBi},
|
transport::{Error as TransportError, Receiver, Sender, grpc::GrpcBi},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use kameo::{
|
use kameo::actor::{ActorRef, Spawn as _};
|
||||||
actor::{ActorRef, Spawn as _},
|
|
||||||
error::SendError,
|
|
||||||
};
|
|
||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actors::{
|
actors::user_agent::{OutOfBand, UserAgentConnection, UserAgentSession},
|
||||||
keyholder::KeyHolderState,
|
grpc::request_tracker::RequestTracker,
|
||||||
user_agent::{
|
|
||||||
OutOfBand, UserAgentConnection, UserAgentSession,
|
|
||||||
session::connection::{
|
|
||||||
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate,
|
|
||||||
HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete,
|
|
||||||
HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess,
|
|
||||||
HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess,
|
|
||||||
HandleSdkClientList, HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
db::models::{CoreEvmWalletAccess, NewEvmWalletAccess},
|
|
||||||
grpc::{Convert, TryConvert, request_tracker::RequestTracker},
|
|
||||||
};
|
};
|
||||||
mod auth;
|
mod auth;
|
||||||
|
mod evm;
|
||||||
mod inbound;
|
mod inbound;
|
||||||
mod outbound;
|
mod outbound;
|
||||||
|
mod sdk_client;
|
||||||
|
mod vault;
|
||||||
|
|
||||||
pub struct OutOfBandAdapter(mpsc::Sender<OutOfBand>);
|
pub struct OutOfBandAdapter(mpsc::Sender<OutOfBand>);
|
||||||
|
|
||||||
@@ -86,23 +52,7 @@ async fn dispatch_loop(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload = match oob {
|
let payload = sdk_client::out_of_band_payload(oob);
|
||||||
OutOfBand::ClientConnectionRequest { profile } => {
|
|
||||||
UserAgentResponsePayload::SdkClientConnectionRequest(ProtoSdkClientConnectionRequest {
|
|
||||||
pubkey: profile.pubkey.to_bytes().to_vec(),
|
|
||||||
info: Some(ProtoClientMetadata {
|
|
||||||
name: profile.metadata.name,
|
|
||||||
description: profile.metadata.description,
|
|
||||||
version: profile.metadata.version,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
OutOfBand::ClientConnectionCancel { pubkey } => {
|
|
||||||
UserAgentResponsePayload::SdkClientConnectionCancel(ProtoSdkClientConnectionCancel {
|
|
||||||
pubkey: pubkey.to_bytes().to_vec(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if bi.send(Ok(UserAgentResponse { id: None, payload: Some(payload) })).await.is_err() {
|
if bi.send(Ok(UserAgentResponse { id: None, payload: Some(payload) })).await.is_err() {
|
||||||
return;
|
return;
|
||||||
@@ -144,7 +94,7 @@ async fn dispatch_loop(
|
|||||||
}
|
}
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
error!(?status, "Failed to process user agent request");
|
error!(?status, "Failed to process user agent request");
|
||||||
let _ = bi.send(Err(status)).await;
|
let _ = bi.send(Err(status)).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -158,285 +108,15 @@ async fn dispatch_inner(
|
|||||||
actor: &ActorRef<UserAgentSession>,
|
actor: &ActorRef<UserAgentSession>,
|
||||||
payload: UserAgentRequestPayload,
|
payload: UserAgentRequestPayload,
|
||||||
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
let response = match payload {
|
match payload {
|
||||||
UserAgentRequestPayload::UnsealStart(UnsealStart { client_pubkey }) => {
|
UserAgentRequestPayload::Vault(req) => vault::dispatch(actor, req).await,
|
||||||
let client_pubkey = <[u8; 32]>::try_from(client_pubkey)
|
UserAgentRequestPayload::Evm(req) => evm::dispatch(actor, req).await,
|
||||||
.map(x25519_dalek::PublicKey::from)
|
UserAgentRequestPayload::SdkClient(req) => sdk_client::dispatch(actor, req).await,
|
||||||
.map_err(|_| Status::invalid_argument("Invalid X25519 public key"))?;
|
UserAgentRequestPayload::Auth(..) => {
|
||||||
|
warn!("Unsupported post-auth user agent auth request");
|
||||||
let response = actor
|
Err(Status::invalid_argument("Unsupported user-agent request"))
|
||||||
.ask(HandleUnsealRequest { client_pubkey })
|
|
||||||
.await
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!(error = ?err, "Failed to handle unseal start request");
|
|
||||||
Status::internal("Failed to start unseal flow")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
UserAgentResponsePayload::UnsealStartResponse(
|
|
||||||
arbiter_proto::proto::user_agent::UnsealStartResponse {
|
|
||||||
server_pubkey: response.server_pubkey.as_bytes().to_vec(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
UserAgentRequestPayload::UnsealEncryptedKey(ProtoUnsealEncryptedKey {
|
|
||||||
nonce,
|
|
||||||
ciphertext,
|
|
||||||
associated_data,
|
|
||||||
}) => {
|
|
||||||
let result = match actor
|
|
||||||
.ask(HandleUnsealEncryptedKey {
|
|
||||||
nonce,
|
|
||||||
ciphertext,
|
|
||||||
associated_data,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(()) => ProtoUnsealResult::Success,
|
|
||||||
Err(SendError::HandlerError(UnsealError::InvalidKey)) => {
|
|
||||||
ProtoUnsealResult::InvalidKey
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to handle unseal request");
|
|
||||||
return Err(Status::internal("Failed to unseal vault"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::UnsealResult(result.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::BootstrapEncryptedKey(ProtoBootstrapEncryptedKey {
|
|
||||||
nonce,
|
|
||||||
ciphertext,
|
|
||||||
associated_data,
|
|
||||||
}) => {
|
|
||||||
let result = match actor
|
|
||||||
.ask(HandleBootstrapEncryptedKey {
|
|
||||||
nonce,
|
|
||||||
ciphertext,
|
|
||||||
associated_data,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(()) => ProtoBootstrapResult::Success,
|
|
||||||
Err(SendError::HandlerError(BootstrapError::InvalidKey)) => {
|
|
||||||
ProtoBootstrapResult::InvalidKey
|
|
||||||
}
|
|
||||||
Err(SendError::HandlerError(BootstrapError::AlreadyBootstrapped)) => {
|
|
||||||
ProtoBootstrapResult::AlreadyBootstrapped
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to handle bootstrap request");
|
|
||||||
return Err(Status::internal("Failed to bootstrap vault"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::BootstrapResult(result.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::QueryVaultState(_) => {
|
|
||||||
let state = match actor.ask(HandleQueryVaultState {}).await {
|
|
||||||
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
|
||||||
Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed,
|
|
||||||
Ok(KeyHolderState::Unsealed) => ProtoVaultState::Unsealed,
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to query vault state");
|
|
||||||
ProtoVaultState::Error
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::VaultState(state.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmWalletCreate(_) => {
|
|
||||||
let result = match actor.ask(HandleEvmWalletCreate {}).await {
|
|
||||||
Ok((wallet_id, address)) => WalletCreateResult::Wallet(WalletEntry {
|
|
||||||
id: wallet_id,
|
|
||||||
address: address.to_vec(),
|
|
||||||
}),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to create EVM wallet");
|
|
||||||
WalletCreateResult::Error(ProtoEvmError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::EvmWalletCreate(WalletCreateResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmWalletList(_) => {
|
|
||||||
let result = match actor.ask(HandleEvmWalletList {}).await {
|
|
||||||
Ok(wallets) => WalletListResult::Wallets(WalletList {
|
|
||||||
wallets: wallets
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, address)| WalletEntry {
|
|
||||||
address: address.to_vec(),
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to list EVM wallets");
|
|
||||||
WalletListResult::Error(ProtoEvmError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::EvmWalletList(WalletListResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantList(_) => {
|
|
||||||
let result = match actor.ask(HandleGrantList {}).await {
|
|
||||||
Ok(grants) => EvmGrantListResult::Grants(EvmGrantList {
|
|
||||||
grants: grants
|
|
||||||
.into_iter()
|
|
||||||
.map(|grant| GrantEntry {
|
|
||||||
id: grant.id,
|
|
||||||
wallet_access_id: grant.shared.wallet_access_id,
|
|
||||||
shared: Some(grant.shared.convert()),
|
|
||||||
specific: Some(grant.settings.convert()),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to list EVM grants");
|
|
||||||
EvmGrantListResult::Error(ProtoEvmError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::EvmGrantList(EvmGrantListResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantCreate(EvmGrantCreateRequest { shared, specific }) => {
|
|
||||||
let basic = shared
|
|
||||||
.ok_or_else(|| Status::invalid_argument("Missing shared grant settings"))?
|
|
||||||
.try_convert()?;
|
|
||||||
let grant = specific
|
|
||||||
.ok_or_else(|| Status::invalid_argument("Missing specific grant settings"))?
|
|
||||||
.try_convert()?;
|
|
||||||
|
|
||||||
let result = match actor.ask(HandleGrantCreate { basic, grant }).await {
|
|
||||||
Ok(grant_id) => EvmGrantCreateResult::GrantId(grant_id),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to create EVM grant");
|
|
||||||
EvmGrantCreateResult::Error(ProtoEvmError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::EvmGrantCreate(EvmGrantCreateResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::EvmGrantDelete(EvmGrantDeleteRequest { grant_id }) => {
|
|
||||||
let result = match actor.ask(HandleGrantDelete { grant_id }).await {
|
|
||||||
Ok(()) => EvmGrantDeleteResult::Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to delete EVM grant");
|
|
||||||
EvmGrantDeleteResult::Error(ProtoEvmError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::EvmGrantDelete(EvmGrantDeleteResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientConnectionResponse(resp) => {
|
|
||||||
let pubkey_bytes = <[u8; 32]>::try_from(resp.pubkey)
|
|
||||||
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key length"))?;
|
|
||||||
let pubkey = ed25519_dalek::VerifyingKey::from_bytes(&pubkey_bytes)
|
|
||||||
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key"))?;
|
|
||||||
|
|
||||||
actor
|
|
||||||
.ask(HandleNewClientApprove {
|
|
||||||
approved: resp.approved,
|
|
||||||
pubkey,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!(?err, "Failed to process client connection response");
|
|
||||||
Status::internal("Failed to process response")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientRevoke(_) => todo!(),
|
|
||||||
|
|
||||||
UserAgentRequestPayload::SdkClientList(_) => {
|
|
||||||
let result = match actor.ask(HandleSdkClientList {}).await {
|
|
||||||
Ok(clients) => ProtoSdkClientListResult::Clients(ProtoSdkClientList {
|
|
||||||
clients: clients
|
|
||||||
.into_iter()
|
|
||||||
.map(|(client, metadata)| ProtoSdkClientEntry {
|
|
||||||
id: client.id,
|
|
||||||
pubkey: client.public_key,
|
|
||||||
info: Some(ProtoClientMetadata {
|
|
||||||
name: metadata.name,
|
|
||||||
description: metadata.description,
|
|
||||||
version: metadata.version,
|
|
||||||
}),
|
|
||||||
created_at: client.created_at.0.timestamp() as i32,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to list SDK clients");
|
|
||||||
ProtoSdkClientListResult::Error(ProtoSdkClientError::Internal.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::SdkClientListResponse(ProtoSdkClientListResponse {
|
|
||||||
result: Some(result),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
|
||||||
let entries: Vec<NewEvmWalletAccess> =
|
|
||||||
accesses.into_iter().map(|a| a.convert()).collect();
|
|
||||||
|
|
||||||
match actor.ask(HandleGrantEvmWalletAccess { entries }).await {
|
|
||||||
Ok(()) => {
|
|
||||||
info!("Successfully granted wallet access");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to grant wallet access");
|
|
||||||
return Err(Status::internal("Failed to grant wallet access"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
|
||||||
match actor.ask(HandleRevokeEvmWalletAccess { entries: accesses }).await {
|
|
||||||
Ok(()) => {
|
|
||||||
info!("Successfully revoked wallet access");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to revoke wallet access");
|
|
||||||
return Err(Status::internal("Failed to revoke wallet access"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::ListWalletAccess(_) => {
|
|
||||||
let result = match actor.ask(HandleListWalletAccess {}).await {
|
|
||||||
Ok(accesses) => ListWalletAccessResponse {
|
|
||||||
accesses: accesses.into_iter().map(|a| a.convert()).collect(),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
warn!(error = ?err, "Failed to list wallet access");
|
|
||||||
return Err(Status::internal("Failed to list wallet access"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UserAgentResponsePayload::ListWalletAccessResponse(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAgentRequestPayload::AuthChallengeRequest(..)
|
|
||||||
| UserAgentRequestPayload::AuthChallengeSolution(..) => {
|
|
||||||
warn!(?payload, "Unsupported post-auth user agent request");
|
|
||||||
return Err(Status::invalid_argument("Unsupported user-agent request"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(response))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(
|
pub async fn start(
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use arbiter_proto::{
|
use arbiter_proto::{
|
||||||
proto::user_agent::{
|
proto::user_agent::{
|
||||||
AuthChallenge as ProtoAuthChallenge, AuthChallengeRequest as ProtoAuthChallengeRequest,
|
UserAgentRequest, UserAgentResponse, auth::{
|
||||||
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
|
self as proto_auth, AuthChallenge as ProtoAuthChallenge,
|
||||||
KeyType as ProtoKeyType, UserAgentRequest, UserAgentResponse,
|
AuthChallengeRequest as ProtoAuthChallengeRequest,
|
||||||
user_agent_request::Payload as UserAgentRequestPayload,
|
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
|
||||||
|
KeyType as ProtoKeyType, request::Payload as AuthRequestPayload,
|
||||||
|
response::Payload as AuthResponsePayload,
|
||||||
|
}, user_agent_request::Payload as UserAgentRequestPayload,
|
||||||
user_agent_response::Payload as UserAgentResponsePayload,
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
},
|
},
|
||||||
transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi},
|
transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi},
|
||||||
@@ -36,12 +39,14 @@ impl<'a> AuthTransportAdapter<'a> {
|
|||||||
|
|
||||||
async fn send_user_agent_response(
|
async fn send_user_agent_response(
|
||||||
&mut self,
|
&mut self,
|
||||||
payload: UserAgentResponsePayload,
|
payload: AuthResponsePayload,
|
||||||
) -> Result<(), TransportError> {
|
) -> Result<(), TransportError> {
|
||||||
self.bi
|
self.bi
|
||||||
.send(Ok(UserAgentResponse {
|
.send(Ok(UserAgentResponse {
|
||||||
id: Some(self.request_tracker.current_request_id()),
|
id: Some(self.request_tracker.current_request_id()),
|
||||||
payload: Some(payload),
|
payload: Some(UserAgentResponsePayload::Auth(proto_auth::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
})),
|
||||||
}))
|
}))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -56,19 +61,17 @@ impl Sender<Result<auth::Outbound, auth::Error>> for AuthTransportAdapter<'_> {
|
|||||||
use auth::{Error, Outbound};
|
use auth::{Error, Outbound};
|
||||||
let payload = match item {
|
let payload = match item {
|
||||||
Ok(Outbound::AuthChallenge { nonce }) => {
|
Ok(Outbound::AuthChallenge { nonce }) => {
|
||||||
UserAgentResponsePayload::AuthChallenge(ProtoAuthChallenge { nonce })
|
AuthResponsePayload::Challenge(ProtoAuthChallenge { nonce })
|
||||||
}
|
|
||||||
Ok(Outbound::AuthSuccess) => {
|
|
||||||
UserAgentResponsePayload::AuthResult(ProtoAuthResult::Success.into())
|
|
||||||
}
|
}
|
||||||
|
Ok(Outbound::AuthSuccess) => AuthResponsePayload::Result(ProtoAuthResult::Success.into()),
|
||||||
Err(Error::UnregisteredPublicKey) => {
|
Err(Error::UnregisteredPublicKey) => {
|
||||||
UserAgentResponsePayload::AuthResult(ProtoAuthResult::InvalidKey.into())
|
AuthResponsePayload::Result(ProtoAuthResult::InvalidKey.into())
|
||||||
}
|
}
|
||||||
Err(Error::InvalidChallengeSolution) => {
|
Err(Error::InvalidChallengeSolution) => {
|
||||||
UserAgentResponsePayload::AuthResult(ProtoAuthResult::InvalidSignature.into())
|
AuthResponsePayload::Result(ProtoAuthResult::InvalidSignature.into())
|
||||||
}
|
}
|
||||||
Err(Error::InvalidBootstrapToken) => {
|
Err(Error::InvalidBootstrapToken) => {
|
||||||
UserAgentResponsePayload::AuthResult(ProtoAuthResult::TokenInvalid.into())
|
AuthResponsePayload::Result(ProtoAuthResult::TokenInvalid.into())
|
||||||
}
|
}
|
||||||
Err(Error::Internal { details }) => {
|
Err(Error::Internal { details }) => {
|
||||||
return self.bi.send(Err(Status::internal(details))).await;
|
return self.bi.send(Err(Status::internal(details))).await;
|
||||||
@@ -112,8 +115,26 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let UserAgentRequestPayload::Auth(auth_request) = payload else {
|
||||||
|
let _ = self
|
||||||
|
.bi
|
||||||
|
.send(Err(Status::invalid_argument(
|
||||||
|
"Unsupported user-agent auth request",
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(payload) = auth_request.payload else {
|
||||||
|
warn!(
|
||||||
|
event = "received auth request with empty payload",
|
||||||
|
"grpc.useragent.auth_adapter"
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
UserAgentRequestPayload::AuthChallengeRequest(ProtoAuthChallengeRequest {
|
AuthRequestPayload::ChallengeRequest(ProtoAuthChallengeRequest {
|
||||||
pubkey,
|
pubkey,
|
||||||
bootstrap_token,
|
bootstrap_token,
|
||||||
key_type,
|
key_type,
|
||||||
@@ -150,18 +171,9 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
|||||||
bootstrap_token,
|
bootstrap_token,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
UserAgentRequestPayload::AuthChallengeSolution(ProtoAuthChallengeSolution {
|
AuthRequestPayload::ChallengeSolution(ProtoAuthChallengeSolution {
|
||||||
signature,
|
signature,
|
||||||
}) => Some(auth::Inbound::AuthChallengeSolution { signature }),
|
}) => Some(auth::Inbound::AuthChallengeSolution { signature }),
|
||||||
_ => {
|
|
||||||
let _ = self
|
|
||||||
.bi
|
|
||||||
.send(Err(Status::invalid_argument(
|
|
||||||
"Unsupported user-agent auth request",
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
170
server/crates/arbiter-server/src/grpc/user_agent/evm.rs
Normal file
170
server/crates/arbiter-server/src/grpc/user_agent/evm.rs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
use arbiter_proto::proto::{
|
||||||
|
evm::{
|
||||||
|
EvmError as ProtoEvmError, EvmGrantCreateRequest, EvmGrantCreateResponse,
|
||||||
|
EvmGrantDeleteRequest, EvmGrantDeleteResponse, EvmGrantList, EvmGrantListResponse,
|
||||||
|
GrantEntry, WalletCreateResponse, WalletEntry, WalletList, WalletListResponse,
|
||||||
|
evm_grant_create_response::Result as EvmGrantCreateResult,
|
||||||
|
evm_grant_delete_response::Result as EvmGrantDeleteResult,
|
||||||
|
evm_grant_list_response::Result as EvmGrantListResult,
|
||||||
|
wallet_create_response::Result as WalletCreateResult,
|
||||||
|
wallet_list_response::Result as WalletListResult,
|
||||||
|
},
|
||||||
|
user_agent::{
|
||||||
|
evm::{self as proto_evm, request::Payload as EvmRequestPayload, response::Payload as EvmResponsePayload},
|
||||||
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use kameo::actor::ActorRef;
|
||||||
|
use tonic::Status;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
actors::user_agent::{
|
||||||
|
UserAgentSession,
|
||||||
|
session::connection::{
|
||||||
|
HandleEvmWalletCreate, HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete,
|
||||||
|
HandleGrantList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grpc::{Convert, TryConvert},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn wrap_evm_response(payload: EvmResponsePayload) -> UserAgentResponsePayload {
|
||||||
|
UserAgentResponsePayload::Evm(proto_evm::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn dispatch(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: proto_evm::Request,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let Some(payload) = req.payload else {
|
||||||
|
return Err(Status::invalid_argument("Missing EVM request payload"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match payload {
|
||||||
|
EvmRequestPayload::WalletCreate(_) => handle_wallet_create(actor).await,
|
||||||
|
EvmRequestPayload::WalletList(_) => handle_wallet_list(actor).await,
|
||||||
|
EvmRequestPayload::GrantCreate(req) => handle_grant_create(actor, req).await,
|
||||||
|
EvmRequestPayload::GrantDelete(req) => handle_grant_delete(actor, req).await,
|
||||||
|
EvmRequestPayload::GrantList(_) => handle_grant_list(actor).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_wallet_create(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor.ask(HandleEvmWalletCreate {}).await {
|
||||||
|
Ok((wallet_id, address)) => WalletCreateResult::Wallet(WalletEntry {
|
||||||
|
id: wallet_id,
|
||||||
|
address: address.to_vec(),
|
||||||
|
}),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to create EVM wallet");
|
||||||
|
WalletCreateResult::Error(ProtoEvmError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_evm_response(EvmResponsePayload::WalletCreate(
|
||||||
|
WalletCreateResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_wallet_list(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor.ask(HandleEvmWalletList {}).await {
|
||||||
|
Ok(wallets) => WalletListResult::Wallets(WalletList {
|
||||||
|
wallets: wallets
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, address)| WalletEntry {
|
||||||
|
address: address.to_vec(),
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to list EVM wallets");
|
||||||
|
WalletListResult::Error(ProtoEvmError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_evm_response(EvmResponsePayload::WalletList(
|
||||||
|
WalletListResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_grant_list(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor.ask(HandleGrantList {}).await {
|
||||||
|
Ok(grants) => EvmGrantListResult::Grants(EvmGrantList {
|
||||||
|
grants: grants
|
||||||
|
.into_iter()
|
||||||
|
.map(|grant| GrantEntry {
|
||||||
|
id: grant.id,
|
||||||
|
wallet_access_id: grant.shared.wallet_access_id,
|
||||||
|
shared: Some(grant.shared.convert()),
|
||||||
|
specific: Some(grant.settings.convert()),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to list EVM grants");
|
||||||
|
EvmGrantListResult::Error(ProtoEvmError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_evm_response(EvmResponsePayload::GrantList(
|
||||||
|
EvmGrantListResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_grant_create(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: EvmGrantCreateRequest,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let basic = req
|
||||||
|
.shared
|
||||||
|
.ok_or_else(|| Status::invalid_argument("Missing shared grant settings"))?
|
||||||
|
.try_convert()?;
|
||||||
|
let grant = req
|
||||||
|
.specific
|
||||||
|
.ok_or_else(|| Status::invalid_argument("Missing specific grant settings"))?
|
||||||
|
.try_convert()?;
|
||||||
|
|
||||||
|
let result = match actor.ask(HandleGrantCreate { basic, grant }).await {
|
||||||
|
Ok(grant_id) => EvmGrantCreateResult::GrantId(grant_id),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to create EVM grant");
|
||||||
|
EvmGrantCreateResult::Error(ProtoEvmError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_evm_response(EvmResponsePayload::GrantCreate(
|
||||||
|
EvmGrantCreateResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_grant_delete(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: EvmGrantDeleteRequest,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor.ask(HandleGrantDelete { grant_id: req.grant_id }).await {
|
||||||
|
Ok(()) => EvmGrantDeleteResult::Ok(()),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to delete EVM grant");
|
||||||
|
EvmGrantDeleteResult::Error(ProtoEvmError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_evm_response(EvmResponsePayload::GrantDelete(
|
||||||
|
EvmGrantDeleteResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
@@ -5,12 +5,14 @@ use arbiter_proto::proto::evm::{
|
|||||||
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
||||||
specific_grant::Grant as ProtoSpecificGrantType,
|
specific_grant::Grant as ProtoSpecificGrantType,
|
||||||
};
|
};
|
||||||
use arbiter_proto::proto::user_agent::{SdkClientWalletAccess, WalletAccess};
|
use arbiter_proto::proto::user_agent::sdk_client::{
|
||||||
|
WalletAccess, WalletAccessEntry as SdkClientWalletAccess,
|
||||||
|
};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use prost_types::Timestamp as ProtoTimestamp;
|
use prost_types::Timestamp as ProtoTimestamp;
|
||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
|
|
||||||
use crate::db::models::{CoreEvmWalletAccess, NewEvmWallet, NewEvmWalletAccess};
|
use crate::db::models::{CoreEvmWalletAccess, NewEvmWalletAccess};
|
||||||
use crate::grpc::Convert;
|
use crate::grpc::Convert;
|
||||||
use crate::{
|
use crate::{
|
||||||
evm::policies::{
|
evm::policies::{
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ use arbiter_proto::proto::{
|
|||||||
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
||||||
specific_grant::Grant as ProtoSpecificGrantType,
|
specific_grant::Grant as ProtoSpecificGrantType,
|
||||||
},
|
},
|
||||||
user_agent::{SdkClientWalletAccess as ProtoSdkClientWalletAccess, WalletAccess},
|
user_agent::sdk_client::{
|
||||||
|
WalletAccess, WalletAccessEntry as ProtoSdkClientWalletAccess,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use prost_types::Timestamp as ProtoTimestamp;
|
use prost_types::Timestamp as ProtoTimestamp;
|
||||||
|
|||||||
190
server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs
Normal file
190
server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
use arbiter_proto::proto::{
|
||||||
|
user_agent::{
|
||||||
|
sdk_client::{
|
||||||
|
self as proto_sdk_client, ConnectionCancel as ProtoSdkClientConnectionCancel,
|
||||||
|
ConnectionRequest as ProtoSdkClientConnectionRequest,
|
||||||
|
ConnectionResponse as ProtoSdkClientConnectionResponse, Entry as ProtoSdkClientEntry,
|
||||||
|
Error as ProtoSdkClientError, GrantWalletAccess as ProtoSdkClientGrantWalletAccess,
|
||||||
|
List as ProtoSdkClientList, ListResponse as ProtoSdkClientListResponse,
|
||||||
|
ListWalletAccessResponse, RevokeWalletAccess as ProtoSdkClientRevokeWalletAccess,
|
||||||
|
list_response::Result as ProtoSdkClientListResult,
|
||||||
|
request::Payload as SdkClientRequestPayload,
|
||||||
|
response::Payload as SdkClientResponsePayload,
|
||||||
|
},
|
||||||
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
|
},
|
||||||
|
shared::ClientInfo as ProtoClientMetadata,
|
||||||
|
};
|
||||||
|
use kameo::actor::ActorRef;
|
||||||
|
use tonic::Status;
|
||||||
|
use tracing::{info, warn};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
actors::user_agent::{
|
||||||
|
OutOfBand, UserAgentSession,
|
||||||
|
session::connection::{
|
||||||
|
HandleGrantEvmWalletAccess, HandleListWalletAccess, HandleNewClientApprove,
|
||||||
|
HandleRevokeEvmWalletAccess, HandleSdkClientList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
db::models::NewEvmWalletAccess,
|
||||||
|
grpc::Convert,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn wrap_sdk_client_response(payload: SdkClientResponsePayload) -> UserAgentResponsePayload {
|
||||||
|
UserAgentResponsePayload::SdkClient(proto_sdk_client::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn out_of_band_payload(oob: OutOfBand) -> UserAgentResponsePayload {
|
||||||
|
match oob {
|
||||||
|
OutOfBand::ClientConnectionRequest { profile } => wrap_sdk_client_response(
|
||||||
|
SdkClientResponsePayload::ConnectionRequest(ProtoSdkClientConnectionRequest {
|
||||||
|
pubkey: profile.pubkey.to_bytes().to_vec(),
|
||||||
|
info: Some(ProtoClientMetadata {
|
||||||
|
name: profile.metadata.name,
|
||||||
|
description: profile.metadata.description,
|
||||||
|
version: profile.metadata.version,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
OutOfBand::ClientConnectionCancel { pubkey } => wrap_sdk_client_response(
|
||||||
|
SdkClientResponsePayload::ConnectionCancel(ProtoSdkClientConnectionCancel {
|
||||||
|
pubkey: pubkey.to_bytes().to_vec(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn dispatch(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: proto_sdk_client::Request,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let Some(payload) = req.payload else {
|
||||||
|
return Err(Status::invalid_argument("Missing SDK client request payload"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match payload {
|
||||||
|
SdkClientRequestPayload::ConnectionResponse(resp) => {
|
||||||
|
handle_connection_response(actor, resp).await
|
||||||
|
}
|
||||||
|
SdkClientRequestPayload::Revoke(_) => {
|
||||||
|
Err(Status::unimplemented("SdkClientRevoke is not yet implemented"))
|
||||||
|
}
|
||||||
|
SdkClientRequestPayload::List(_) => handle_list(actor).await,
|
||||||
|
SdkClientRequestPayload::GrantWalletAccess(req) => handle_grant_wallet_access(actor, req).await,
|
||||||
|
SdkClientRequestPayload::RevokeWalletAccess(req) => {
|
||||||
|
handle_revoke_wallet_access(actor, req).await
|
||||||
|
}
|
||||||
|
SdkClientRequestPayload::ListWalletAccess(_) => handle_list_wallet_access(actor).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_connection_response(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
resp: ProtoSdkClientConnectionResponse,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let pubkey_bytes = <[u8; 32]>::try_from(resp.pubkey)
|
||||||
|
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key length"))?;
|
||||||
|
let pubkey = ed25519_dalek::VerifyingKey::from_bytes(&pubkey_bytes)
|
||||||
|
.map_err(|_| Status::invalid_argument("Invalid Ed25519 public key"))?;
|
||||||
|
|
||||||
|
actor
|
||||||
|
.ask(HandleNewClientApprove {
|
||||||
|
approved: resp.approved,
|
||||||
|
pubkey,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!(?err, "Failed to process client connection response");
|
||||||
|
Status::internal("Failed to process response")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_list(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor.ask(HandleSdkClientList {}).await {
|
||||||
|
Ok(clients) => ProtoSdkClientListResult::Clients(ProtoSdkClientList {
|
||||||
|
clients: clients
|
||||||
|
.into_iter()
|
||||||
|
.map(|(client, metadata)| ProtoSdkClientEntry {
|
||||||
|
id: client.id,
|
||||||
|
pubkey: client.public_key,
|
||||||
|
info: Some(ProtoClientMetadata {
|
||||||
|
name: metadata.name,
|
||||||
|
description: metadata.description,
|
||||||
|
version: metadata.version,
|
||||||
|
}),
|
||||||
|
created_at: client.created_at.0.timestamp() as i32,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to list SDK clients");
|
||||||
|
ProtoSdkClientListResult::Error(ProtoSdkClientError::Internal.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_sdk_client_response(SdkClientResponsePayload::List(
|
||||||
|
ProtoSdkClientListResponse {
|
||||||
|
result: Some(result),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_grant_wallet_access(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: ProtoSdkClientGrantWalletAccess,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let entries: Vec<NewEvmWalletAccess> = req.accesses.into_iter().map(|a| a.convert()).collect();
|
||||||
|
match actor.ask(HandleGrantEvmWalletAccess { entries }).await {
|
||||||
|
Ok(()) => {
|
||||||
|
info!("Successfully granted wallet access");
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to grant wallet access");
|
||||||
|
Err(Status::internal("Failed to grant wallet access"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_revoke_wallet_access(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: ProtoSdkClientRevokeWalletAccess,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
match actor
|
||||||
|
.ask(HandleRevokeEvmWalletAccess {
|
||||||
|
entries: req.accesses,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
info!("Successfully revoked wallet access");
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to revoke wallet access");
|
||||||
|
Err(Status::internal("Failed to revoke wallet access"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_list_wallet_access(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
match actor.ask(HandleListWalletAccess {}).await {
|
||||||
|
Ok(accesses) => Ok(Some(wrap_sdk_client_response(
|
||||||
|
SdkClientResponsePayload::ListWalletAccess(ListWalletAccessResponse {
|
||||||
|
accesses: accesses.into_iter().map(|a| a.convert()).collect(),
|
||||||
|
}),
|
||||||
|
))),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to list wallet access");
|
||||||
|
Err(Status::internal("Failed to list wallet access"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
server/crates/arbiter-server/src/grpc/user_agent/vault.rs
Normal file
181
server/crates/arbiter-server/src/grpc/user_agent/vault.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
use arbiter_proto::proto::user_agent::{
|
||||||
|
user_agent_response::Payload as UserAgentResponsePayload,
|
||||||
|
vault::{
|
||||||
|
self as proto_vault,
|
||||||
|
bootstrap::{
|
||||||
|
self as proto_bootstrap, BootstrapEncryptedKey as ProtoBootstrapEncryptedKey,
|
||||||
|
BootstrapResult as ProtoBootstrapResult,
|
||||||
|
},
|
||||||
|
request::Payload as VaultRequestPayload,
|
||||||
|
response::Payload as VaultResponsePayload,
|
||||||
|
unseal::{
|
||||||
|
self as proto_unseal, UnsealEncryptedKey as ProtoUnsealEncryptedKey,
|
||||||
|
UnsealResult as ProtoUnsealResult, UnsealStart,
|
||||||
|
request::Payload as UnsealRequestPayload,
|
||||||
|
response::Payload as UnsealResponsePayload,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use arbiter_proto::proto::shared::VaultState as ProtoVaultState;
|
||||||
|
use kameo::{actor::ActorRef, error::SendError};
|
||||||
|
use tonic::Status;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
actors::{
|
||||||
|
keyholder::KeyHolderState,
|
||||||
|
user_agent::{
|
||||||
|
UserAgentSession,
|
||||||
|
session::connection::{
|
||||||
|
BootstrapError, HandleBootstrapEncryptedKey, HandleQueryVaultState,
|
||||||
|
HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn wrap_vault_response(payload: VaultResponsePayload) -> UserAgentResponsePayload {
|
||||||
|
UserAgentResponsePayload::Vault(proto_vault::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_unseal_response(payload: UnsealResponsePayload) -> UserAgentResponsePayload {
|
||||||
|
wrap_vault_response(VaultResponsePayload::Unseal(proto_unseal::Response {
|
||||||
|
payload: Some(payload),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_bootstrap_response(result: ProtoBootstrapResult) -> UserAgentResponsePayload {
|
||||||
|
wrap_vault_response(VaultResponsePayload::Bootstrap(proto_bootstrap::Response {
|
||||||
|
result: result.into(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn dispatch(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: proto_vault::Request,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let Some(payload) = req.payload else {
|
||||||
|
return Err(Status::invalid_argument("Missing vault request payload"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match payload {
|
||||||
|
VaultRequestPayload::QueryState(_) => handle_query_vault_state(actor).await,
|
||||||
|
VaultRequestPayload::Unseal(req) => dispatch_unseal_request(actor, req).await,
|
||||||
|
VaultRequestPayload::Bootstrap(req) => handle_bootstrap_request(actor, req).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dispatch_unseal_request(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: proto_unseal::Request,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let Some(payload) = req.payload else {
|
||||||
|
return Err(Status::invalid_argument("Missing unseal request payload"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match payload {
|
||||||
|
UnsealRequestPayload::Start(req) => handle_unseal_start(actor, req).await,
|
||||||
|
UnsealRequestPayload::EncryptedKey(req) => handle_unseal_encrypted_key(actor, req).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_unseal_start(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: UnsealStart,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let client_pubkey = <[u8; 32]>::try_from(req.client_pubkey)
|
||||||
|
.map(x25519_dalek::PublicKey::from)
|
||||||
|
.map_err(|_| Status::invalid_argument("Invalid X25519 public key"))?;
|
||||||
|
|
||||||
|
let response = actor
|
||||||
|
.ask(HandleUnsealRequest { client_pubkey })
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!(error = ?err, "Failed to handle unseal start request");
|
||||||
|
Status::internal("Failed to start unseal flow")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Some(wrap_unseal_response(UnsealResponsePayload::Start(
|
||||||
|
proto_unseal::UnsealStartResponse {
|
||||||
|
server_pubkey: response.server_pubkey.as_bytes().to_vec(),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_unseal_encrypted_key(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: ProtoUnsealEncryptedKey,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor
|
||||||
|
.ask(HandleUnsealEncryptedKey {
|
||||||
|
nonce: req.nonce,
|
||||||
|
ciphertext: req.ciphertext,
|
||||||
|
associated_data: req.associated_data,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => ProtoUnsealResult::Success,
|
||||||
|
Err(SendError::HandlerError(UnsealError::InvalidKey)) => ProtoUnsealResult::InvalidKey,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to handle unseal request");
|
||||||
|
return Err(Status::internal("Failed to unseal vault"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_unseal_response(UnsealResponsePayload::Result(
|
||||||
|
result.into(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_bootstrap_request(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: proto_bootstrap::Request,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let encrypted_key = req
|
||||||
|
.encrypted_key
|
||||||
|
.ok_or_else(|| Status::invalid_argument("Missing bootstrap encrypted key"))?;
|
||||||
|
handle_bootstrap_encrypted_key(actor, encrypted_key).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_bootstrap_encrypted_key(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
req: ProtoBootstrapEncryptedKey,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let result = match actor
|
||||||
|
.ask(HandleBootstrapEncryptedKey {
|
||||||
|
nonce: req.nonce,
|
||||||
|
ciphertext: req.ciphertext,
|
||||||
|
associated_data: req.associated_data,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => ProtoBootstrapResult::Success,
|
||||||
|
Err(SendError::HandlerError(BootstrapError::InvalidKey)) => ProtoBootstrapResult::InvalidKey,
|
||||||
|
Err(SendError::HandlerError(BootstrapError::AlreadyBootstrapped)) => {
|
||||||
|
ProtoBootstrapResult::AlreadyBootstrapped
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to handle bootstrap request");
|
||||||
|
return Err(Status::internal("Failed to bootstrap vault"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_bootstrap_response(result)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_query_vault_state(
|
||||||
|
actor: &ActorRef<UserAgentSession>,
|
||||||
|
) -> Result<Option<UserAgentResponsePayload>, Status> {
|
||||||
|
let state = match actor.ask(HandleQueryVaultState {}).await {
|
||||||
|
Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped,
|
||||||
|
Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed,
|
||||||
|
Ok(KeyHolderState::Unsealed) => ProtoVaultState::Unsealed,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = ?err, "Failed to query vault state");
|
||||||
|
ProtoVaultState::Error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(wrap_vault_response(VaultResponsePayload::State(
|
||||||
|
state.into(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- biometric_signature (10.2.0):
|
- biometric_signature (11.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- cryptography_flutter (0.0.1):
|
- cryptography_flutter (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -35,7 +35,7 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
biometric_signature: 7ef6a703fcc2c3b0e3d937a8560507b1ba9d3414
|
biometric_signature: bae0597fffbc51252959e78b56a2f5afb8d4e1f5
|
||||||
cryptography_flutter: be2b3e0e31603521b6a1c2bea232a88a2488a91c
|
cryptography_flutter: be2b3e0e31603521b6a1c2bea232a88a2488a91c
|
||||||
flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23
|
flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
|
|||||||
Reference in New Issue
Block a user