use std::collections::HashMap;
use async_trait::async_trait;
use base64::Engine;
use std::str::FromStr;
use tokio::sync::Mutex;
use uuid::Uuid;
use crate::security::hsm::config::{HardwareConfig, HardwareDeviceType};
use crate::security::hsm::error::HsmError;
use crate::security::hsm::provider::{
HsmOperation, HsmProvider, HsmProviderStatus, HsmRequest, HsmResponse, KeyGenParams, KeyInfo,
KeyPair, KeyType, KeyUsage, SigningAlgorithm,
};
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::Network;
use bitcoin::{psbt::Psbt, Address};
use chrono::Utc;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
enum ConnectionState {
Disconnected,
Connected,
Authenticated,
}
#[derive(Debug, Clone)]
struct DeviceInfo;
#[derive(Debug)]
pub struct HardwareHsmProvider {
config: HardwareConfig,
device_info: Mutex<Option<DeviceInfo>>,
connection_state: Mutex<ConnectionState>,
keys: Mutex<HashMap<String, KeyInfo>>,
network: Network,
secp: Secp256k1<secp256k1::All>,
}
impl HardwareHsmProvider {
pub async fn new(
config: &HardwareConfig,
network: Network,
_audit_logger: Arc<crate::security::hsm::audit::AuditLogger>,
) -> Result<Self, HsmError> {
Ok(Self {
config: config.clone(),
device_info: Mutex::new(None),
connection_state: Mutex::new(ConnectionState::Disconnected),
keys: Mutex::new(HashMap::new()),
network, secp: Secp256k1::new(),
})
}
fn generate_key_id(&self) -> String {
Uuid::new_v4().to_string()
}
async fn connect(&self) -> Result<(), HsmError> {
let mut state = self.connection_state.lock().await;
if *state == ConnectionState::Connected || *state == ConnectionState::Authenticated {
return Ok(());
}
let device_info = DeviceInfo;
*state = ConnectionState::Connected;
let mut device_info_lock = self.device_info.lock().await;
*device_info_lock = Some(device_info);
tracing::info!(
"Connected to hardware HSM device: {:?}",
self.config.device_type
);
Ok(())
}
async fn authenticate(&self) -> Result<(), HsmError> {
let mut state = self.connection_state.lock().await;
if *state == ConnectionState::Authenticated {
return Ok(());
}
if *state == ConnectionState::Disconnected {
drop(state);
self.connect().await?;
state = self.connection_state.lock().await;
}
match self.config.device_type {
HardwareDeviceType::YubiHsm => {
if self.config.auth_key_id.is_none() {
return Err(HsmError::AuthenticationError(
"Authentication key ID required for YubiHSM".to_string(),
));
}
}
HardwareDeviceType::Ledger | HardwareDeviceType::TrezorModel => {
tracing::info!("Please confirm operation on your device");
}
HardwareDeviceType::Custom => {
if self.config.password.is_none() {
return Err(HsmError::AuthenticationError(
"Password required for custom HSM".to_string(),
));
}
}
}
*state = ConnectionState::Authenticated;
tracing::info!("Authenticated with hardware HSM device");
Ok(())
}
async fn ensure_authenticated(&self) -> Result<(), HsmError> {
let state = self.connection_state.lock().await;
match *state {
ConnectionState::Disconnected => {
drop(state);
self.connect().await?;
self.authenticate().await?;
}
ConnectionState::Connected => {
drop(state);
self.authenticate().await?;
}
ConnectionState::Authenticated => {}
}
Ok(())
}
async fn generate_bitcoin_key(&self, params: &KeyGenParams) -> Result<KeyPair, HsmError> {
self.ensure_authenticated().await?;
let secret_key = SecretKey::new(&mut rand::thread_rng());
let public_key = PublicKey::from_secret_key(&self.secp, &secret_key);
let compressed_pubkey = bitcoin::key::CompressedPublicKey::from_private_key(
&self.secp,
&bitcoin::PrivateKey::new(secret_key, self.network),
)
.map_err(|e| {
HsmError::KeyGenerationError(format!("Failed to create compressed public key: {}", e))
})?;
let address = Address::p2wpkh(&compressed_pubkey, self.network);
tracing::info!("Generated new testnet address on hardware: {}", address);
let key_id = params.id.clone().unwrap_or_else(|| self.generate_key_id());
let key_info = KeyInfo {
id: key_id.clone(),
label: params.label.clone(),
key_type: params.key_type.clone(),
extractable: false, usages: params.usages.clone(),
created_at: Utc::now(),
expires_at: params.expires_at,
attributes: params.attributes.clone(),
};
let mut keys = self.keys.lock().await;
keys.insert(key_id.clone(), key_info);
Ok(KeyPair {
id: key_id.clone(),
key_type: params.key_type.clone(),
public_key: public_key.serialize().to_vec(),
private_key_handle: key_id,
})
}
async fn sign_bitcoin_transaction(&self, key_id: &str, tx: &mut Psbt) -> Result<(), HsmError> {
self.ensure_authenticated().await?;
let keys = self.keys.lock().await;
let key_info = keys
.get(key_id)
.ok_or_else(|| HsmError::KeyNotFound(key_id.to_string()))?;
if !key_info.usages.contains(&KeyUsage::Sign) {
return Err(HsmError::PermissionDenied(
"Key does not have signing permission".to_string(),
));
}
tracing::info!("Hardware device would sign Bitcoin transaction");
tracing::info!(
"Transaction has {} inputs and {} outputs",
tx.inputs.len(),
tx.outputs.len()
);
tracing::info!("Please confirm transaction on your hardware device");
Ok(())
}
}
#[async_trait]
impl HsmProvider for HardwareHsmProvider {
async fn initialize(&self) -> Result<(), HsmError> {
self.connect().await?;
tracing::info!("Initialized hardware HSM for testnet operations");
Ok(())
}
async fn generate_key(&self, params: KeyGenParams) -> Result<(KeyPair, KeyInfo), HsmError> {
match ¶ms.key_type {
KeyType::Ec { curve }
if *curve == crate::security::hsm::provider::EcCurve::Secp256k1 =>
{
let key_pair = self.generate_bitcoin_key(¶ms).await?;
let key_info = KeyInfo {
id: params.id.unwrap_or_else(|| self.generate_key_id()),
label: params.label.clone(),
key_type: params.key_type.clone(),
usages: params.usages.clone(),
extractable: params.extractable,
expires_at: params.expires_at,
created_at: chrono::Utc::now(),
attributes: params.attributes.clone(),
};
Ok((key_pair, key_info))
}
_ => Err(HsmError::UnsupportedKeyType),
}
}
async fn sign(
&self,
key_id: &str,
_algorithm: SigningAlgorithm,
data: &[u8],
) -> Result<Vec<u8>, HsmError> {
self.ensure_authenticated().await?;
let keys = self.keys.lock().await;
let key_info = keys
.get(key_id)
.ok_or_else(|| HsmError::KeyNotFound(key_id.to_string()))?;
if !key_info.usages.contains(&KeyUsage::Sign) {
return Err(HsmError::PermissionDenied(
"Key does not have signing permission".to_string(),
));
}
tracing::info!("Hardware device would sign data of length {}", data.len());
Ok(vec![
0x30, 0x44, 0x02, 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x02, 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
])
}
async fn verify(
&self,
key_id: &str,
_algorithm: SigningAlgorithm,
data: &[u8],
_signature: &[u8],
) -> Result<bool, HsmError> {
self.ensure_authenticated().await?;
let keys = self.keys.lock().await;
let key_info = keys
.get(key_id)
.ok_or_else(|| HsmError::KeyNotFound(key_id.to_string()))?;
if !key_info.usages.contains(&KeyUsage::Verify) {
return Err(HsmError::PermissionDenied(
"Key does not have verify permission".to_string(),
));
}
tracing::info!(
"Hardware device would verify signature for data of length {}",
data.len()
);
Ok(true)
}
async fn export_public_key(&self, key_id: &str) -> Result<Vec<u8>, HsmError> {
self.ensure_authenticated().await?;
let keys = self.keys.lock().await;
let key_info = keys
.get(key_id)
.ok_or_else(|| HsmError::KeyNotFound(key_id.to_string()))?;
tracing::info!(
"Hardware device would export public key for key ID: {}",
key_id
);
Ok(vec![
0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f, 0x20,
])
}
async fn list_keys(&self) -> Result<Vec<KeyInfo>, HsmError> {
self.ensure_authenticated().await?;
let keys = self.keys.lock().await;
Ok(keys.values().cloned().collect())
}
async fn delete_key(&self, key_id: &str) -> Result<(), HsmError> {
self.ensure_authenticated().await?;
let mut keys = self.keys.lock().await;
if keys.remove(key_id).is_none() {
return Err(HsmError::KeyNotFound(key_id.to_string()));
}
tracing::info!("Deleted key {} from hardware device", key_id);
Ok(())
}
async fn get_status(&self) -> Result<HsmProviderStatus, HsmError> {
let state = self.connection_state.lock().await;
match *state {
ConnectionState::Disconnected => Ok(HsmProviderStatus::Unavailable),
ConnectionState::Connected => Ok(HsmProviderStatus::NeedsAuthentication),
ConnectionState::Authenticated => Ok(HsmProviderStatus::Ready),
}
}
async fn close(&self) -> Result<(), HsmError> {
let mut state = self.connection_state.lock().await;
*state = ConnectionState::Disconnected;
tracing::info!("Closed connection to hardware HSM device");
Ok(())
}
async fn execute_operation(&self, request: HsmRequest) -> Result<HsmResponse, HsmError> {
match request.operation {
HsmOperation::GenerateKey => {
let params: KeyGenParams = serde_json::from_value(request.parameters.clone())
.map_err(|e| {
HsmError::InvalidParameters(format!(
"Invalid key generation parameters: {}",
e
))
})?;
let key_pair = self.generate_key(params).await?;
let response_data = serde_json::to_value(key_pair)
.map_err(|e| HsmError::SerializationError(e.to_string()))?;
Ok(HsmResponse::success(request.id, Some(response_data)))
}
HsmOperation::Sign => {
let params: SignParams = serde_json::from_value(request.parameters.clone())
.map_err(|e| {
HsmError::InvalidParameters(format!("Invalid signing parameters: {}", e))
})?;
let signature = self
.sign(¶ms.key_id, params._algorithm, ¶ms.data)
.await?;
let response_data = serde_json::to_value(Base64SignatureResponse {
signature: base64::engine::general_purpose::STANDARD.encode(&signature),
_algorithm: params._algorithm,
})
.map_err(|e| HsmError::SerializationError(e.to_string()))?;
Ok(HsmResponse::success(request.id, Some(response_data)))
}
HsmOperation::Custom(op) if op == "sign_bitcoin_tx" => {
let params: BitcoinTxSignParams =
serde_json::from_value(request.parameters.clone()).map_err(|e| {
HsmError::InvalidParameters(format!(
"Invalid Bitcoin TX signing parameters: {}",
e
))
})?;
let mut psbt = bitcoin::psbt::Psbt::from_str(¶ms.psbt)
.map_err(|e| HsmError::InvalidParameters(format!("Invalid PSBT: {}", e)))?;
self.sign_bitcoin_transaction(¶ms.key_id, &mut psbt)
.await?;
let response_data = serde_json::json!({
"signed_psbt": psbt.to_string(),
"network": "testnet",
});
Ok(HsmResponse::success(request.id, Some(response_data)))
}
HsmOperation::Custom(op) if op == "connect" => {
self.connect().await?;
Ok(HsmResponse::success(request.id, None))
}
HsmOperation::Custom(op) if op == "authenticate" => {
self.authenticate().await?;
Ok(HsmResponse::success(request.id, None))
}
_ => Err(HsmError::UnsupportedOperation(format!(
"{:?}",
request.operation
))),
}
}
}
#[derive(Debug, serde::Deserialize)]
struct SignParams {
key_id: String,
_algorithm: SigningAlgorithm,
data: Vec<u8>,
}
#[derive(Debug, serde::Deserialize)]
struct BitcoinTxSignParams {
key_id: String,
psbt: String,
}
#[derive(Debug, serde::Serialize)]
struct Base64SignatureResponse {
signature: String,
_algorithm: SigningAlgorithm,
}