Skip to content

Commit f42b283

Browse files
committed
Send no-aead-agreement response when aead negotiation fails.
This gives more feedback to clients as to what is wrong with their request.
1 parent d6168f4 commit f42b283

2 files changed

Lines changed: 199 additions & 11 deletions

File tree

nts-pool-ke/src/pool_ke.rs

Lines changed: 155 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use opentelemetry::{
1111
};
1212
use pool_nts::{
1313
AlgorithmDescription, BufferBorrowingReader, ClientRequest, ErrorCode, ErrorResponse,
14-
FixedKeyRequest, KeyExchangeResponse, MAX_MESSAGE_SIZE, NoAgreementResponse, NtsError,
15-
ProtocolId,
14+
FixedKeyRequest, KeyExchangeResponse, MAX_MESSAGE_SIZE, NoAeadAlgorithmAgreementResponse,
15+
NoProtocolAgreementResponse, NtsError, ProtocolId,
1616
};
1717
use rustls::{pki_types::pem::PemObject, version::TLS13};
1818
use tokio::{
@@ -82,7 +82,6 @@ impl std::fmt::Display for ConnectionError {
8282
enum SelectionOutcome {
8383
NoSharedProtocol,
8484
NoSharedAlgorithm {
85-
#[expect(unused)]
8685
protocol: ProtocolId,
8786
},
8887
Selection {
@@ -522,8 +521,19 @@ impl<S: ServerManager + 'static> NtsPoolKe<S> {
522521
protocol,
523522
algorithm,
524523
}) => (protocol, algorithm),
525-
Ok(SelectionOutcome::NoSharedAlgorithm { .. } | SelectionOutcome::NoSharedProtocol) => {
526-
NoAgreementResponse
524+
Ok(SelectionOutcome::NoSharedAlgorithm { protocol }) => {
525+
NoAeadAlgorithmAgreementResponse { protocol }
526+
.serialize(&mut client_stream)
527+
.await
528+
.map_err(ConnectionError::SendResponse)?;
529+
client_stream
530+
.shutdown()
531+
.await
532+
.map_err(ConnectionError::SendResponse)?;
533+
return Ok(());
534+
}
535+
Ok(SelectionOutcome::NoSharedProtocol) => {
536+
NoProtocolAgreementResponse
527537
.serialize(&mut client_stream)
528538
.await
529539
.map_err(ConnectionError::SendResponse)?;
@@ -1187,6 +1197,146 @@ mod tests {
11871197
pool_handle.abort();
11881198
}
11891199

1200+
#[tokio::test]
1201+
async fn test_keyexchange_protocol_negotiation_failure() {
1202+
crate::test_init();
1203+
let pool_listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1204+
let pool_addr = pool_listener.local_addr().unwrap();
1205+
1206+
let manager = TestManager::new(
1207+
"a.test".into(),
1208+
vec![
1209+
0x80, 1, 0, 2, 0, 0, 0x80, 4, 0, 2, 0, 0, 0, 5, 0, 2, 1, 2, 0, 5, 0, 2, 3, 4, 0x80,
1210+
0, 0, 0,
1211+
],
1212+
&[0],
1213+
&[AlgorithmDescription { id: 0, keysize: 16 }],
1214+
false,
1215+
);
1216+
let pool_manager = manager.clone();
1217+
1218+
let pool_handle = tokio::spawn(async move {
1219+
let pool_config = NtsPoolKeConfig {
1220+
certificate_chain: PathBuf::from(format!(
1221+
"{}/testdata/pool.test.fullchain.pem",
1222+
env!("CARGO_MANIFEST_DIR"),
1223+
)),
1224+
private_key: PathBuf::from(format!(
1225+
"{}/testdata/pool.test.key",
1226+
env!("CARGO_MANIFEST_DIR"),
1227+
)),
1228+
listen: pool_addr,
1229+
key_exchange_timeout: Duration::from_millis(1000),
1230+
timesource_timeout: Duration::from_millis(500),
1231+
max_connections: 1,
1232+
use_proxy_protocol: false,
1233+
monitoring_keys: None,
1234+
};
1235+
1236+
let pool = Arc::new(NtsPoolKe::new(pool_config, pool_manager).await.unwrap());
1237+
pool.serve_inner(pool_listener).await
1238+
});
1239+
1240+
let pool_connector = upstream_tls_config();
1241+
let conn = TcpStream::connect(pool_addr).await.unwrap();
1242+
let mut conn = pool_connector
1243+
.connect(ServerName::try_from("pool.test").unwrap(), conn)
1244+
.await
1245+
.unwrap();
1246+
1247+
conn.write_all(&[0x80, 1, 0, 2, 0, 2, 0x80, 4, 0, 2, 0, 0, 0x80, 0, 0, 0])
1248+
.await
1249+
.unwrap();
1250+
let mut buf = [0u8; MAX_MESSAGE_SIZE as _];
1251+
let response =
1252+
KeyExchangeResponse::parse(&mut BufferBorrowingReader::new(&mut conn, &mut buf))
1253+
.await
1254+
.unwrap_err();
1255+
conn.shutdown().await.unwrap();
1256+
1257+
assert_eq!(manager.inner.written.lock().unwrap().len(), 0);
1258+
assert!(matches!(response, NtsError::NoOverlappingProtocol));
1259+
assert_eq!(
1260+
manager
1261+
.inner
1262+
.reuse_count
1263+
.load(std::sync::atomic::Ordering::Relaxed),
1264+
0
1265+
);
1266+
1267+
pool_handle.abort();
1268+
}
1269+
1270+
#[tokio::test]
1271+
async fn test_keyexchange_algorithm_negotiation_failure() {
1272+
crate::test_init();
1273+
let pool_listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1274+
let pool_addr = pool_listener.local_addr().unwrap();
1275+
1276+
let manager = TestManager::new(
1277+
"a.test".into(),
1278+
vec![
1279+
0x80, 1, 0, 2, 0, 0, 0x80, 4, 0, 2, 0, 0, 0, 5, 0, 2, 1, 2, 0, 5, 0, 2, 3, 4, 0x80,
1280+
0, 0, 0,
1281+
],
1282+
&[0],
1283+
&[AlgorithmDescription { id: 0, keysize: 16 }],
1284+
false,
1285+
);
1286+
let pool_manager = manager.clone();
1287+
1288+
let pool_handle = tokio::spawn(async move {
1289+
let pool_config = NtsPoolKeConfig {
1290+
certificate_chain: PathBuf::from(format!(
1291+
"{}/testdata/pool.test.fullchain.pem",
1292+
env!("CARGO_MANIFEST_DIR"),
1293+
)),
1294+
private_key: PathBuf::from(format!(
1295+
"{}/testdata/pool.test.key",
1296+
env!("CARGO_MANIFEST_DIR"),
1297+
)),
1298+
listen: pool_addr,
1299+
key_exchange_timeout: Duration::from_millis(1000),
1300+
timesource_timeout: Duration::from_millis(500),
1301+
max_connections: 1,
1302+
use_proxy_protocol: false,
1303+
monitoring_keys: None,
1304+
};
1305+
1306+
let pool = Arc::new(NtsPoolKe::new(pool_config, pool_manager).await.unwrap());
1307+
pool.serve_inner(pool_listener).await
1308+
});
1309+
1310+
let pool_connector = upstream_tls_config();
1311+
let conn = TcpStream::connect(pool_addr).await.unwrap();
1312+
let mut conn = pool_connector
1313+
.connect(ServerName::try_from("pool.test").unwrap(), conn)
1314+
.await
1315+
.unwrap();
1316+
1317+
conn.write_all(&[0x80, 1, 0, 2, 0, 0, 0x80, 4, 0, 2, 0, 1, 0x80, 0, 0, 0])
1318+
.await
1319+
.unwrap();
1320+
let mut buf = [0u8; MAX_MESSAGE_SIZE as _];
1321+
let response =
1322+
KeyExchangeResponse::parse(&mut BufferBorrowingReader::new(&mut conn, &mut buf))
1323+
.await
1324+
.unwrap_err();
1325+
conn.shutdown().await.unwrap();
1326+
1327+
assert_eq!(manager.inner.written.lock().unwrap().len(), 0);
1328+
assert!(matches!(response, NtsError::NoOverlappingAeadAlgorithm));
1329+
assert_eq!(
1330+
manager
1331+
.inner
1332+
.reuse_count
1333+
.load(std::sync::atomic::Ordering::Relaxed),
1334+
0
1335+
);
1336+
1337+
pool_handle.abort();
1338+
}
1339+
11901340
#[tokio::test]
11911341
async fn test_keyexchange_keepalive() {
11921342
crate::test_init();

pool-nts/src/lib.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,9 @@ impl<'a> KeyExchangeResponse<'a> {
732732
}
733733

734734
#[derive(Debug)]
735-
pub struct NoAgreementResponse;
735+
pub struct NoProtocolAgreementResponse;
736736

737-
impl NoAgreementResponse {
737+
impl NoProtocolAgreementResponse {
738738
pub async fn serialize(self, mut writer: impl AsyncWrite + Unpin + Send) -> Result<(), Error> {
739739
NtsRecord::NextProtocol {
740740
protocol_ids: [].as_slice().into(),
@@ -747,6 +747,27 @@ impl NoAgreementResponse {
747747
}
748748
}
749749

750+
pub struct NoAeadAlgorithmAgreementResponse {
751+
pub protocol: ProtocolId,
752+
}
753+
754+
impl NoAeadAlgorithmAgreementResponse {
755+
pub async fn serialize(self, mut writer: impl AsyncWrite + Unpin + Send) -> Result<(), Error> {
756+
NtsRecord::NextProtocol {
757+
protocol_ids: [self.protocol].as_slice().into(),
758+
}
759+
.serialize(&mut writer)
760+
.await?;
761+
NtsRecord::AeadAlgorithm {
762+
algorithm_ids: [].as_slice().into(),
763+
}
764+
.serialize(&mut writer)
765+
.await?;
766+
NtsRecord::EndOfMessage.serialize(&mut writer).await?;
767+
Ok(())
768+
}
769+
}
770+
750771
#[derive(Debug)]
751772
pub struct ErrorResponse {
752773
pub errorcode: ErrorCode,
@@ -775,11 +796,14 @@ mod tests {
775796
task::{Context, Poll, Waker},
776797
};
777798

778-
use crate::record::{AlgorithmList, ProtocolList};
799+
use crate::{
800+
NoAeadAlgorithmAgreementResponse,
801+
record::{AlgorithmList, ProtocolList},
802+
};
779803

780804
use super::{
781805
AlgorithmDescription, ClientRequest, ErrorCode, ErrorResponse, FixedKeyRequest,
782-
KeyExchangeResponse, NoAgreementResponse, NtsError, ServerInformationRequest,
806+
KeyExchangeResponse, NoProtocolAgreementResponse, NtsError, ServerInformationRequest,
783807
ServerInformationResponse,
784808
};
785809

@@ -1842,12 +1866,26 @@ mod tests {
18421866
let mut buf = vec![];
18431867
assert!(
18441868
swrap(
1845-
NoAgreementResponse::serialize,
1846-
NoAgreementResponse,
1869+
NoProtocolAgreementResponse::serialize,
1870+
NoProtocolAgreementResponse,
18471871
&mut buf
18481872
)
18491873
.is_ok()
18501874
);
18511875
assert_eq!(buf, [0x80, 1, 0, 0, 0x80, 0, 0, 0]);
18521876
}
1877+
1878+
#[test]
1879+
fn test_no_algorithm_agreement_response() {
1880+
let mut buf = vec![];
1881+
assert!(
1882+
swrap(
1883+
NoAeadAlgorithmAgreementResponse::serialize,
1884+
NoAeadAlgorithmAgreementResponse { protocol: 1 },
1885+
&mut buf
1886+
)
1887+
.is_ok()
1888+
);
1889+
assert_eq!(buf, [0x80, 1, 0, 2, 0, 1, 0x80, 4, 0, 0, 0x80, 0, 0, 0]);
1890+
}
18531891
}

0 commit comments

Comments
 (0)