From 9d3eb0953c30faa7549fcd8c0043ec28914fa41c Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 26 Jul 2026 18:57:07 -0700 Subject: [PATCH 1/3] Fix inverted MinTlsVersion protocol version mapping MinTlsVersion::V13 mapped to a ProtocolVersions value with both TLS 1.2 and 1.3 enabled, so it did not exclude TLS 1.2 and would not fail the handshake against a peer that only supports TLS 1.2. MinTlsVersion::V12 mapped to v12_only(), disabling TLS 1.3 entirely. Swap the arms so each matches its documented meaning: V12 allows TLS 1.2 and 1.3, V13 allows only TLS 1.3. Add tests asserting the configured version set for each variant, clarify the enum and FFI schema documentation, and correct the TLS section of the guide. Fixes #437 --- CHANGELOG.md | 4 ++++ dnp3/src/tcp/tls/mod.rs | 39 +++++++++++++++++++++++++++++++---- ffi/dnp3-schema/src/shared.rs | 5 ++++- guide/docs/api/tls.mdx | 2 +- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2de3c31e..d8a8d17e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ +### 1.7.0-RC4 ### +* :shield: Fix inverted `MinTlsVersion` mapping. `MinTlsVersion::V13` previously enabled both TLS 1.2 and 1.3, so it did not exclude TLS 1.2 as documented, and `MinTlsVersion::V12` enabled TLS 1.2 only, disabling TLS 1.3. Both arms now match their documented meaning: `V12` allows TLS 1.2 and 1.3, `V13` allows only TLS 1.3. Affects 1.6.0 through 1.7.0-RC3. See [#437](https://github.com/stepfunc/dnp3/issues/437). +* :bell: **Behavior change for existing TLS configurations.** Users who set `V13` were silently permitting TLS 1.2 and will now reject peers that do not support TLS 1.3. Users on the default `V12` (including all bindings users who never overrode it) will now negotiate TLS 1.3 where the peer supports it, rather than being pinned to TLS 1.2. + ### 1.7.0-RC3 ### * :star: Add unstable, non-spawning master and outstation task APIs behind the semver-exempt `unstable` feature, allowing applications to obtain runnable tasks/futures without the library calling `tokio::spawn` internally. See [#433](https://github.com/stepfunc/dnp3/pull/433). * :star: Add `EndpointList::into_connect_handler()` and `EndpointList::into_connect_handler_with_options()` to build a `ClientConnectionHandler` from an endpoint list, e.g. for use with `spawn_master_tcp_client_3`. See [#433](https://github.com/stepfunc/dnp3/pull/433). diff --git a/dnp3/src/tcp/tls/mod.rs b/dnp3/src/tcp/tls/mod.rs index 09ed80f4..f54bac32 100644 --- a/dnp3/src/tcp/tls/mod.rs +++ b/dnp3/src/tcp/tls/mod.rs @@ -88,19 +88,50 @@ impl std::error::Error for TlsError {} derive(serde::Serialize, serde::Deserialize) )] pub enum MinTlsVersion { - /// TLS 1.2 + /// Allow TLS 1.2 and TLS 1.3 V12, - /// TLS 1.3 + /// Allow only TLS 1.3 + /// + /// A peer that does not support TLS 1.3 will fail to complete the handshake. V13, } impl From for sfio_rustls_config::ProtocolVersions { fn from(value: MinTlsVersion) -> Self { match value { - MinTlsVersion::V12 => sfio_rustls_config::ProtocolVersions::v12_only(), - MinTlsVersion::V13 => sfio_rustls_config::ProtocolVersions::new() + MinTlsVersion::V12 => sfio_rustls_config::ProtocolVersions::new() .enable_v12() .enable_v13(), + MinTlsVersion::V13 => sfio_rustls_config::ProtocolVersions::v13_only(), } } } + +#[cfg(test)] +mod tests { + use super::*; + use sfio_rustls_config::ProtocolVersions; + + /// `MinTlsVersion` is a *minimum*, so v1.2 must also allow v1.3 to be negotiated. + #[test] + fn v12_allows_both_versions() { + assert_eq!( + ProtocolVersions::from(MinTlsVersion::V12), + ProtocolVersions::new().enable_v12().enable_v13() + ); + } + + /// Requesting v1.3 must *exclude* v1.2 so that the handshake fails closed against + /// a peer that only supports v1.2. + #[test] + fn v13_excludes_v12() { + assert_eq!( + ProtocolVersions::from(MinTlsVersion::V13), + ProtocolVersions::v13_only() + ); + assert_ne!( + ProtocolVersions::from(MinTlsVersion::V13), + ProtocolVersions::from(MinTlsVersion::V12) + ); + } +} diff --git a/ffi/dnp3-schema/src/shared.rs b/ffi/dnp3-schema/src/shared.rs index e82d322e..86eed676 100644 --- a/ffi/dnp3-schema/src/shared.rs +++ b/ffi/dnp3-schema/src/shared.rs @@ -736,7 +736,10 @@ fn define_min_tls_version(lib: &mut LibraryBuilder) -> BackTraced { let handle = lib .define_enum("min_tls_version")? .push("v12", "Allow TLS 1.2 and 1.3")? - .push("v13", "Only allow TLS 1.3")? + .push( + "v13", + "Only allow TLS 1.3. A peer that does not support TLS 1.3 will fail to complete the handshake.", + )? .doc("Minimum TLS version to allow")? .build()?; diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index 69ea56ff..73622d85 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -74,7 +74,7 @@ Configure TLS using `TlsClientConfig` or `TlsServerConfig`. Both contain these f - `password`: * Password used to decrypt the private key file. If the key is not encrypted, this **must** be an empty string (`""`) and not `null` or omitted. This applies to all language bindings (Rust, C, C++, Java, C#). See the next section for more details. - `min_tls_version`: - * Minimum TLS version to support. Setting this to `Tls1_3` will force the usage of TLSv1.3. + * Minimum TLS version to support. The default (`Tls1_2`) allows TLS 1.2 and 1.3 to be negotiated. Setting this to `Tls1_3` forces the usage of TLSv1.3: a peer that does not support TLS 1.3 will fail to complete the handshake. - `certificate_mode`: * Mode used to verify the peer certificate. From dfbf53a0808c6494f31129981ccfa036a84623a5 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 26 Jul 2026 19:03:15 -0700 Subject: [PATCH 2/3] Correct MinTlsVersion variant names in the TLS guide The guide referred to Tls1_2 and Tls1_3, which are not the variant names in any binding. Use V12 and V13, matching the convention already used for CertificateMode elsewhere in the same document, and attribute the default to the bindings rather than implying Rust has one. --- guide/docs/api/tls.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index 73622d85..51a33d74 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -74,7 +74,7 @@ Configure TLS using `TlsClientConfig` or `TlsServerConfig`. Both contain these f - `password`: * Password used to decrypt the private key file. If the key is not encrypted, this **must** be an empty string (`""`) and not `null` or omitted. This applies to all language bindings (Rust, C, C++, Java, C#). See the next section for more details. - `min_tls_version`: - * Minimum TLS version to support. The default (`Tls1_2`) allows TLS 1.2 and 1.3 to be negotiated. Setting this to `Tls1_3` forces the usage of TLSv1.3: a peer that does not support TLS 1.3 will fail to complete the handshake. + * Minimum TLS version to support. `V12` allows TLS 1.2 and 1.3 to be negotiated, and is the default in the bindings. Setting this to `V13` forces the usage of TLSv1.3: a peer that does not support TLS 1.3 will fail to complete the handshake. - `certificate_mode`: * Mode used to verify the peer certificate. From 8e6634bc575f6fcfae01c702c7b7e74f9b32b649 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 26 Jul 2026 19:05:34 -0700 Subject: [PATCH 3/3] Use :lock: for the security entry in the changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8a8d17e..ad9479ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ### 1.7.0-RC4 ### -* :shield: Fix inverted `MinTlsVersion` mapping. `MinTlsVersion::V13` previously enabled both TLS 1.2 and 1.3, so it did not exclude TLS 1.2 as documented, and `MinTlsVersion::V12` enabled TLS 1.2 only, disabling TLS 1.3. Both arms now match their documented meaning: `V12` allows TLS 1.2 and 1.3, `V13` allows only TLS 1.3. Affects 1.6.0 through 1.7.0-RC3. See [#437](https://github.com/stepfunc/dnp3/issues/437). +* :lock: Fix inverted `MinTlsVersion` mapping. `MinTlsVersion::V13` previously enabled both TLS 1.2 and 1.3, so it did not exclude TLS 1.2 as documented, and `MinTlsVersion::V12` enabled TLS 1.2 only, disabling TLS 1.3. Both arms now match their documented meaning: `V12` allows TLS 1.2 and 1.3, `V13` allows only TLS 1.3. Affects 1.6.0 through 1.7.0-RC3. See [#437](https://github.com/stepfunc/dnp3/issues/437). * :bell: **Behavior change for existing TLS configurations.** Users who set `V13` were silently permitting TLS 1.2 and will now reject peers that do not support TLS 1.3. Users on the default `V12` (including all bindings users who never overrode it) will now negotiate TLS 1.3 where the peer supports it, rather than being pinned to TLS 1.2. ### 1.7.0-RC3 ###