use thiserror::Error;
#[derive(Debug, Error)]
pub enum SignerError {
#[error(
"DEV signer requires SBO3L_DEV_ONLY_SIGNER=1 to be set; \
this is a production-mode lockout, not a configuration nag"
)]
DevOnlyLockout,
#[error(
"unknown SBO3L_SIGNER_BACKEND='{0}'; expected one of dev / local_file / aws_kms / gcp_kms / phala_tee"
)]
UnknownBackend(String),
#[error("backend '{0}' was not compiled in; rebuild with --features {0}")]
BackendNotCompiled(&'static str),
#[error("environment variable {0} is required for the selected backend")]
MissingEnv(&'static str),
#[error("KMS request failed: {0}")]
Kms(String),
#[error("KMS key '{key_id}' is not Ed25519 (got '{found_spec}')")]
KeySpecMismatch { key_id: String, found_spec: String },
#[error("hex decoding: {0}")]
Hex(#[from] hex::FromHexError),
}
pub trait Signer: Send + Sync {
fn sign_hex(&self, message: &[u8]) -> Result<String, SignerError>;
fn verifying_key_hex(&self) -> Result<String, SignerError>;
fn key_id(&self) -> &str;
}
pub mod dev;
pub mod local_file;
#[cfg(feature = "aws_kms")]
pub mod aws_kms;
#[cfg(feature = "gcp_kms")]
pub mod gcp_kms;
#[cfg(feature = "phala_tee")]
pub mod phala_tee;
#[cfg(feature = "eth_signer")]
pub mod eth;
#[cfg(feature = "eth_signer")]
pub mod eth_kms;
#[cfg(feature = "eth_signer")]
pub mod eth_local;
#[cfg(feature = "eth_kms_aws")]
pub mod eth_kms_aws_live;
#[cfg(any(feature = "eth_kms_aws", feature = "eth_kms_gcp"))]
pub mod eth_kms_common;
#[cfg(feature = "eth_kms_gcp")]
pub mod eth_kms_gcp_live;
pub use dev::DevSignerLockedDown;
pub use local_file::{KeyFileFormat, LocalFileSigner};
#[cfg(feature = "eth_signer")]
pub use eth::EthSigner;
#[cfg(feature = "eth_signer")]
pub use eth_local::{eip55_checksum, EthLocalFileSigner};
#[cfg(feature = "eth_signer")]
pub fn eth_signer_from_env(role: &str) -> Result<Box<dyn EthSigner>, SignerError> {
let backend =
std::env::var("SBO3L_ETH_SIGNER_BACKEND").unwrap_or_else(|_| "local_file".to_string());
match backend.as_str() {
"local_file" => Ok(Box::new(eth_local::EthLocalFileSigner::from_env(role)?)),
"aws_kms" => {
#[cfg(feature = "eth_kms_aws")]
{
Ok(Box::new(eth_kms_aws_live::AwsEthKmsLiveSigner::from_env(
role,
)?))
}
#[cfg(all(feature = "aws_kms", not(feature = "eth_kms_aws")))]
{
Ok(Box::new(eth_kms::aws::AwsEthKmsSigner::from_env(role)?))
}
#[cfg(not(any(feature = "aws_kms", feature = "eth_kms_aws")))]
{
let _ = role;
Err(SignerError::BackendNotCompiled("aws_kms"))
}
}
"gcp_kms" => {
#[cfg(feature = "eth_kms_gcp")]
{
Ok(Box::new(eth_kms_gcp_live::GcpEthKmsLiveSigner::from_env(
role,
)?))
}
#[cfg(all(feature = "gcp_kms", not(feature = "eth_kms_gcp")))]
{
Ok(Box::new(eth_kms::gcp::GcpEthKmsSigner::from_env(role)?))
}
#[cfg(not(any(feature = "gcp_kms", feature = "eth_kms_gcp")))]
{
let _ = role;
Err(SignerError::BackendNotCompiled("gcp_kms"))
}
}
other => Err(SignerError::UnknownBackend(other.to_string())),
}
}
pub fn signer_from_env(role: &str) -> Result<Box<dyn Signer>, SignerError> {
let backend = std::env::var("SBO3L_SIGNER_BACKEND").unwrap_or_else(|_| "dev".to_string());
match backend.as_str() {
"dev" => Ok(Box::new(DevSignerLockedDown::from_env(role)?)),
"local_file" => Ok(Box::new(LocalFileSigner::from_env(role)?)),
"aws_kms" => {
#[cfg(feature = "aws_kms")]
{
Ok(Box::new(aws_kms::AwsKmsSigner::from_env(role)?))
}
#[cfg(not(feature = "aws_kms"))]
{
let _ = role;
Err(SignerError::BackendNotCompiled("aws_kms"))
}
}
"gcp_kms" => {
#[cfg(feature = "gcp_kms")]
{
Ok(Box::new(gcp_kms::GcpKmsSigner::from_env(role)?))
}
#[cfg(not(feature = "gcp_kms"))]
{
let _ = role;
Err(SignerError::BackendNotCompiled("gcp_kms"))
}
}
"phala_tee" => {
#[cfg(feature = "phala_tee")]
{
Ok(Box::new(phala_tee::PhalaTeeSigner::from_env(role)?))
}
#[cfg(not(feature = "phala_tee"))]
{
let _ = role;
Err(SignerError::BackendNotCompiled("phala_tee"))
}
}
other => Err(SignerError::UnknownBackend(other.to_string())),
}
}