|
| 1 | +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +use std::{future::Future, panic::AssertUnwindSafe}; |
| 5 | + |
| 6 | +/// The libcrypto that s2n-tls is linked against. |
| 7 | +#[derive(Debug, PartialEq, Eq)] |
| 8 | +enum Libcrypto { |
| 9 | + Awslc, |
| 10 | + AwslcFips, |
| 11 | + OpenSsl102, |
| 12 | + OpenSsl111, |
| 13 | + OpenSsl30, |
| 14 | +} |
| 15 | + |
| 16 | +impl Libcrypto { |
| 17 | + /// Retrieve the libcrypto from the `S2N_LIBCRYPTO` env variable if available, |
| 18 | + /// otherwise return "awslc". |
| 19 | + /// |
| 20 | + /// S2N_LIBCRYPTO is set in CI as well as the Nix devshell. |
| 21 | + fn from_env() -> Self { |
| 22 | + let libcrypto = match std::env::var("S2N_LIBCRYPTO") { |
| 23 | + Ok(libcrypto) => libcrypto, |
| 24 | + Err(_) => { |
| 25 | + println!("S2N_LIBCRYPTO not set, assuming awslc"); |
| 26 | + "awslc".to_string() |
| 27 | + } |
| 28 | + }; |
| 29 | + |
| 30 | + match libcrypto.as_str() { |
| 31 | + "awslc" => Libcrypto::Awslc, |
| 32 | + "awslc-fips" => Libcrypto::AwslcFips, |
| 33 | + "openssl-1.0.2" => Libcrypto::OpenSsl102, |
| 34 | + "openssl-1.1.1" => Libcrypto::OpenSsl111, |
| 35 | + "openssl-3.0" => Libcrypto::OpenSsl30, |
| 36 | + _ => panic!("unexpected libcrypto: {libcrypto}"), |
| 37 | + } |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +/// A `Capability` represents a functionality of s2n-tls that may or may not be |
| 42 | +/// available depending on the linked libcrypto. |
| 43 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
| 44 | +pub enum Capability { |
| 45 | + /// Support for TLS 1.3 |
| 46 | + Tls13, |
| 47 | + /// Support for ML-DSA and ML-KEM |
| 48 | + PQAlgorithms, |
| 49 | +} |
| 50 | + |
| 51 | +impl Capability { |
| 52 | + /// Returns whether a capability is supported. |
| 53 | + /// |
| 54 | + /// Internally, this just maps from the libcrypto to its supported capabilities. |
| 55 | + fn supported(&self) -> bool { |
| 56 | + let libcrypto = Libcrypto::from_env(); |
| 57 | + match self { |
| 58 | + // OpenSSL 1.0.2 doesn't support RSA-PSS, so TLS 1.3 isn't enabled |
| 59 | + Capability::Tls13 => libcrypto != Libcrypto::OpenSsl102, |
| 60 | + // PQ is only supported for AWS-LC |
| 61 | + Capability::PQAlgorithms => { |
| 62 | + libcrypto == Libcrypto::Awslc || libcrypto == Libcrypto::AwslcFips |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +/// Declare the required capabilities for a test to run. |
| 69 | +/// |
| 70 | +/// If all the required capabilities are present then the test must pass. Otherwise |
| 71 | +/// we expect the test to panic/fail. |
| 72 | +pub fn required_capability(required_capabilities: &[Capability], test: fn()) { |
| 73 | + let result = std::panic::catch_unwind(test); |
| 74 | + if required_capabilities.iter().all(|c| c.supported()) { |
| 75 | + result.unwrap(); |
| 76 | + } else { |
| 77 | + println!("expecting test failure"); |
| 78 | + let panic = result.unwrap_err(); |
| 79 | + println!("panic was {panic:?}"); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +pub fn required_capability_async( |
| 84 | + required_capabilities: &[Capability], |
| 85 | + test: impl Future<Output = Result<(), Box<dyn std::error::Error>>>, |
| 86 | +) { |
| 87 | + let rt = tokio::runtime::Builder::new_current_thread() |
| 88 | + .enable_all() |
| 89 | + .build() |
| 90 | + .unwrap(); |
| 91 | + |
| 92 | + let result = std::panic::catch_unwind(AssertUnwindSafe(|| rt.block_on(test))); |
| 93 | + |
| 94 | + if required_capabilities.iter().all(Capability::supported) { |
| 95 | + // 1 -> no panic |
| 96 | + // 2 -> returned "ok" |
| 97 | + result.unwrap().unwrap(); |
| 98 | + } else { |
| 99 | + println!("expecting test failure"); |
| 100 | + match result { |
| 101 | + Ok(Ok(())) => panic!("test did not fail"), |
| 102 | + Ok(Err(e)) => println!("err was {e:?}"), |
| 103 | + Err(e) => println!("panic was {e:?}"), |
| 104 | + } |
| 105 | + } |
| 106 | +} |
0 commit comments