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;
pub use dev::DevSignerLockedDown;
pub use local_file::{KeyFileFormat, LocalFileSigner};
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())),
}
}