Skip to content

Commit 42a4352

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 b415b5c commit 42a4352

File tree

4 files changed

+72
-7
lines changed

4 files changed

+72
-7
lines changed

src/client.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,14 @@ impl ClientType {
116116
config.validate_domain(),
117117
socks5,
118118
config.timeout(),
119+
config.crypto_provider(),
120+
)?,
121+
None => RawClient::new_ssl(
122+
url.as_str(),
123+
config.validate_domain(),
124+
config.timeout(),
125+
config.crypto_provider(),
119126
)?,
120-
None => {
121-
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?
122-
}
123127
};
124128

125129
Ok(ClientType::SSL(client))

src/config.rs

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

3+
use rustls::crypto::CryptoProvider;
4+
35
/// Configuration for an electrum client
46
///
57
/// Refer to [`Client::from_config`] and [`ClientType::from_config`].
@@ -12,6 +14,9 @@ pub struct Config {
1214
socks5: Option<Socks5Config>,
1315
/// timeout in seconds, default None (depends on TcpStream default)
1416
timeout: Option<Duration>,
17+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
18+
/// An optional [`CryptoProvider`] for users that don't want either default `aws-lc-rs` or `ring` providers
19+
crypto_provider: Option<CryptoProvider>,
1520
/// number of retry if any error, default 1
1621
retry: u8,
1722
/// when ssl, validate the domain, default true
@@ -60,6 +65,13 @@ impl ConfigBuilder {
6065
self
6166
}
6267

68+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
69+
/// Sets the custom [`CryptoProvider`].
70+
pub fn crypto_provider(mut self, crypto_provider: Option<CryptoProvider>) -> Self {
71+
self.config.crypto_provider = crypto_provider;
72+
self
73+
}
74+
6375
/// Sets the retry attempts number
6476
pub fn retry(mut self, retry: u8) -> Self {
6577
self.config.retry = retry;
@@ -135,6 +147,14 @@ impl Config {
135147
pub fn builder() -> ConfigBuilder {
136148
ConfigBuilder::new()
137149
}
150+
151+
#[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
152+
/// Get the configuration for `crypto_provider`
153+
///
154+
/// Set this with [`ConfigBuilder::crypto_provider`]
155+
pub fn crypto_provider(&self) -> Option<&CryptoProvider> {
156+
self.crypto_provider.as_ref()
157+
}
138158
}
139159

140160
impl Default for Config {
@@ -144,6 +164,7 @@ impl Default for Config {
144164
timeout: None,
145165
retry: 1,
146166
validate_domain: true,
167+
crypto_provider: None,
147168
}
148169
}
149170
}

src/raw_client.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ use bitcoin::{Script, Txid};
2222
#[cfg(feature = "use-openssl")]
2323
use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
2424

25+
#[cfg(feature = "use-rustls")]
26+
use rustls::crypto::aws_lc_rs::default_provider;
27+
#[cfg(feature = "use-rustls-ring")]
28+
use rustls::crypto::ring::default_provider;
2529
#[cfg(all(
2630
any(
2731
feature = "default",
@@ -31,6 +35,7 @@ use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
3135
not(feature = "use-openssl")
3236
))]
3337
use rustls::{
38+
crypto::CryptoProvider,
3439
pki_types::ServerName,
3540
pki_types::{Der, TrustAnchor},
3641
ClientConfig, ClientConnection, RootCertStore, StreamOwned,
@@ -368,6 +373,7 @@ impl RawClient<ElectrumSslStream> {
368373
socket_addrs: A,
369374
validate_domain: bool,
370375
timeout: Option<Duration>,
376+
crypto_provider: Option<&CryptoProvider>,
371377
) -> Result<Self, Error> {
372378
debug!(
373379
"new_ssl socket_addrs.domain():{:?} validate_domain:{} timeout:{:?}",
@@ -378,16 +384,27 @@ impl RawClient<ElectrumSslStream> {
378384
if validate_domain {
379385
socket_addrs.domain().ok_or(Error::MissingDomain)?;
380386
}
387+
388+
let crypto_provider = match crypto_provider {
389+
Some(provider) => provider.to_owned(),
390+
391+
#[cfg(feature = "use-rustls")]
392+
None => default_provider(),
393+
394+
#[cfg(feature = "use-rustls-ring")]
395+
None => default_provider(),
396+
};
397+
381398
match timeout {
382399
Some(timeout) => {
383400
let stream = connect_with_total_timeout(socket_addrs.clone(), timeout)?;
384401
stream.set_read_timeout(Some(timeout))?;
385402
stream.set_write_timeout(Some(timeout))?;
386-
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream)
403+
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream, crypto_provider)
387404
}
388405
None => {
389406
let stream = TcpStream::connect(socket_addrs.clone())?;
390-
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream)
407+
Self::new_ssl_from_stream(socket_addrs, validate_domain, stream, crypto_provider)
391408
}
392409
}
393410
}
@@ -397,10 +414,13 @@ impl RawClient<ElectrumSslStream> {
397414
socket_addr: A,
398415
validate_domain: bool,
399416
tcp_stream: TcpStream,
417+
crypto_provider: CryptoProvider,
400418
) -> Result<Self, Error> {
401419
use std::convert::TryFrom;
402420

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

405425
let config = if validate_domain {
406426
socket_addr.domain().ok_or(Error::MissingDomain)?;
@@ -480,6 +500,7 @@ impl RawClient<ElectrumProxyStream> {
480500
validate_domain: bool,
481501
proxy: &crate::Socks5Config,
482502
timeout: Option<Duration>,
503+
crypto_provider: Option<&CryptoProvider>,
483504
) -> Result<RawClient<ElectrumSslStream>, Error> {
484505
let target = target_addr.to_target_addr()?;
485506

@@ -496,7 +517,22 @@ impl RawClient<ElectrumProxyStream> {
496517
stream.get_mut().set_read_timeout(timeout)?;
497518
stream.get_mut().set_write_timeout(timeout)?;
498519

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

src/types.rs

Lines changed: 4 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,7 @@ 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+
Error::CouldNotBuildWithSafeDefaultVersion(_) => f.write_str("Couldn't build the `ClientConfig` with safe default version"),
368372
}
369373
}
370374
}

0 commit comments

Comments
 (0)