Skip to content

Commit 63beb8a

Browse files
committed
feat: integrate TOFU certificate validation into the main struct
This allows users to use the TOFU flow directly through the high-level API by providing a store implementation in the - Update to automatically establish TOFU-validated SSL connections when a store is provided in the configuration - Add the field to the struct to support Trust On First Use (TOFU) validation. Also change to allow injecting custom store implementations through the builder pattern
1 parent 4004eed commit 63beb8a

File tree

3 files changed

+40
-11
lines changed

3 files changed

+40
-11
lines changed

src/client.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,25 @@ impl ClientType {
110110
pub fn from_config(url: &str, config: &Config) -> Result<Self, Error> {
111111
if url.starts_with("ssl://") {
112112
let url = url.replacen("ssl://", "", 1);
113-
let client = match config.socks5() {
114-
Some(socks5) => RawClient::new_proxy_ssl(
113+
let client = match (config.socks5(), config.tofu_store()) {
114+
(Some(socks5), _) => RawClient::new_proxy_ssl(
115115
url.as_str(),
116116
config.validate_domain(),
117117
socks5,
118118
config.timeout(),
119119
)?,
120-
None => {
121-
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?
122-
}
120+
121+
(None, Some(tofu_store)) => RawClient::new_ssl_with_tofu(
122+
url.as_str(),
123+
tofu_store.clone(),
124+
config.timeout(),
125+
)?,
126+
127+
(None, None) => RawClient::new_ssl(
128+
url.as_str(),
129+
config.validate_domain(),
130+
config.timeout(),
131+
)?,
123132
};
124133

125134
Ok(ClientType::SSL(client))

src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use std::time::Duration;
2+
use std::sync::Arc;
3+
use crate::tofu::TofuStore;
24

35
/// Configuration for an electrum client
46
///
@@ -16,6 +18,8 @@ pub struct Config {
1618
retry: u8,
1719
/// when ssl, validate the domain, default true
1820
validate_domain: bool,
21+
/// TOFU store for certificate validation
22+
tofu_store: Option<Arc<dyn TofuStore>>,
1923
}
2024

2125
/// Configuration for Socks5
@@ -72,6 +76,12 @@ impl ConfigBuilder {
7276
self
7377
}
7478

79+
/// Sets the TOFU store
80+
pub fn tofu_store<S: TofuStore + 'static>(mut self, store: Arc<S>) -> Self {
81+
self.config.tofu_store = Some(store);
82+
self
83+
}
84+
7585
/// Return the config and consume the builder
7686
pub fn build(self) -> Config {
7787
self.config
@@ -131,6 +141,13 @@ impl Config {
131141
self.validate_domain
132142
}
133143

144+
/// Get the TOFU store
145+
///
146+
/// Set this with [`ConfigBuilder::tofu_store`]
147+
pub fn tofu_store(&self) -> &Option<Arc<dyn TofuStore>> {
148+
&self.tofu_store
149+
}
150+
134151
/// Convenience method for calling [`ConfigBuilder::new`]
135152
pub fn builder() -> ConfigBuilder {
136153
ConfigBuilder::new()
@@ -144,6 +161,7 @@ impl Default for Config {
144161
timeout: None,
145162
retry: 1,
146163
validate_domain: true,
164+
tofu_store: None,
147165
}
148166
}
149167
}

src/raw_client.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ use crate::api::ElectrumApi;
4848
use crate::batch::Batch;
4949
use crate::types::*;
5050

51+
use crate::TofuStore;
52+
5153
macro_rules! impl_batch_call {
5254
( $self:expr, $data:expr, $call:ident ) => {{
5355
impl_batch_call!($self, $data, $call, )
@@ -302,9 +304,9 @@ impl RawClient<ElectrumSslStream> {
302304
/// Creates a new SSL client with TOFU (Trust On First Use) certificate validation.
303305
/// This method establishes an SSL connection and verify certificates. On first connection,
304306
/// the certificate is stored. On subsequent onnections, the certificate must match the stored one.
305-
pub fn new_ssl_with_tofu<S: crate::TofuStore + 'static>(
307+
pub fn new_ssl_with_tofu(
306308
socket_addrs: &dyn ToSocketAddrsDomain,
307-
tofu_store: std::sync::Arc<S>,
309+
tofu_store: std::sync::Arc<dyn TofuStore>,
308310
stream: TcpStream,
309311
) -> Result<Self, Error> {
310312
let mut builder =
@@ -420,10 +422,10 @@ mod danger {
420422
}
421423

422424
impl TofuVerifier {
423-
pub fn new<S: TofuStore + 'static>(
425+
pub fn new(
424426
provider: CryptoProvider,
425427
host: String,
426-
tofu_store: Arc<S>,
428+
tofu_store: Arc<dyn TofuStore>,
427429
) -> Self {
428430
Self {
429431
provider,
@@ -628,9 +630,9 @@ impl RawClient<ElectrumSslStream> {
628630
/// Create a new SSL client with TOFU (Trust On First Use) certificate validation.
629631
/// This method establishes an SSL connection and verify certificates. On first connection,
630632
/// the certificate is stored. On subsequent connections, the certificate must match the stored one.
631-
pub fn new_ssl_with_tofu<A: ToSocketAddrsDomain + Clone, S: crate::TofuStore + 'static>(
633+
pub fn new_ssl_with_tofu<A: ToSocketAddrsDomain + Clone>(
632634
socket_addrs: A,
633-
tofu_store: std::sync::Arc<S>,
635+
tofu_store: std::sync::Arc<dyn TofuStore>,
634636
timeout: Option<Duration>,
635637
) -> Result<Self, Error> {
636638

0 commit comments

Comments
 (0)