Skip to content

Commit e80e4d7

Browse files
committed
feat: add optional CryptoProvider to the client Config
It adds a new field to the client `Config, expecting the `CryptoProvider` from the user. It uses aws-lc-rs or ring providers by default if any of these features are enabled. It's based on the suggestion comment at bitcoindevkit#135, reference: bitcoindevkit#135 (comment)
1 parent 746a0e6 commit e80e4d7

File tree

4 files changed

+109
-4
lines changed

4 files changed

+109
-4
lines changed

src/client.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,39 @@ 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+
#[cfg(all(
114+
any(
115+
feature = "default",
116+
feature = "use-rustls",
117+
feature = "use-rustls-ring"
118+
),
119+
not(feature = "use-openssl")
120+
))]
113121
let client = match config.socks5() {
114122
Some(socks5) => RawClient::new_proxy_ssl(
115123
url.as_str(),
116124
config.validate_domain(),
117125
socks5,
118126
config.timeout(),
127+
config.crypto_provider(),
119128
)?,
129+
None => RawClient::new_ssl(
130+
url.as_str(),
131+
config.validate_domain(),
132+
config.timeout(),
133+
config.crypto_provider(),
134+
)?,
135+
};
136+
137+
#[cfg(feature = "openssl")]
138+
let client = match config.socks5() {
139+
Some(socks5) => RawClient::new_proxy_ssl(
140+
url.as_str(),
141+
config.validate_domain(),
142+
socks5,
143+
config.timeout(),
144+
)?,
145+
120146
None => {
121147
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?
122148
}

src/config.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use std::time::Duration;
22

3+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
4+
use rustls::crypto::CryptoProvider;
5+
36
/// Configuration for an electrum client
47
///
58
/// Refer to [`Client::from_config`] and [`ClientType::from_config`].
@@ -12,6 +15,16 @@ pub struct Config {
1215
socks5: Option<Socks5Config>,
1316
/// timeout in seconds, default None (depends on TcpStream default)
1417
timeout: Option<Duration>,
18+
#[cfg(all(
19+
any(
20+
feature = "default",
21+
feature = "use-rustls",
22+
feature = "use-rustls-ring"
23+
),
24+
not(feature = "use-openssl")
25+
))]
26+
/// An optional [`CryptoProvider`] for users that don't want either default `aws-lc-rs` or `ring` providers
27+
crypto_provider: Option<CryptoProvider>,
1528
/// number of retry if any error, default 1
1629
retry: u8,
1730
/// when ssl, validate the domain, default true
@@ -60,6 +73,13 @@ impl ConfigBuilder {
6073
self
6174
}
6275

76+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
77+
/// Sets the custom [`CryptoProvider`].
78+
pub fn crypto_provider(mut self, crypto_provider: Option<CryptoProvider>) -> Self {
79+
self.config.crypto_provider = crypto_provider;
80+
self
81+
}
82+
6383
/// Sets the retry attempts number
6484
pub fn retry(mut self, retry: u8) -> Self {
6585
self.config.retry = retry;
@@ -135,6 +155,21 @@ impl Config {
135155
pub fn builder() -> ConfigBuilder {
136156
ConfigBuilder::new()
137157
}
158+
159+
#[cfg(all(
160+
any(
161+
feature = "default",
162+
feature = "use-rustls",
163+
feature = "use-rustls-ring"
164+
),
165+
not(feature = "use-openssl")
166+
))]
167+
/// Get the configuration for `crypto_provider`
168+
///
169+
/// Set this with [`ConfigBuilder::crypto_provider`]
170+
pub fn crypto_provider(&self) -> Option<&CryptoProvider> {
171+
self.crypto_provider.as_ref()
172+
}
138173
}
139174

140175
impl Default for Config {
@@ -144,6 +179,8 @@ impl Default for Config {
144179
timeout: None,
145180
retry: 1,
146181
validate_domain: true,
182+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
183+
crypto_provider: None,
147184
}
148185
}
149186
}

src/raw_client.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,17 @@ use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
3131
not(feature = "use-openssl")
3232
))]
3333
use rustls::{
34+
crypto::CryptoProvider,
3435
pki_types::ServerName,
3536
pki_types::{Der, TrustAnchor},
3637
ClientConfig, ClientConnection, RootCertStore, StreamOwned,
3738
};
3839

40+
#[cfg(feature = "use-rustls")]
41+
use rustls::crypto::aws_lc_rs::default_provider;
42+
#[cfg(feature = "use-rustls-ring")]
43+
use rustls::crypto::ring::default_provider;
44+
3945
#[cfg(any(feature = "default", feature = "proxy"))]
4046
use crate::socks::{Socks5Stream, TargetAddr, ToTargetAddr};
4147

@@ -368,6 +374,7 @@ impl RawClient<ElectrumSslStream> {
368374
socket_addrs: A,
369375
validate_domain: bool,
370376
timeout: Option<Duration>,
377+
crypto_provider: Option<&CryptoProvider>,
371378
) -> Result<Self, Error> {
372379
debug!(
373380
"new_ssl socket_addrs.domain():{:?} validate_domain:{} timeout:{:?}",
@@ -378,16 +385,27 @@ impl RawClient<ElectrumSslStream> {
378385
if validate_domain {
379386
socket_addrs.domain().ok_or(Error::MissingDomain)?;
380387
}
388+
389+
let crypto_provider = match crypto_provider {
390+
Some(provider) => provider.to_owned(),
391+
392+
#[cfg(feature = "use-rustls")]
393+
None => default_provider(),
394+
395+
#[cfg(feature = "use-rustls-ring")]
396+
None => default_provider(),
397+
};
398+
381399
match timeout {
382400
Some(timeout) => {
383401
let stream = connect_with_total_timeout(socket_addrs.clone(), timeout)?;
384402
stream.set_read_timeout(Some(timeout))?;
385403
stream.set_write_timeout(Some(timeout))?;
386-
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream)
404+
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream, crypto_provider)
387405
}
388406
None => {
389407
let stream = TcpStream::connect(socket_addrs.clone())?;
390-
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream)
408+
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream, crypto_provider)
391409
}
392410
}
393411
}
@@ -397,10 +415,13 @@ impl RawClient<ElectrumSslStream> {
397415
socket_addr: A,
398416
validate_domain: bool,
399417
tcp_stream: TcpStream,
418+
crypto_provider: CryptoProvider,
400419
) -> Result<Self, Error> {
401420
use std::convert::TryFrom;
402421

403-
let builder = ClientConfig::builder();
422+
let builder = ClientConfig::builder_with_provider(crypto_provider.into())
423+
.with_safe_default_protocol_versions()
424+
.map_err(|e| Error::CouldNotBuildWithSafeDefaultVersion(e))?;
404425

405426
let config = if validate_domain {
406427
socket_addr.domain().ok_or(Error::MissingDomain)?;
@@ -480,6 +501,7 @@ impl RawClient<ElectrumProxyStream> {
480501
validate_domain: bool,
481502
proxy: &crate::Socks5Config,
482503
timeout: Option<Duration>,
504+
crypto_provider: Option<&CryptoProvider>,
483505
) -> Result<RawClient<ElectrumSslStream>, Error> {
484506
let target = target_addr.to_target_addr()?;
485507

@@ -496,7 +518,22 @@ impl RawClient<ElectrumProxyStream> {
496518
stream.get_mut().set_read_timeout(timeout)?;
497519
stream.get_mut().set_write_timeout(timeout)?;
498520

499-
RawClient::new_ssl_from_stream(target, validate_domain, stream.into_inner())
521+
let crypto_provider = match crypto_provider {
522+
Some(provider) => provider.to_owned(),
523+
524+
#[cfg(feature = "use-rustls")]
525+
None => default_provider(),
526+
527+
#[cfg(feature = "use-rustls-ring")]
528+
None => default_provider(),
529+
};
530+
531+
RawClient::new_ssl_from_stream(
532+
target,
533+
validate_domain,
534+
stream.into_inner(),
535+
crypto_provider,
536+
)
500537
}
501538
}
502539

src/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ pub enum Error {
318318
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
319319
/// Could not create a rustls client connection
320320
CouldNotCreateConnection(rustls::Error),
321+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
322+
/// Could not create the `ClientConfig` with safe default protocol version
323+
CouldNotBuildWithSafeDefaultVersion(rustls::Error),
321324

322325
#[cfg(feature = "use-openssl")]
323326
/// Invalid OpenSSL method used
@@ -365,6 +368,8 @@ impl Display for Error {
365368
Error::MissingDomain => f.write_str("Missing domain while it was explicitly asked to validate it"),
366369
Error::CouldntLockReader => f.write_str("Couldn't take a lock on the reader mutex. This means that there's already another reader thread is running"),
367370
Error::Mpsc => f.write_str("Broken IPC communication channel: the other thread probably has exited"),
371+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
372+
Error::CouldNotBuildWithSafeDefaultVersion(_) => f.write_str("Couldn't build the `ClientConfig` with safe default version"),
368373
}
369374
}
370375
}

0 commit comments

Comments
 (0)