mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
fix: harden tunnel security and timeout handling
This commit is contained in:
@@ -185,6 +185,12 @@ pub struct RequestMeta {
|
||||
pub method: String,
|
||||
pub url: String,
|
||||
pub headers: std::collections::HashMap<String, String>,
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub stream: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub request_timeout_ms: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub stream_first_byte_timeout_ms: Option<u64>,
|
||||
#[serde(default = "default_timeout", deserialize_with = "deserialize_timeout")]
|
||||
pub timeout: u64,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -39,6 +39,8 @@ pub enum TunnelSecurityError {
|
||||
MissingEncryptedFlag,
|
||||
#[error("secure tunnel frame payload is too short")]
|
||||
PayloadTooShort,
|
||||
#[error("secure tunnel frame sequence is not the expected next value")]
|
||||
UnexpectedSequence,
|
||||
#[error("secure tunnel frame encryption failed")]
|
||||
Encrypt,
|
||||
#[error("secure tunnel frame decryption failed")]
|
||||
@@ -51,6 +53,7 @@ pub struct SecureFrameCodec {
|
||||
seal_prefix: [u8; 4],
|
||||
open_prefix: [u8; 4],
|
||||
next_sequence: AtomicU64,
|
||||
next_open_sequence: AtomicU64,
|
||||
}
|
||||
|
||||
impl SecureFrameCodec {
|
||||
@@ -90,6 +93,7 @@ impl SecureFrameCodec {
|
||||
seal_prefix,
|
||||
open_prefix,
|
||||
next_sequence: AtomicU64::new(0),
|
||||
next_open_sequence: AtomicU64::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,6 +136,10 @@ impl SecureFrameCodec {
|
||||
|
||||
let mut payload = frame.payload.clone();
|
||||
let sequence = payload.get_u64();
|
||||
let expected_sequence = self.next_open_sequence.load(Ordering::Relaxed);
|
||||
if sequence != expected_sequence {
|
||||
return Err(TunnelSecurityError::UnexpectedSequence);
|
||||
}
|
||||
let nonce_bytes = nonce_bytes(self.open_prefix, sequence);
|
||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||
let clear_flags = frame.flags & !FLAG_ENCRYPTED;
|
||||
@@ -146,6 +154,8 @@ impl SecureFrameCodec {
|
||||
},
|
||||
)
|
||||
.map_err(|_| TunnelSecurityError::Decrypt)?;
|
||||
self.next_open_sequence
|
||||
.store(expected_sequence.wrapping_add(1), Ordering::Relaxed);
|
||||
|
||||
Ok(Frame::new(
|
||||
frame.stream_id,
|
||||
@@ -243,6 +253,68 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_frame_rejects_replayed_sequence() {
|
||||
let client = SecureFrameCodec::new(&test_key(), "session-1", TunnelSecurityRole::Client)
|
||||
.expect("client codec");
|
||||
let server = SecureFrameCodec::new(&test_key(), "session-1", TunnelSecurityRole::Server)
|
||||
.expect("server codec");
|
||||
let encrypted = client
|
||||
.encrypt_frame(Frame::new(
|
||||
1,
|
||||
MsgType::RequestBody,
|
||||
0,
|
||||
Bytes::from_static(b"secret"),
|
||||
))
|
||||
.expect("encrypt");
|
||||
let wire = Frame::decode(encrypted).expect("wire frame");
|
||||
|
||||
server.decrypt_frame(wire.clone()).expect("first decrypt");
|
||||
assert!(matches!(
|
||||
server.decrypt_frame(wire),
|
||||
Err(TunnelSecurityError::UnexpectedSequence)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_frame_rejects_out_of_order_sequence_without_advancing() {
|
||||
let client = SecureFrameCodec::new(&test_key(), "session-1", TunnelSecurityRole::Client)
|
||||
.expect("client codec");
|
||||
let server = SecureFrameCodec::new(&test_key(), "session-1", TunnelSecurityRole::Server)
|
||||
.expect("server codec");
|
||||
let first = Frame::decode(
|
||||
client
|
||||
.encrypt_frame(Frame::new(
|
||||
1,
|
||||
MsgType::RequestBody,
|
||||
0,
|
||||
Bytes::from_static(b"first"),
|
||||
))
|
||||
.expect("encrypt first"),
|
||||
)
|
||||
.expect("first wire frame");
|
||||
let second = Frame::decode(
|
||||
client
|
||||
.encrypt_frame(Frame::new(
|
||||
1,
|
||||
MsgType::RequestBody,
|
||||
0,
|
||||
Bytes::from_static(b"second"),
|
||||
))
|
||||
.expect("encrypt second"),
|
||||
)
|
||||
.expect("second wire frame");
|
||||
|
||||
assert!(matches!(
|
||||
server.decrypt_frame(second),
|
||||
Err(TunnelSecurityError::UnexpectedSequence)
|
||||
));
|
||||
assert_eq!(
|
||||
server.decrypt_frame(first).expect("first decrypt").payload,
|
||||
Bytes::from_static(b"first")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_frame_uses_session_in_key_derivation() {
|
||||
let session_a = "node-1:connection-a";
|
||||
|
||||
Reference in New Issue
Block a user