diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index de58cdef5e9..5bd6810ba27 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: '3.x' diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index 07546311ee9..53fd7e3ac7f 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -37,7 +37,7 @@ jobs: # https://github.com/aws/aws-lc-rs/blob/main/aws-lc-fips-sys/README.md#build-prerequisites # go required for generate.sh to build aws-lc-rs in FIPS mode - name: Install go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '>=1.18' @@ -192,7 +192,7 @@ jobs: # https://github.com/aws/aws-lc-rs/blob/main/aws-lc-fips-sys/README.md#build-prerequisites # go required to build aws-lc-rs in FIPS mode - name: Install go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '>=1.18' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5a8b230f47b..2b91a3ecef2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,7 +22,7 @@ jobs: # Upload the doxygen artifacts on pull requests to help reviewers easily # view changes. if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: s2n-tls-doxygen path: | diff --git a/.github/workflows/proof_ci.yaml b/.github/workflows/proof_ci.yaml index 4bcf60b4b2d..c946ccdf256 100644 --- a/.github/workflows/proof_ci.yaml +++ b/.github/workflows/proof_ci.yaml @@ -226,7 +226,7 @@ jobs: && mv $FINAL_REPORT_DIR/${{ steps.artifact.outputs.name }}.zip . - name: Upload zip artifact of CBMC proof results to GitHub Actions if: ${{ env.REPO_VISIBILITY == 'public' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ steps.artifact.outputs.name }} path: ${{ steps.artifact.outputs.name }}.zip diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 42b74d03143..6c91c08ecc0 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,7 +12,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: stale-pr-message: > This PR has been automatically marked as stale because it has not had diff --git a/.github/workflows/usage_guide.yml b/.github/workflows/usage_guide.yml index 8078bdec870..23ed742dba1 100644 --- a/.github/workflows/usage_guide.yml +++ b/.github/workflows/usage_guide.yml @@ -66,7 +66,7 @@ jobs: echo "URL=$URL" >> $GITHUB_OUTPUT - name: Output mdbook url - uses: ouzi-dev/commit-status-updater@v2.0.1 + uses: ouzi-dev/commit-status-updater@v2 if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name with: name: "book / url" diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml index 8cdf850b3bd..ba4b1ad4686 100644 --- a/bindings/rust/s2n-tls-hyper/Cargo.toml +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -13,15 +13,20 @@ publish = false default = [] [dependencies] -s2n-tls = { version = "=0.3.7", path = "../s2n-tls" } -s2n-tls-tokio = { version = "=0.3.7", path = "../s2n-tls-tokio" } +s2n-tls = { version = "=0.3.8", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.3.8", path = "../s2n-tls-tokio" } hyper = { version = "1" } -hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } +hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1", "http2"] } tower-service = { version = "0.3" } -http = { version= "1" } +http = { version = "1" } [dev-dependencies] tokio = { version = "1", features = ["macros", "test-util"] } http-body-util = "0.1" hyper-util = { version = "0.1", features = ["server"] } bytes = "1" + +# Newer versions require Rust 1.65, see https://github.com/aws/s2n-tls/issues/4242. +hashbrown = { version = "=0.15.0" } +# Newer versions require Rust 1.70, see https://github.com/aws/s2n-tls/issues/4395. +tokio-util = { version = "=0.7.11" } diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index ac5070bdfef..ea5670d62f0 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -41,6 +41,10 @@ where /// /// This API creates an `HttpsConnector` using the default hyper `HttpConnector`. To use an /// existing HTTP connector, use `HttpsConnector::new_with_http()`. + /// + /// Note that s2n-tls-hyper will override the ALPN extension to negotiate HTTP. Any ALPN values + /// configured on `conn_builder` with APIs like + /// `s2n_tls::config::Builder::set_application_protocol_preference()` will be ignored. pub fn new(conn_builder: Builder) -> HttpsConnector { let mut http = HttpConnector::new(); @@ -77,6 +81,10 @@ where /// ``` /// /// `HttpsConnector::new()` can be used to create the HTTP connector automatically. + /// + /// Note that s2n-tls-hyper will override the ALPN extension to negotiate HTTP. Any ALPN values + /// configured on `conn_builder` with APIs like + /// `s2n_tls::config::Builder::set_application_protocol_preference()` will be ignored. pub fn new_with_http(http: Http, conn_builder: Builder) -> HttpsConnector { Self { http, conn_builder } } @@ -118,6 +126,22 @@ where return Box::pin(async move { Err(Error::InvalidScheme) }); } + // Attempt to negotiate HTTP/2 by including it in the ALPN extension. Other supported HTTP + // versions are also included to prevent the server from rejecting the TLS connection if + // HTTP/2 isn't supported: + // + // https://datatracker.ietf.org/doc/html/rfc7301#section-3.2 + // In the event that the server supports no + // protocols that the client advertises, then the server SHALL respond + // with a fatal "no_application_protocol" alert. + let builder = connection::ModifiedBuilder::new(self.conn_builder.clone(), |conn| { + conn.set_application_protocol_preference([ + b"h2".to_vec(), + b"http/1.1".to_vec(), + b"http/1.0".to_vec(), + ]) + }); + // IPv6 addresses are enclosed in square brackets within the host of a URI (e.g. // `https://[::1:2:3:4]/`). These square brackets aren't part of the domain itself, so they // are trimmed off to provide the proper server name to s2n-tls-tokio (e.g. `::1:2:3:4`). @@ -129,7 +153,6 @@ where } let domain = domain.to_owned(); - let builder = self.conn_builder.clone(); let call = self.http.call(req); Box::pin(async move { // `HttpsConnector` wraps an HTTP connector that also implements `Service`. diff --git a/bindings/rust/s2n-tls-hyper/src/stream.rs b/bindings/rust/s2n-tls-hyper/src/stream.rs index 61bc59fe682..c5440285465 100644 --- a/bindings/rust/s2n-tls-hyper/src/stream.rs +++ b/bindings/rust/s2n-tls-hyper/src/stream.rs @@ -48,7 +48,15 @@ where { fn connected(&self) -> Connected { match self { - MaybeHttpsStream::Https(stream) => stream.inner().get_ref().connected(), + MaybeHttpsStream::Https(stream) => { + let connected = stream.inner().get_ref().connected(); + let conn = stream.inner().as_ref(); + match conn.application_protocol() { + // Inform hyper that HTTP/2 was negotiated in the ALPN. + Some(b"h2") => connected.negotiated_h2(), + _ => connected, + } + } } } } diff --git a/bindings/rust/s2n-tls-hyper/tests/http.rs b/bindings/rust/s2n-tls-hyper/tests/http.rs index 1453f63c637..f9d9d001b69 100644 --- a/bindings/rust/s2n-tls-hyper/tests/http.rs +++ b/bindings/rust/s2n-tls-hyper/tests/http.rs @@ -4,7 +4,7 @@ use crate::common::InsecureAcceptAllCertificatesHandler; use bytes::Bytes; use common::echo::serve_echo; -use http::{Method, Request, Uri}; +use http::{Method, Request, Uri, Version}; use http_body_util::{BodyExt, Empty, Full}; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use s2n_tls::{ @@ -264,3 +264,69 @@ async fn ipv6() -> Result<(), Box> { Ok(()) } + +#[tokio::test] +async fn http2() -> Result<(), Box> { + for expected_http_version in [Version::HTTP_11, Version::HTTP_2] { + let server_config = { + let mut builder = common::config()?; + if expected_http_version == Version::HTTP_2 { + builder.set_application_protocol_preference(["h2"])?; + } + builder.build()? + }; + + common::echo::make_echo_request(server_config.clone(), |port| async move { + let connector = HttpsConnector::new(common::config()?.build()?); + let client: Client<_, Empty> = + Client::builder(TokioExecutor::new()).build(connector); + + let uri = Uri::from_str(format!("https://localhost:{}", port).as_str())?; + let response = client.get(uri).await?; + assert_eq!(response.status(), 200); + + // Ensure that HTTP/2 is negotiated when supported by the server. + assert_eq!(response.version(), expected_http_version); + + Ok(()) + }) + .await?; + } + + Ok(()) +} + +/// Ensure that HTTP/2 is negotiated, regardless of any pre-configured ALPN values. +#[tokio::test] +async fn config_alpn_ignored() -> Result<(), Box> { + let server_config = { + let mut builder = common::config()?; + builder.set_application_protocol_preference(["h2"])?; + builder.build()? + }; + + common::echo::make_echo_request(server_config, |port| async move { + let client_config = { + let mut builder = common::config()?; + // Set an arbitrary non-HTTP/2 ALPN value. + builder.set_application_protocol_preference([b"http/1.1"])?; + builder.build()? + }; + + let connector = HttpsConnector::new(client_config); + let client: Client<_, Empty> = + Client::builder(TokioExecutor::new()).build(connector); + + let uri = Uri::from_str(format!("https://localhost:{}", port).as_str())?; + let response = client.get(uri).await?; + assert_eq!(response.status(), 200); + + // Ensure that HTTP/2 was negotiated. + assert_eq!(response.version(), Version::HTTP_2); + + Ok(()) + }) + .await?; + + Ok(()) +} diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index 6c497c5633b..99100448fef 100644 --- a/bindings/rust/s2n-tls-sys/templates/Cargo.template +++ b/bindings/rust/s2n-tls-sys/templates/Cargo.template @@ -1,7 +1,7 @@ [package] name = "s2n-tls-sys" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.3.7" +version = "0.3.8" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" diff --git a/bindings/rust/s2n-tls-tokio/Cargo.toml b/bindings/rust/s2n-tls-tokio/Cargo.toml index fc6db8d014d..038ee9176d2 100644 --- a/bindings/rust/s2n-tls-tokio/Cargo.toml +++ b/bindings/rust/s2n-tls-tokio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls-tokio" description = "An implementation of TLS streams for Tokio built on top of s2n-tls" -version = "0.3.7" +version = "0.3.8" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -15,7 +15,7 @@ default = [] errno = { version = "0.3" } libc = { version = "0.2" } pin-project-lite = { version = "0.2" } -s2n-tls = { version = "=0.3.7", path = "../s2n-tls" } +s2n-tls = { version = "=0.3.8", path = "../s2n-tls" } tokio = { version = "1", features = ["net", "time"] } [dev-dependencies] diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml index 498df276255..d68e390cd64 100644 --- a/bindings/rust/s2n-tls/Cargo.toml +++ b/bindings/rust/s2n-tls/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.3.7" +version = "0.3.8" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -21,7 +21,7 @@ unstable-testing = [] [dependencies] errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.3.7", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.3.8", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" diff --git a/codebuild/spec/buildspec_al2023_ktls.yml b/codebuild/spec/buildspec_al2023_ktls.yml index 6b95d5df73b..f0b19605caf 100644 --- a/codebuild/spec/buildspec_al2023_ktls.yml +++ b/codebuild/spec/buildspec_al2023_ktls.yml @@ -11,8 +11,10 @@ phases: - yum update -y; yum upgrade -y pre_build: commands: - # Nix is installed, but intentionally not setup for root, fix that - - cp -aR /home/nix/.nix-profile ~/; chown -R root /root/.nix-profile; export PATH=$HOME/.nix-profile/bin:$PATH + # (Re)Install nix + - sh <(curl -L https://nixos.org/nix/install) --no-daemon + # Make sure nix exists in the PATH + - export PATH=$HOME/.nix-profile/bin:$PATH # Turn on flakes - mkdir -p ~/.config/nix; echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf # Populate the store from the nix cache diff --git a/codebuild/spec/buildspec_generalbatch.yml b/codebuild/spec/buildspec_generalbatch.yml index dc86e81b94f..192449a331e 100644 --- a/codebuild/spec/buildspec_generalbatch.yml +++ b/codebuild/spec/buildspec_generalbatch.yml @@ -177,10 +177,10 @@ batch: - buildspec: codebuild/spec/buildspec_ubuntu.yml env: compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild + image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 privileged-mode: true variables: - GCC_VERSION: '6' + GCC_VERSION: '13' TESTS: crt identifier: s2nUnitCRT - buildspec: codebuild/spec/buildspec_ubuntu.yml diff --git a/tests/integrationv2/common.py b/tests/integrationv2/common.py index b550d463659..35ba091cd79 100644 --- a/tests/integrationv2/common.py +++ b/tests/integrationv2/common.py @@ -111,7 +111,20 @@ def __init__(self, name, prefix, location=TEST_CERT_DIRECTORY): self.algorithm = 'RSAPSS' def compatible_with_cipher(self, cipher): - return (self.algorithm == cipher.algorithm) or (cipher.algorithm == 'ANY') + if self.algorithm == cipher.algorithm: + return True + # TLS1.3 cipher suites do not specify auth method, so allow any auth method + if cipher.algorithm == 'ANY': + return True + if self.algorithm == 'RSAPSS': + # RSA-PSS certs can only be used by ciphers with RSA auth + if cipher.algorithm != 'RSA': + return False + # RSA-PSS certs do not support RSA key exchange, only RSA auth + # "DHE" here is intended to capture both "DHE" and "ECDHE" + if 'DHE' in cipher.name: + return True + return False def compatible_with_curve(self, curve): if self.algorithm != 'EC': @@ -442,7 +455,7 @@ class Signatures(object): RSA_PSS_PSS_SHA256 = Signature( 'rsa_pss_pss_sha256', - min_protocol=Protocols.TLS13, + min_protocol=Protocols.TLS12, sig_type='RSA-PSS-PSS', sig_digest='SHA256') diff --git a/tests/integrationv2/providers.py b/tests/integrationv2/providers.py index ae203af6e6c..185d4f08be1 100644 --- a/tests/integrationv2/providers.py +++ b/tests/integrationv2/providers.py @@ -148,22 +148,24 @@ def get_send_marker(cls): @classmethod def supports_protocol(cls, protocol, with_cert=None): - # TLS 1.3 is unsupported for openssl-1.0 + # RSA-PSS is unsupported for openssl-1.0 # libressl and boringssl are disabled because of configuration issues # see https://github.com/aws/s2n-tls/issues/3250 - TLS_13_UNSUPPORTED_LIBCRYPTOS = { + PSS_UNSUPPORTED_LIBCRYPTOS = { "libressl", "boringssl", "openssl-1.0" } - - # Disable TLS 1.3 tests for all libcryptos that don't support 1.3 - if protocol == Protocols.TLS13: - current_libcrypto = get_flag(S2N_PROVIDER_VERSION) - for unsupported_lc in TLS_13_UNSUPPORTED_LIBCRYPTOS: - # e.g. "openssl-1.0" in "openssl-1.0.2-fips" - if unsupported_lc in current_libcrypto: - return False + pss_is_unsupported = any([ + # e.g. "openssl-1.0" in "openssl-1.0.2-fips" + libcrypto in get_flag(S2N_PROVIDER_VERSION) + for libcrypto in PSS_UNSUPPORTED_LIBCRYPTOS + ]) + if pss_is_unsupported: + if protocol == Protocols.TLS13: + return False + if with_cert and with_cert.algorithm == 'RSAPSS': + return False # SSLv3 cannot be negotiated in FIPS mode with libcryptos other than AWS-LC. if all([ diff --git a/tests/integrationv2/utils.py b/tests/integrationv2/utils.py index 3d7c2022120..47842fd3bdb 100644 --- a/tests/integrationv2/utils.py +++ b/tests/integrationv2/utils.py @@ -72,9 +72,8 @@ def invalid_test_parameters(*args, **kwargs): # Always consider S2N providers.append(S2N) - # Only TLS1.3 supports RSA-PSS-PSS certificates - # (Earlier versions support RSA-PSS signatures, just via RSA-PSS-RSAE) - if protocol and protocol is not Protocols.TLS13: + # Older versions do not support RSA-PSS-PSS certificates + if protocol and protocol < Protocols.TLS12: if client_certificate and client_certificate.algorithm == 'RSAPSS': return True if certificate and certificate.algorithm == 'RSAPSS':