refactor(crypto): replace byte-slice signing contexts with SigningContext enum

This commit is contained in:
CleverWild
2026-08-26 13:06:59 +02:00
committed by Skipper
parent f881102f0a
commit a501283b0c
12 changed files with 121 additions and 52 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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