WIP: feat-shamir (old) #103
1
server/Cargo.lock
generated
1
server/Cargo.lock
generated
@@ -707,6 +707,7 @@ dependencies = [
|
||||
"memsafe",
|
||||
"ml-dsa",
|
||||
"rand 0.10.1",
|
||||
"strum 0.28.0",
|
||||
"thiserror",
|
||||
"x-wing",
|
||||
]
|
||||
|
||||
@@ -27,6 +27,7 @@ rustls = { version = "0.23.40", features = ["aws-lc-rs", "logging", "prefer-post
|
||||
rustls-pki-types = "1.14.1"
|
||||
sha2 = "0.11"
|
||||
smlang = "0.8.0"
|
||||
strum = { version = "0.28.0", features = ["derive"] }
|
||||
thiserror = "2.0.18"
|
||||
tokio = { version = "1.52.1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1.18", features = ["full"] }
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
storage::StorageError,
|
||||
transport::{ClientTransport, next_request_id},
|
||||
};
|
||||
use arbiter_crypto::authn::{self, CLIENT_CONTEXT, SigningKey};
|
||||
use arbiter_crypto::authn::{self, SigningContext, SigningKey};
|
||||
use arbiter_proto::{
|
||||
ClientMetadata,
|
||||
proto::{
|
||||
@@ -110,7 +110,7 @@ async fn send_auth_challenge_solution(
|
||||
};
|
||||
let challenge_payload: Vec<u8> = challenge.format();
|
||||
let signature = key
|
||||
.sign_message(&challenge_payload, CLIENT_CONTEXT)
|
||||
.sign_message(&challenge_payload, SigningContext::Client)
|
||||
.map_err(|_| AuthError::UnexpectedAuthResponse)?
|
||||
.to_bytes();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
ml-dsa = {workspace = true, optional = true }
|
||||
rand = {workspace = true, optional = true}
|
||||
memsafe = {version = "0.4.0", optional = true}
|
||||
strum = { workspace = true, optional = true }
|
||||
hmac.workspace = true
|
||||
alloy.workspace = true
|
||||
x-wing = { version = "0.1.0-rc.0", features = ["zeroize"] }
|
||||
@@ -18,7 +19,7 @@ workspace = true
|
||||
|
||||
[features]
|
||||
default = ["authn", "safecell"]
|
||||
authn = ["dep:ml-dsa", "dep:rand"]
|
||||
authn = ["dep:ml-dsa", "dep:rand", "dep:strum"]
|
||||
safecell = ["dep:memsafe"]
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -5,10 +5,25 @@ use ml_dsa::{
|
||||
SigningKey as MlDsaSigningKey, VerifyingKey as MlDsaVerifyingKey, signature::Keypair as _,
|
||||
};
|
||||
use rand::RngExt;
|
||||
use strum::IntoStaticStr;
|
||||
|
||||
pub static CLIENT_CONTEXT: &[u8] = b"arbiter_client";
|
||||
pub static OPERATOR_CONTEXT: &[u8] = b"arbiter_operator";
|
||||
pub static GOVERNANCE_CONTEXT: &[u8] = b"arbiter_governance_vote";
|
||||
/// Domain separation tag mixed into every ML-DSA signature.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr)]
|
||||
|
CleverWild marked this conversation as resolved
Outdated
|
||||
pub enum SigningContext {
|
||||
#[strum(serialize = "arbiter_client")]
|
||||
Client,
|
||||
#[strum(serialize = "arbiter_operator")]
|
||||
Operator,
|
||||
#[strum(serialize = "arbiter_governance_vote")]
|
||||
GovernanceVote,
|
||||
}
|
||||
|
||||
impl SigningContext {
|
||||
#[must_use]
|
||||
pub fn as_bytes(self) -> &'static [u8] {
|
||||
<&'static str>::from(self).as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
const NONCE_SIZE: usize = 32;
|
||||
|
||||
@@ -86,15 +101,26 @@ impl PublicKey {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn verify(&self, challenge: &AuthChallenge, context: &[u8], signature: &Signature) -> bool {
|
||||
pub fn verify(
|
||||
&self,
|
||||
challenge: &AuthChallenge,
|
||||
context: SigningContext,
|
||||
signature: &Signature,
|
||||
) -> bool {
|
||||
let challenge = challenge.format();
|
||||
self.0
|
||||
.verify_with_context(&challenge, context, &signature.0)
|
||||
.verify_with_context(&challenge, context.as_bytes(), &signature.0)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn verify_message(&self, message: &[u8], context: &[u8], signature: &Signature) -> bool {
|
||||
self.0.verify_with_context(message, context, &signature.0)
|
||||
pub fn verify_message(
|
||||
&self,
|
||||
message: &[u8],
|
||||
context: SigningContext,
|
||||
signature: &Signature,
|
||||
) -> bool {
|
||||
self.0
|
||||
.verify_with_context(message, context.as_bytes(), &signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,17 +147,21 @@ impl SigningKey {
|
||||
self.0.verifying_key().into()
|
||||
}
|
||||
|
||||
pub fn sign_message(&self, message: &[u8], context: &[u8]) -> Result<Signature, Error> {
|
||||
pub fn sign_message(
|
||||
&self,
|
||||
message: &[u8],
|
||||
context: SigningContext,
|
||||
) -> Result<Signature, Error> {
|
||||
self.0
|
||||
.signing_key()
|
||||
.sign_deterministic(message, context)
|
||||
.sign_deterministic(message, context.as_bytes())
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
pub fn sign_challenge(
|
||||
&self,
|
||||
challenge: &AuthChallenge,
|
||||
context: &[u8],
|
||||
context: SigningContext,
|
||||
) -> Result<Signature, Error> {
|
||||
let challenge = challenge.format();
|
||||
|
||||
@@ -198,7 +228,7 @@ mod tests {
|
||||
|
||||
use crate::authn::AuthChallenge;
|
||||
|
||||
use super::{CLIENT_CONTEXT, PublicKey, Signature, SigningKey, OPERATOR_CONTEXT};
|
||||
use super::{PublicKey, Signature, SigningContext, SigningKey};
|
||||
|
||||
#[test]
|
||||
fn public_key_round_trip_decodes() {
|
||||
@@ -214,7 +244,7 @@ mod tests {
|
||||
fn signature_round_trip_decodes() {
|
||||
let key = SigningKey::generate();
|
||||
let signature = key
|
||||
.sign_message(b"challenge", CLIENT_CONTEXT)
|
||||
.sign_message(b"challenge", SigningContext::Client)
|
||||
.expect("signature should be created");
|
||||
|
||||
let decoded =
|
||||
@@ -229,11 +259,11 @@ mod tests {
|
||||
let public_key = key.public_key();
|
||||
let challenge = AuthChallenge::generate(&mut rand::rng());
|
||||
let signature = key
|
||||
.sign_challenge(&challenge, CLIENT_CONTEXT)
|
||||
.sign_challenge(&challenge, SigningContext::Client)
|
||||
.expect("signature should be created");
|
||||
|
||||
assert!(public_key.verify(&challenge, CLIENT_CONTEXT, &signature));
|
||||
assert!(!public_key.verify(&challenge, OPERATOR_CONTEXT, &signature));
|
||||
assert!(public_key.verify(&challenge, SigningContext::Client, &signature));
|
||||
assert!(!public_key.verify(&challenge, SigningContext::Operator, &signature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -246,13 +276,13 @@ mod tests {
|
||||
let challenge = AuthChallenge::generate(&mut rand::rng());
|
||||
|
||||
let signature = restored
|
||||
.sign_challenge(&challenge, CLIENT_CONTEXT)
|
||||
.sign_challenge(&challenge, SigningContext::Client)
|
||||
.expect("signature should be created");
|
||||
|
||||
assert!(
|
||||
restored
|
||||
.public_key()
|
||||
.verify(&challenge, CLIENT_CONTEXT, &signature)
|
||||
.verify(&challenge, SigningContext::Client, &signature)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ kameo.workspace = true
|
||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
||||
argon2 = { version = "0.5.3", features = ["zeroize"] }
|
||||
restructed = "0.2.2"
|
||||
strum = { version = "0.28.0", features = ["derive"] }
|
||||
strum.workspace = true
|
||||
pem = "3.0.6"
|
||||
sha2.workspace = true
|
||||
hmac.workspace = true
|
||||
|
||||
@@ -336,7 +336,7 @@ impl ProposalManager {
|
||||
approve: bool,
|
||||
signature: Vec<u8>,
|
||||
) -> Result<VoteOutcome, Error> {
|
||||
use arbiter_crypto::authn::{self, GOVERNANCE_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, SigningContext};
|
||||
|
||||
let mut conn = self.db.get().await?;
|
||||
|
||||
@@ -391,7 +391,7 @@ impl ProposalManager {
|
||||
let auth_sig = authn::Signature::try_from(signature.as_slice())
|
||||
.map_err(|()| Error::InvalidSignature)?;
|
||||
|
||||
if !pubkey.verify_message(&vote_msg, GOVERNANCE_CONTEXT, &auth_sig) {
|
||||
if !pubkey.verify_message(&vote_msg, SigningContext::GovernanceVote, &auth_sig) {
|
||||
return Err(Error::InvalidSignature);
|
||||
|
CleverWild marked this conversation as resolved
Outdated
Skipper
commented
there is there is `exists()` function in diesel, which returns `true` / `false` if record exist
|
||||
}
|
||||
|
||||
@@ -537,7 +537,7 @@ impl ProposalManager {
|
||||
approve: bool,
|
||||
signature: Vec<u8>,
|
||||
) -> Result<VoteOutcome, Error> {
|
||||
use arbiter_crypto::authn::{self, GOVERNANCE_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, SigningContext};
|
||||
|
||||
let mut conn = self.db.get().await?;
|
||||
|
||||
@@ -596,7 +596,7 @@ impl ProposalManager {
|
||||
let auth_sig = authn::Signature::try_from(signature.as_slice())
|
||||
.map_err(|()| Error::InvalidSignature)?;
|
||||
|
||||
if !pubkey.verify_message(&vote_msg, GOVERNANCE_CONTEXT, &auth_sig) {
|
||||
if !pubkey.verify_message(&vote_msg, SigningContext::GovernanceVote, &auth_sig) {
|
||||
return Err(Error::InvalidSignature);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
schema::program_client,
|
||||
},
|
||||
};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, CLIENT_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, SigningContext};
|
||||
use arbiter_proto::{
|
||||
ClientMetadata,
|
||||
transport::{Bi, expect_message},
|
||||
@@ -306,7 +306,7 @@ where
|
||||
Error::Transport
|
||||
})?;
|
||||
|
||||
if !pubkey.verify(&challenge, CLIENT_CONTEXT, &signature) {
|
||||
if !pubkey.verify(&challenge, SigningContext::Client, &signature) {
|
||||
error!("Challenge solution verification failed");
|
||||
return Err(Error::InvalidChallengeSolution);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
db::{DatabasePool, schema::operator_identity},
|
||||
peers::operator::auth::Outbound,
|
||||
};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, OPERATOR_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, SigningContext};
|
||||
use arbiter_proto::transport::Bi;
|
||||
|
||||
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl};
|
||||
@@ -141,7 +141,7 @@ where
|
||||
Error::InvalidChallengeSolution
|
||||
})?;
|
||||
|
||||
let valid = pubkey.verify(challenge, OPERATOR_CONTEXT, &signature);
|
||||
let valid = pubkey.verify(challenge, SigningContext::Operator, &signature);
|
||||
|
||||
if !valid {
|
||||
self.transport
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::common::ChannelTransport;
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, CLIENT_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, SigningContext};
|
||||
use arbiter_proto::{
|
||||
ClientMetadata,
|
||||
transport::{Receiver, Sender},
|
||||
@@ -71,7 +71,7 @@ async fn insert_registered_client(
|
||||
fn sign_client_challenge(key: &SigningKey<MlDsa87>, challenge: &AuthChallenge) -> authn::Signature {
|
||||
let challenge = challenge.format();
|
||||
key.signing_key()
|
||||
.sign_deterministic(&challenge, CLIENT_CONTEXT)
|
||||
.sign_deterministic(&challenge, SigningContext::Client.as_bytes())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use arbiter_crypto::authn::{self, GOVERNANCE_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, SigningContext};
|
||||
use arbiter_server::{
|
||||
actors::{
|
||||
GlobalActors,
|
||||
@@ -159,7 +159,9 @@ async fn single_operator_vote_reaches_quorum() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
@@ -203,7 +205,9 @@ async fn two_operator_first_vote_is_pending() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = key1.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = key1
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
@@ -246,7 +250,9 @@ async fn duplicate_vote_rejected() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -259,7 +265,9 @@ async fn duplicate_vote_rejected() {
|
||||
.unwrap();
|
||||
|
||||
// Second vote same operator
|
||||
let sig2 = key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig2 = key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let result = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -357,7 +365,9 @@ async fn query_pending_excludes_already_voted() {
|
||||
|
||||
// Vote on p1 — with 1 operator this reaches quorum (QuorumApproved)
|
||||
let msg = make_vote_message(p1, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -418,7 +428,9 @@ async fn expired_proposal_is_hidden_and_unvotable() {
|
||||
|
||||
// And the write path must refuse it rather than rely on a status flip.
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let result = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -464,7 +476,9 @@ async fn approve_sdk_client_writes_integrity_envelope() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = op_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = op_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -516,7 +530,9 @@ async fn grant_wallet_access_on_quorum_approval() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -605,7 +621,9 @@ async fn approve_persistent_grant_creates_basic_grant_row() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -720,7 +738,9 @@ async fn approve_one_off_transaction_stores_result() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -771,7 +791,9 @@ async fn replace_operator_updates_pubkey_and_starts_rekey() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -828,7 +850,9 @@ async fn update_shamir_parameters_reaches_quorum() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -874,7 +898,9 @@ async fn key_rotation_requires_full_quorum() {
|
||||
|
||||
let cast = |op_id, key: &authn::SigningKey| {
|
||||
let actors = actors.clone();
|
||||
let sig = key.sign_message(&make_vote_message(proposal_id, true), GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = key
|
||||
.sign_message(&make_vote_message(proposal_id, true), SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
async move {
|
||||
actors
|
||||
.proposal_manager
|
||||
@@ -915,7 +941,9 @@ async fn approve_server_update_reaches_quorum() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = signing_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -955,7 +983,9 @@ async fn recovery_vote_rejected_when_sleeping() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = rec_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = rec_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let err = actors
|
||||
.proposal_manager
|
||||
.ask(CastRecoveryVote {
|
||||
@@ -999,7 +1029,9 @@ async fn recovery_vote_blocked_on_non_replace_proposal() {
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = rec_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = rec_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let err = actors
|
||||
.proposal_manager
|
||||
.ask(CastRecoveryVote {
|
||||
@@ -1103,7 +1135,9 @@ async fn recovery_operator_vote_contributes_to_replace_quorum() {
|
||||
|
||||
// Ordinary operator approves — still pending (needs recovery too)
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = op_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = op_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
@@ -1117,7 +1151,9 @@ async fn recovery_operator_vote_contributes_to_replace_quorum() {
|
||||
assert_eq!(outcome, VoteOutcome::Pending);
|
||||
|
||||
// Recovery operator approves — now quorum is reached
|
||||
let sig = rec_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let sig = rec_key
|
||||
.sign_message(&msg, SigningContext::GovernanceVote)
|
||||
.unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastRecoveryVote {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::common::ChannelTransport;
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, OPERATOR_CONTEXT};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, SigningContext};
|
||||
use arbiter_proto::transport::{Error as TransportError, Receiver, Sender};
|
||||
use arbiter_server::{
|
||||
actors::{GlobalActors, bootstrap::GetToken, vault::Bootstrap},
|
||||
@@ -24,7 +24,7 @@ fn sign_operator_challenge(
|
||||
) -> authn::Signature {
|
||||
let challenge = challenge.format();
|
||||
key.signing_key()
|
||||
.sign_deterministic(&challenge, OPERATOR_CONTEXT)
|
||||
.sign_deterministic(&challenge, SigningContext::Operator.as_bytes())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user
declare enum here with 3 possible context, associate a binary data with them:
https://docs.rs/strum/latest/strum/derive.IntoStaticStr.html