Skip to content

Commit ce40fc2

Browse files
committed
refactor(ffi): setup_tls_params (previous setup_tls_info)
refactor(ffi): `ffi::CryptoInfo` is renamed to `ffi::TlsCryptoInfo`, and no longer exported
1 parent f41ae94 commit ce40fc2

File tree

2 files changed

+175
-188
lines changed

2 files changed

+175
-188
lines changed

ktls/src/ffi.rs

Lines changed: 143 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use std::io;
2-
use std::os::fd::AsFd;
1+
use std::os::fd::{AsFd, AsRawFd};
32
use std::os::unix::prelude::RawFd;
3+
use std::{io, mem};
44

55
use ktls_sys::bindings as ktls;
66
use nix::sys::socket::{setsockopt, sockopt};
7+
use rustls::crypto::cipher::NONCE_LEN;
78
use rustls::internal::msgs::enums::AlertLevel;
89
use rustls::internal::msgs::message::Message;
9-
use rustls::{AlertDescription, ConnectionTrafficSecrets, SupportedCipherSuite};
10+
use rustls::{AlertDescription, ConnectionTrafficSecrets, ExtractedSecrets, SupportedCipherSuite};
1011

1112
pub(crate) const TLS_1_2_VERSION_NUMBER: u16 = (((ktls::TLS_1_2_VERSION_MAJOR & 0xFF) as u16) << 8)
1213
| ((ktls::TLS_1_2_VERSION_MINOR & 0xFF) as u16);
@@ -17,12 +18,6 @@ pub(crate) const TLS_1_3_VERSION_NUMBER: u16 = (((ktls::TLS_1_3_VERSION_MAJOR &
1718
/// `setsockopt` level constant: TLS
1819
const SOL_TLS: libc::c_int = 282;
1920

20-
/// `setsockopt` SOL_TLS level constant: transmit (write)
21-
const TLS_TX: libc::c_int = 1;
22-
23-
/// `setsockopt` SOL_TLS level constant: receive (read)
24-
const TLX_RX: libc::c_int = 2;
25-
2621
/// Sets the TLS Upper Layer Protocol (ULP).
2722
///
2823
/// This should be called before performing any I/O operations on the
@@ -62,82 +57,131 @@ impl From<SetupUlpError> for io::Error {
6257
}
6358
}
6459

65-
#[derive(Clone, Copy, Debug)]
66-
pub enum Direction {
67-
// Transmit
68-
Tx,
69-
// Receive
70-
Rx,
71-
}
60+
/// Sets the kTLS parameters on the socket after the TLS handshake is completed.
61+
///
62+
/// ## Errors
63+
///
64+
/// * Invalid crypto materials.
65+
/// * Syscall error.
66+
pub(crate) fn setup_tls_params<S: AsFd>(
67+
socket: &S,
68+
cipher_suite: SupportedCipherSuite,
69+
secrets: ExtractedSecrets,
70+
) -> io::Result<()> {
71+
TlsCryptoInfo::extract(cipher_suite, secrets.tx)?.set_tx(socket)?;
72+
TlsCryptoInfo::extract(cipher_suite, secrets.rx)?.set_rx(socket)?;
7273

73-
impl From<Direction> for libc::c_int {
74-
fn from(val: Direction) -> Self {
75-
match val {
76-
Direction::Tx => TLS_TX,
77-
Direction::Rx => TLX_RX,
78-
}
79-
}
74+
Ok(())
8075
}
8176

82-
#[allow(dead_code)]
83-
pub enum CryptoInfo {
84-
AesGcm128(ktls::tls12_crypto_info_aes_gcm_128),
85-
AesGcm256(ktls::tls12_crypto_info_aes_gcm_256),
86-
AesCcm128(ktls::tls12_crypto_info_aes_ccm_128),
87-
Chacha20Poly1305(ktls::tls12_crypto_info_chacha20_poly1305),
88-
Sm4Gcm(ktls::tls12_crypto_info_sm4_gcm),
89-
Sm4Ccm(ktls::tls12_crypto_info_sm4_ccm),
77+
#[repr(C)]
78+
#[allow(unused)]
79+
/// A wrapper around the `libc::tls12_crypto_info_*` structs, use with setting
80+
/// up the kTLS r/w parameters on the TCP socket.
81+
///
82+
/// This is originated from the `nix` crate, which currently does not support
83+
/// `AES-128-CCM` or `SM4-*`, so we implement our own version here.
84+
pub(crate) enum TlsCryptoInfo {
85+
AesGcm128(libc::tls12_crypto_info_aes_gcm_128),
86+
AesGcm256(libc::tls12_crypto_info_aes_gcm_256),
87+
AesCcm128(libc::tls12_crypto_info_aes_ccm_128),
88+
Chacha20Poly1305(libc::tls12_crypto_info_chacha20_poly1305),
89+
Sm4Gcm(libc::tls12_crypto_info_sm4_gcm),
90+
Sm4Ccm(libc::tls12_crypto_info_sm4_ccm),
9091
}
9192

92-
impl CryptoInfo {
93-
/// Return the system struct as a pointer.
94-
pub fn as_ptr(&self) -> *const libc::c_void {
95-
match self {
96-
CryptoInfo::AesGcm128(info) => info as *const _ as *const libc::c_void,
97-
CryptoInfo::AesGcm256(info) => info as *const _ as *const libc::c_void,
98-
CryptoInfo::AesCcm128(info) => info as *const _ as *const libc::c_void,
99-
CryptoInfo::Chacha20Poly1305(info) => info as *const _ as *const libc::c_void,
100-
CryptoInfo::Sm4Gcm(info) => info as *const _ as *const libc::c_void,
101-
CryptoInfo::Sm4Ccm(info) => info as *const _ as *const libc::c_void,
102-
}
93+
impl TlsCryptoInfo {
94+
/// Sets the kTLS parameters on the given file descriptor, assuming that the
95+
/// [`TlsCryptoInfo`] is *extract* from the sequence number and
96+
/// secrets for the "tx" (transmit) direction.
97+
pub(crate) fn set_tx<S: AsFd>(self, socket: &S) -> io::Result<()> {
98+
self.set(socket, libc::TLS_TX)
10399
}
104100

105-
/// Return the system struct size.
106-
pub fn size(&self) -> usize {
107-
match self {
108-
CryptoInfo::AesGcm128(_) => std::mem::size_of::<ktls::tls12_crypto_info_aes_gcm_128>(),
109-
CryptoInfo::AesGcm256(_) => std::mem::size_of::<ktls::tls12_crypto_info_aes_gcm_256>(),
110-
CryptoInfo::AesCcm128(_) => std::mem::size_of::<ktls::tls12_crypto_info_aes_ccm_128>(),
111-
CryptoInfo::Chacha20Poly1305(_) => {
112-
std::mem::size_of::<ktls::tls12_crypto_info_chacha20_poly1305>()
113-
}
114-
CryptoInfo::Sm4Gcm(_) => std::mem::size_of::<ktls::tls12_crypto_info_sm4_gcm>(),
115-
CryptoInfo::Sm4Ccm(_) => std::mem::size_of::<ktls::tls12_crypto_info_sm4_ccm>(),
101+
/// Sets the kTLS parameters on the given file descriptor, assuming that the
102+
/// [`TlsCryptoInfo`] is *extract* from the sequence number and
103+
/// secrets for the "rx" (receive) direction.
104+
pub(crate) fn set_rx<S: AsFd>(self, socket: &S) -> io::Result<()> {
105+
self.set(socket, libc::TLS_RX)
106+
}
107+
108+
/// Sets the kTLS parameters on the given file descriptor.
109+
fn set<S: AsFd>(&self, socket: &S, direction: libc::c_int) -> io::Result<()> {
110+
let (ffi_ptr, ffi_len) = match self {
111+
Self::AesGcm128(crypto_info) => (
112+
<*const _>::cast(crypto_info),
113+
mem::size_of_val(crypto_info) as libc::socklen_t,
114+
),
115+
Self::AesGcm256(crypto_info) => (
116+
<*const _>::cast(crypto_info),
117+
mem::size_of_val(crypto_info) as libc::socklen_t,
118+
),
119+
Self::AesCcm128(crypto_info) => (
120+
<*const _>::cast(crypto_info),
121+
mem::size_of_val(crypto_info) as libc::socklen_t,
122+
),
123+
Self::Chacha20Poly1305(crypto_info) => (
124+
<*const _>::cast(crypto_info),
125+
mem::size_of_val(crypto_info) as libc::socklen_t,
126+
),
127+
Self::Sm4Gcm(crypto_info) => (
128+
<*const _>::cast(crypto_info),
129+
mem::size_of_val(crypto_info) as libc::socklen_t,
130+
),
131+
Self::Sm4Ccm(crypto_info) => (
132+
<*const _>::cast(crypto_info),
133+
mem::size_of_val(crypto_info) as libc::socklen_t,
134+
),
135+
};
136+
137+
// SAFETY: syscall
138+
let ret = unsafe {
139+
libc::setsockopt(
140+
socket.as_fd().as_raw_fd(),
141+
libc::SOL_TLS,
142+
direction,
143+
ffi_ptr,
144+
ffi_len,
145+
)
146+
};
147+
148+
if ret < 0 {
149+
return Err(io::Error::last_os_error());
116150
}
151+
152+
Ok(())
117153
}
118154
}
119155

120-
#[derive(thiserror::Error, Debug)]
121-
pub enum KtlsCompatibilityError {
122-
#[error("cipher suite not supported with kTLS: {0:?}")]
123-
UnsupportedCipherSuite(SupportedCipherSuite),
124-
125-
#[error("wrong size key")]
156+
#[derive(Debug, thiserror::Error)]
157+
/// Crypto material is invalid, e.g., wrong size key or IV.
158+
enum InvalidCryptoInfo {
159+
#[error("Wrong size key")]
160+
/// The provided key has an incorrect size (unlikely).
126161
WrongSizeKey,
127162

128-
#[error("wrong size iv")]
129-
WrongSizeIv,
163+
#[error("The negotiated cipher suite [{0:?}] is not supported by the current kernel")]
164+
/// The negotiated cipher suite is not supported by the current kernel.
165+
UnsupportedCipherSuite(SupportedCipherSuite),
166+
}
167+
168+
impl From<InvalidCryptoInfo> for io::Error {
169+
fn from(err: InvalidCryptoInfo) -> Self {
170+
io::Error::other(err)
171+
}
130172
}
131173

132-
impl CryptoInfo {
133-
/// Try to convert rustls cipher suite and secrets into a `CryptoInfo`.
134-
pub fn from_rustls(
174+
impl TlsCryptoInfo {
175+
/// Extract the [`TlsCryptoInfo`] from the given
176+
/// [`SupportedCipherSuite`] and [`ConnectionTrafficSecrets`].
177+
fn extract(
135178
cipher_suite: SupportedCipherSuite,
136179
(seq, secrets): (u64, ConnectionTrafficSecrets),
137-
) -> Result<CryptoInfo, KtlsCompatibilityError> {
180+
) -> Result<Self, InvalidCryptoInfo> {
138181
let version = match cipher_suite {
139-
SupportedCipherSuite::Tls12(..) => TLS_1_2_VERSION_NUMBER,
140-
SupportedCipherSuite::Tls13(..) => TLS_1_3_VERSION_NUMBER,
182+
#[cfg(feature = "tls12")]
183+
SupportedCipherSuite::Tls12(..) => libc::TLS_1_2_VERSION,
184+
SupportedCipherSuite::Tls13(..) => libc::TLS_1_3_VERSION,
141185
};
142186

143187
Ok(match secrets {
@@ -146,117 +190,64 @@ impl CryptoInfo {
146190
// rustls 0.21 and 0.22, the extract_keys codepath was changed,
147191
// so, for TLS 1.2, both GCM-128 and GCM-256 return the
148192
// Aes128Gcm variant.
193+
//
194+
// This issue is fixed since rustls 0.23.
149195

150-
match key.as_ref().len() {
151-
16 => CryptoInfo::AesGcm128(ktls::tls12_crypto_info_aes_gcm_128 {
152-
info: ktls::tls_crypto_info {
153-
version,
154-
cipher_type: ktls::TLS_CIPHER_AES_GCM_128 as _,
155-
},
156-
iv: iv
157-
.as_ref()
158-
.get(4..)
159-
.expect("AES-GCM-128 iv is 8 bytes")
160-
.try_into()
161-
.expect("AES-GCM-128 iv is 8 bytes"),
162-
key: key
163-
.as_ref()
164-
.try_into()
165-
.expect("AES-GCM-128 key is 16 bytes"),
166-
salt: iv
167-
.as_ref()
168-
.get(..4)
169-
.expect("AES-GCM-128 salt is 4 bytes")
170-
.try_into()
171-
.expect("AES-GCM-128 salt is 4 bytes"),
172-
rec_seq: seq.to_be_bytes(),
173-
}),
174-
32 => CryptoInfo::AesGcm256(ktls::tls12_crypto_info_aes_gcm_256 {
175-
info: ktls::tls_crypto_info {
176-
version,
177-
cipher_type: ktls::TLS_CIPHER_AES_GCM_256 as _,
178-
},
179-
iv: iv
180-
.as_ref()
181-
.get(4..)
182-
.expect("AES-GCM-256 iv is 8 bytes")
183-
.try_into()
184-
.expect("AES-GCM-256 iv is 8 bytes"),
185-
key: key
186-
.as_ref()
187-
.try_into()
188-
.expect("AES-GCM-256 key is 32 bytes"),
189-
salt: iv
190-
.as_ref()
191-
.get(..4)
192-
.expect("AES-GCM-256 salt is 4 bytes")
193-
.try_into()
194-
.expect("AES-GCM-256 salt is 4 bytes"),
195-
rec_seq: seq.to_be_bytes(),
196-
}),
197-
_ => unreachable!("GCM key length is not 16 or 32"),
198-
}
199-
}
200-
ConnectionTrafficSecrets::Aes256Gcm { key, iv } => {
201-
CryptoInfo::AesGcm256(ktls::tls12_crypto_info_aes_gcm_256 {
202-
info: ktls::tls_crypto_info {
196+
let iv_and_salt: &[u8; NONCE_LEN] = iv.as_ref().try_into().unwrap();
197+
198+
Self::AesGcm128(libc::tls12_crypto_info_aes_gcm_128 {
199+
info: libc::tls_crypto_info {
203200
version,
204-
cipher_type: ktls::TLS_CIPHER_AES_GCM_256 as _,
201+
cipher_type: libc::TLS_CIPHER_AES_GCM_128,
205202
},
206-
iv: iv
207-
.as_ref()
208-
.get(4..)
209-
.expect("AES-GCM-256 iv is 8 bytes")
210-
.try_into()
211-
.expect("AES-GCM-256 iv is 8 bytes"),
203+
iv: iv_and_salt[4..].try_into().unwrap(),
212204
key: key
213205
.as_ref()
214206
.try_into()
215-
.expect("AES-GCM-256 key is 32 bytes"),
216-
salt: iv
207+
.map_err(|_| InvalidCryptoInfo::WrongSizeKey)?,
208+
salt: iv_and_salt[..4].try_into().unwrap(),
209+
rec_seq: seq.to_be_bytes(),
210+
})
211+
}
212+
ConnectionTrafficSecrets::Aes256Gcm { key, iv } => {
213+
let iv_and_salt: &[u8; NONCE_LEN] = iv.as_ref().try_into().unwrap();
214+
215+
Self::AesGcm256(libc::tls12_crypto_info_aes_gcm_256 {
216+
info: libc::tls_crypto_info {
217+
version,
218+
cipher_type: libc::TLS_CIPHER_AES_GCM_256,
219+
},
220+
iv: iv_and_salt[4..].try_into().unwrap(),
221+
key: key
217222
.as_ref()
218-
.get(..4)
219-
.expect("AES-GCM-256 salt is 4 bytes")
220223
.try_into()
221-
.expect("AES-GCM-256 salt is 4 bytes"),
224+
.map_err(|_| InvalidCryptoInfo::WrongSizeKey)?,
225+
salt: iv_and_salt[..4].try_into().unwrap(),
222226
rec_seq: seq.to_be_bytes(),
223227
})
224228
}
225229
ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv } => {
226-
CryptoInfo::Chacha20Poly1305(ktls::tls12_crypto_info_chacha20_poly1305 {
227-
info: ktls::tls_crypto_info {
230+
Self::Chacha20Poly1305(libc::tls12_crypto_info_chacha20_poly1305 {
231+
info: libc::tls_crypto_info {
228232
version,
229-
cipher_type: ktls::TLS_CIPHER_CHACHA20_POLY1305 as _,
233+
cipher_type: libc::TLS_CIPHER_CHACHA20_POLY1305,
230234
},
231-
iv: iv
232-
.as_ref()
233-
.try_into()
234-
.expect("Chacha20-Poly1305 iv is 12 bytes"),
235+
iv: iv.as_ref().try_into().unwrap(),
235236
key: key
236237
.as_ref()
237238
.try_into()
238-
.expect("Chacha20-Poly1305 key is 32 bytes"),
239-
salt: ktls::__IncompleteArrayField::new(),
239+
.map_err(|_| InvalidCryptoInfo::WrongSizeKey)?,
240+
salt: [],
240241
rec_seq: seq.to_be_bytes(),
241242
})
242243
}
243244
_ => {
244-
return Err(KtlsCompatibilityError::UnsupportedCipherSuite(cipher_suite));
245+
return Err(InvalidCryptoInfo::UnsupportedCipherSuite(cipher_suite));
245246
}
246247
})
247248
}
248249
}
249250

250-
pub fn setup_tls_info(fd: RawFd, dir: Direction, info: CryptoInfo) -> Result<(), crate::Error> {
251-
let ret = unsafe { libc::setsockopt(fd, SOL_TLS, dir.into(), info.as_ptr(), info.size() as _) };
252-
if ret < 0 {
253-
return Err(crate::Error::TlsCryptoInfoError(
254-
std::io::Error::last_os_error(),
255-
));
256-
}
257-
Ok(())
258-
}
259-
260251
const TLS_SET_RECORD_TYPE: libc::c_int = 1;
261252
const ALERT: u8 = 0x15;
262253

0 commit comments

Comments
 (0)