Skip to content

Commit 469e52d

Browse files
authored
chore: add static lists of supported TLS parameters (#5698)
1 parent 246c3e0 commit 469e52d

File tree

10 files changed

+542
-2
lines changed

10 files changed

+542
-2
lines changed

.github/workflows/ci_rust.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ jobs:
140140
# in the same unit of compilation as the definition. Generally this just
141141
# means that if the linker can't resolve foo_method in tls/foo.c, you
142142
# forgot to include api/unstable/foo.h in tls/foo.c
143+
144+
# Note that we don't test the whole "standard" workspace, only the
145+
# integration crate. This is because `s2n-tls-sys-internal` (one of our
146+
# test dependencies) can not be built with libs2n.so, and requires static
147+
# linking to access internal details.
143148
run: |
144149
cmake . -Bbuild \
145150
-DBUILD_SHARED_LIBS=on \
@@ -160,7 +165,7 @@ jobs:
160165
161166
# Run tests with the external build
162167
cargo test --manifest-path ${{env.ROOT_PATH}}/Cargo.toml
163-
cargo test --manifest-path ${{env.STANDARD_PATH}}/Cargo.toml
168+
cargo test --manifest-path ${{env.STANDARD_PATH}}/integration/Cargo.toml
164169
165170
echo ""
166171
echo "Test that the external build will enable the proper cfg flag in s2n-tls. The"

bindings/rust/standard/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
[workspace]
22
members = [
33
"benchmarks",
4-
"integration",
4+
"integration", "s2n-metrics-subscriber",
55
"s2n-tls-hyper",
6+
"s2n-tls-sys-internal",
67
"tls-harness",
78
]
89

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "s2n-metrics-subscriber"
3+
version = "0.0.1"
4+
edition = "2021"
5+
6+
# the crate is in development, and should not be published
7+
publish = false
8+
9+
[dependencies]
10+
s2n-tls = { path = "../../extended/s2n-tls"}
11+
12+
[dev-dependencies]
13+
s2n-tls-sys-internal = { path = "../s2n-tls-sys-internal" }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod static_lists;
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! This module contains the static lists of all possible values emitted by the
5+
//! s2n-tls "getter" APIs. These static lists are important because they allow us
6+
//! to maintain an array of atomic counters instead of having to resort to a hashmap
7+
8+
// allowing unused lints while crate is under development, many of these structs
9+
// won't be used until the subscriber is actually implemented
10+
#![allow(unused)]
11+
12+
#[cfg(test)]
13+
use std::ffi::c_char;
14+
15+
#[cfg(test)]
16+
use s2n_tls_sys_internal::{
17+
s2n_cipher_suite, s2n_ecc_named_curve, s2n_kem_group, s2n_signature_scheme,
18+
};
19+
pub trait ToStaticString {
20+
fn to_static_string(&self) -> &'static str;
21+
}
22+
23+
impl ToStaticString for s2n_tls::enums::Version {
24+
fn to_static_string(&self) -> &'static str {
25+
match self {
26+
s2n_tls::enums::Version::SSLV3 => "SSLv3",
27+
s2n_tls::enums::Version::TLS10 => "TLSv1_0",
28+
s2n_tls::enums::Version::TLS11 => "TLSv1_1",
29+
s2n_tls::enums::Version::TLS12 => "TLSv1_2",
30+
s2n_tls::enums::Version::TLS13 => "TLSv1_3",
31+
_ => "unknown",
32+
}
33+
}
34+
}
35+
36+
/// This list should match the negotiable TLS versions in s2n-tls, and determines
37+
/// how many "counter" slots the negotiated version metrics have.
38+
pub const VERSIONS_AVAILABLE_IN_S2N: &[&str] =
39+
&["SSLv3", "TLSv1_0", "TLSv1_1", "TLSv1_2", "TLSv1_3"];
40+
41+
/// Convert a pointer to null terminated bytes into a static string
42+
///
43+
/// Safety: the memory pointed to by value is static
44+
/// Safety: the bytes are null terminated
45+
#[cfg(test)]
46+
unsafe fn static_memory_to_str(value: *const c_char) -> &'static str {
47+
use std::ffi::CStr;
48+
CStr::from_ptr(value).to_str().unwrap()
49+
}
50+
51+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52+
pub(crate) struct Cipher {
53+
iana_description: &'static str,
54+
iana_value: [u8; 2],
55+
openssl_name: &'static str,
56+
}
57+
58+
impl Cipher {
59+
const fn new(
60+
iana_description: &'static str,
61+
iana_value: [u8; 2],
62+
openssl_name: &'static str,
63+
) -> Self {
64+
Self {
65+
openssl_name,
66+
iana_description,
67+
iana_value,
68+
}
69+
}
70+
71+
#[cfg(test)]
72+
fn from_s2n_cipher_suite(s2n_cipher: &s2n_cipher_suite) -> Self {
73+
unsafe {
74+
// SAFETY: the name and iana_name fields are both static, null-terminated
75+
// strings
76+
let openssl_name = static_memory_to_str(s2n_cipher.name);
77+
let iana_description = static_memory_to_str(s2n_cipher.iana_name);
78+
let iana_value = s2n_cipher.iana_value;
79+
Self::new(iana_description, iana_value, openssl_name)
80+
}
81+
}
82+
}
83+
84+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
85+
pub(crate) struct Group {
86+
iana_description: &'static str,
87+
iana_value: u16,
88+
}
89+
90+
impl Group {
91+
const fn new(iana_description: &'static str, iana_value: u16) -> Self {
92+
Self {
93+
iana_description,
94+
iana_value,
95+
}
96+
}
97+
98+
#[cfg(test)]
99+
fn from_s2n_kem_group(kem_group: &s2n_kem_group) -> Self {
100+
unsafe {
101+
// SAFETY: the name field is a static, null-terminated string
102+
let name = static_memory_to_str(kem_group.name);
103+
let iana_id = kem_group.iana_id;
104+
Self::new(name, iana_id)
105+
}
106+
}
107+
108+
#[cfg(test)]
109+
fn from_s2n_ecc_curve(curve: &s2n_ecc_named_curve) -> Self {
110+
unsafe {
111+
// SAFETY: the name field is a static, null-terminated string
112+
let name = static_memory_to_str(curve.name);
113+
let iana_id = curve.iana_id;
114+
Self::new(name, iana_id)
115+
}
116+
}
117+
}
118+
119+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120+
pub(crate) struct SignatureScheme {
121+
iana_description: &'static str,
122+
iana_value: u16,
123+
}
124+
125+
impl SignatureScheme {
126+
const fn new(iana_description: &'static str, iana_value: u16) -> Self {
127+
Self {
128+
iana_description,
129+
iana_value,
130+
}
131+
}
132+
133+
#[cfg(test)]
134+
fn from_s2n_signature_scheme(scheme: &s2n_signature_scheme) -> Self {
135+
unsafe {
136+
// SAFETY: the name field is a static, null-terminated string
137+
let name = static_memory_to_str(scheme.name);
138+
let iana_value = scheme.iana_value;
139+
Self::new(name, iana_value)
140+
}
141+
}
142+
}
143+
144+
/// We are required to track OpenSSL naming because that is what the s2n-tls
145+
/// connection API's return.
146+
#[rustfmt::skip]
147+
pub(crate) const CIPHERS_AVAILABLE_IN_S2N: &[Cipher] = &[
148+
Cipher::new("TLS_AES_128_GCM_SHA256", [19, 1], "TLS_AES_128_GCM_SHA256" ),
149+
Cipher::new("TLS_AES_256_GCM_SHA384", [19, 2], "TLS_AES_256_GCM_SHA384" ),
150+
Cipher::new("TLS_CHACHA20_POLY1305_SHA256", [19, 3], "TLS_CHACHA20_POLY1305_SHA256" ),
151+
Cipher::new("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", [0, 22], "DHE-RSA-DES-CBC3-SHA" ),
152+
Cipher::new("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", [0, 51], "DHE-RSA-AES128-SHA" ),
153+
Cipher::new("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", [0, 103], "DHE-RSA-AES128-SHA256" ),
154+
Cipher::new("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", [0, 158], "DHE-RSA-AES128-GCM-SHA256" ),
155+
Cipher::new("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", [0, 57], "DHE-RSA-AES256-SHA" ),
156+
Cipher::new("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", [0, 107], "DHE-RSA-AES256-SHA256" ),
157+
Cipher::new("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", [0, 159], "DHE-RSA-AES256-GCM-SHA384" ),
158+
Cipher::new("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", [204, 170], "DHE-RSA-CHACHA20-POLY1305" ),
159+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", [192, 9], "ECDHE-ECDSA-AES128-SHA" ),
160+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", [192, 35], "ECDHE-ECDSA-AES128-SHA256" ),
161+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", [192, 43], "ECDHE-ECDSA-AES128-GCM-SHA256" ),
162+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", [192, 10], "ECDHE-ECDSA-AES256-SHA" ),
163+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", [192, 36], "ECDHE-ECDSA-AES256-SHA384" ),
164+
Cipher::new("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", [192, 44], "ECDHE-ECDSA-AES256-GCM-SHA384" ),
165+
Cipher::new("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", [204, 169], "ECDHE-ECDSA-CHACHA20-POLY1305" ),
166+
Cipher::new("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", [192, 18], "ECDHE-RSA-DES-CBC3-SHA" ),
167+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", [192, 19], "ECDHE-RSA-AES128-SHA" ),
168+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", [192, 39], "ECDHE-RSA-AES128-SHA256" ),
169+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", [192, 47], "ECDHE-RSA-AES128-GCM-SHA256" ),
170+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", [192, 20], "ECDHE-RSA-AES256-SHA" ),
171+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", [192, 40], "ECDHE-RSA-AES256-SHA384" ),
172+
Cipher::new("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", [192, 48], "ECDHE-RSA-AES256-GCM-SHA384" ),
173+
Cipher::new("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", [204, 168], "ECDHE-RSA-CHACHA20-POLY1305" ),
174+
Cipher::new("TLS_ECDHE_RSA_WITH_RC4_128_SHA", [192, 17], "ECDHE-RSA-RC4-SHA" ),
175+
Cipher::new("TLS_NULL_WITH_NULL_NULL", [0, 0], "TLS_NULL_WITH_NULL_NULL" ),
176+
Cipher::new("TLS_RSA_WITH_3DES_EDE_CBC_SHA", [0, 10], "DES-CBC3-SHA" ),
177+
Cipher::new("TLS_RSA_WITH_AES_128_CBC_SHA", [0, 47], "AES128-SHA" ),
178+
Cipher::new("TLS_RSA_WITH_AES_128_CBC_SHA256", [0, 60], "AES128-SHA256" ),
179+
Cipher::new("TLS_RSA_WITH_AES_128_GCM_SHA256", [0, 156], "AES128-GCM-SHA256" ),
180+
Cipher::new("TLS_RSA_WITH_AES_256_CBC_SHA", [0, 53], "AES256-SHA" ),
181+
Cipher::new("TLS_RSA_WITH_AES_256_CBC_SHA256", [0, 61], "AES256-SHA256" ),
182+
Cipher::new("TLS_RSA_WITH_AES_256_GCM_SHA384", [0, 157], "AES256-GCM-SHA384" ),
183+
Cipher::new("TLS_RSA_WITH_RC4_128_MD5", [0, 4], "RC4-MD5" ),
184+
Cipher::new("TLS_RSA_WITH_RC4_128_SHA", [0, 5], "RC4-SHA"),
185+
];
186+
187+
pub(crate) const GROUPS_AVAILABLE_IN_S2N: &[Group] = &[
188+
Group::new("MLKEM1024", 514),
189+
Group::new("SecP256r1Kyber768Draft00", 25498),
190+
Group::new("SecP256r1MLKEM768", 4587),
191+
Group::new("SecP384r1MLKEM1024", 4589),
192+
Group::new("X25519Kyber768Draft00", 25497),
193+
Group::new("X25519MLKEM768", 4588),
194+
Group::new("secp256r1", 23),
195+
Group::new("secp256r1_kyber-512-r3", 12090),
196+
Group::new("secp384r1", 24),
197+
Group::new("secp384r1_kyber-768-r3", 12092),
198+
Group::new("secp521r1", 25),
199+
Group::new("secp521r1_kyber-1024-r3", 12093),
200+
Group::new("x25519", 29),
201+
Group::new("x25519_kyber-512-r3", 12089),
202+
];
203+
204+
pub(crate) const SIGNATURE_SCHEMES_AVAILABLE_IN_S2N: &[SignatureScheme] = &[
205+
SignatureScheme::new("ecdsa_sha1", 515),
206+
SignatureScheme::new("ecdsa_sha256", 1027),
207+
SignatureScheme::new("ecdsa_sha384", 1283),
208+
SignatureScheme::new("ecdsa_sha512", 1539),
209+
SignatureScheme::new("legacy_ecdsa_sha224", 771),
210+
SignatureScheme::new("legacy_rsa_md5_sha1", 65535),
211+
SignatureScheme::new("legacy_rsa_sha224", 769),
212+
SignatureScheme::new("mldsa44", 2308),
213+
SignatureScheme::new("mldsa65", 2309),
214+
SignatureScheme::new("mldsa87", 2310),
215+
SignatureScheme::new("rsa_pkcs1_sha1", 513),
216+
SignatureScheme::new("rsa_pkcs1_sha256", 1025),
217+
SignatureScheme::new("rsa_pkcs1_sha384", 1281),
218+
SignatureScheme::new("rsa_pkcs1_sha512", 1537),
219+
SignatureScheme::new("rsa_pss_pss_sha256", 2057),
220+
SignatureScheme::new("rsa_pss_pss_sha384", 2058),
221+
SignatureScheme::new("rsa_pss_pss_sha512", 2059),
222+
SignatureScheme::new("rsa_pss_rsae_sha256", 2052),
223+
SignatureScheme::new("rsa_pss_rsae_sha384", 2053),
224+
SignatureScheme::new("rsa_pss_rsae_sha512", 2054),
225+
];
226+
227+
#[cfg(test)]
228+
mod tests {
229+
use super::*;
230+
use std::collections::HashSet;
231+
232+
/// return all of the ciphers defined in any s2n-tls security policy
233+
fn all_available_ciphers() -> Vec<Cipher> {
234+
let ciphers: HashSet<Cipher> = s2n_tls_sys_internal::security_policy_table()
235+
.iter()
236+
.flat_map(|sp| {
237+
let sp = unsafe { &*sp.security_policy };
238+
let names: Vec<Cipher> = sp
239+
.ciphers()
240+
.iter()
241+
.cloned()
242+
.map(Cipher::from_s2n_cipher_suite)
243+
.collect();
244+
names
245+
})
246+
.collect();
247+
let mut ciphers: Vec<Cipher> = ciphers.into_iter().collect();
248+
ciphers.sort_by_key(|cipher| cipher.iana_description);
249+
ciphers
250+
}
251+
252+
/// return all of the groups defined in any s2n-tls security policy
253+
fn all_available_groups() -> Vec<Group> {
254+
let groups: HashSet<Group> = s2n_tls_sys_internal::security_policy_table()
255+
.iter()
256+
.flat_map(|sp| {
257+
let sp = unsafe { &*sp.security_policy };
258+
let curves = sp
259+
.curves()
260+
.iter()
261+
.map(|curve| Group::from_s2n_ecc_curve(curve));
262+
let kem_groups = sp.kems().iter().map(|kem| Group::from_s2n_kem_group(kem));
263+
curves.chain(kem_groups).collect::<Vec<Group>>()
264+
})
265+
.collect();
266+
let mut groups: Vec<Group> = groups.into_iter().collect();
267+
groups.sort_by_key(|group| group.iana_description);
268+
groups
269+
}
270+
271+
/// return all of the signatures defined in any s2n-tls security policy
272+
fn all_available_signatures() -> Vec<SignatureScheme> {
273+
let sigs: HashSet<SignatureScheme> = s2n_tls_sys_internal::security_policy_table()
274+
.iter()
275+
.flat_map(|sp| {
276+
let sp = unsafe { &*sp.security_policy };
277+
sp.signatures()
278+
.iter()
279+
.map(|sig| SignatureScheme::from_s2n_signature_scheme(sig))
280+
})
281+
.collect();
282+
let mut sigs: Vec<SignatureScheme> = sigs.into_iter().collect();
283+
sigs.sort_by_key(|group| group.iana_description);
284+
sigs
285+
}
286+
287+
#[test]
288+
fn all_ciphers_in_static_list() {
289+
let ciphers = all_available_ciphers();
290+
assert_eq!(&ciphers, CIPHERS_AVAILABLE_IN_S2N);
291+
}
292+
293+
#[test]
294+
fn all_groups_in_static_list() {
295+
let groups = all_available_groups();
296+
assert_eq!(&groups, GROUPS_AVAILABLE_IN_S2N);
297+
}
298+
299+
#[test]
300+
fn all_signature_schemes_in_static_list() {
301+
let schemes = all_available_signatures();
302+
assert_eq!(&schemes, SIGNATURE_SCHEMES_AVAILABLE_IN_S2N);
303+
}
304+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "s2n-tls-sys-internal"
3+
version = "0.0.1"
4+
edition = "2021"
5+
description = "Internal bindings to s2n-tls C library for s2n-metrics-subscriber"
6+
license = "Apache-2.0"
7+
8+
# the crate is only used for testing, and should never be published.
9+
publish = false
10+
11+
[dependencies]
12+
s2n-tls-sys = { path = "../../extended/s2n-tls-sys" }
13+
libc = "0.2.121"
14+
aws-lc-sys = "*"
15+
16+
[build-dependencies]
17+
bindgen = "0.72"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This crate is an internal-only crate which can be used to access and work with private structs from the s2n-tls C library.
2+
3+
This is in contrast to `s2n-tls-sys` which contains bindings to public s2n-tls structs.

0 commit comments

Comments
 (0)