From a374bca601f9bcf26f25c0b982e17a26eb7bd806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 28 May 2024 15:10:45 +0200 Subject: [PATCH] Check timestamp --- Cargo.lock | 1 + common/ip-packet-requests/src/v7/request.rs | 12 +++++++++ common/ip-packet-requests/src/v7/response.rs | 2 ++ common/ip-packet-requests/src/v7/signature.rs | 12 +++++++++ service-providers/ip-packet-router/Cargo.toml | 3 ++- .../ip-packet-router/src/mixnet_listener.rs | 27 +++++++++---------- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21059ac3ef..90fbf05acf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4826,6 +4826,7 @@ dependencies = [ "serde_json", "tap", "thiserror", + "time", "tokio", "tokio-tun", "tokio-util", diff --git a/common/ip-packet-requests/src/v7/request.rs b/common/ip-packet-requests/src/v7/request.rs index 9f76f77ec0..b250acf7d9 100644 --- a/common/ip-packet-requests/src/v7/request.rs +++ b/common/ip-packet-requests/src/v7/request.rs @@ -257,6 +257,10 @@ impl SignedRequest for SignedStaticConnectRequest { fn signature(&self) -> Option<&Vec> { self.signature.as_ref() } + + fn timestamp(&self) -> OffsetDateTime { + self.request.timestamp + } } // A dynamic connect request is when the client does not provide the internal IP address it will use @@ -314,6 +318,10 @@ impl SignedRequest for SignedDynamicConnectRequest { fn signature(&self) -> Option<&Vec> { self.signature.as_ref() } + + fn timestamp(&self) -> OffsetDateTime { + self.request.timestamp + } } // A disconnect request is when the client wants to disconnect from the ip packet router and free @@ -359,6 +367,10 @@ impl SignedRequest for SignedDisconnectRequest { fn signature(&self) -> Option<&Vec> { self.signature.as_ref() } + + fn timestamp(&self) -> OffsetDateTime { + self.request.timestamp + } } // A data request is when the client wants to send an IP packet to a destination. diff --git a/common/ip-packet-requests/src/v7/response.rs b/common/ip-packet-requests/src/v7/response.rs index 54389ff98d..d0eb42bbed 100644 --- a/common/ip-packet-requests/src/v7/response.rs +++ b/common/ip-packet-requests/src/v7/response.rs @@ -277,6 +277,8 @@ pub enum StaticConnectFailureReason { RequestedIpAlreadyInUse, #[error("requested nym-address is already in use")] RequestedNymAddressAlreadyInUse, + #[error("request timestamp is out of date")] + OutOfDateTimestamp, #[error("{0}")] Other(String), } diff --git a/common/ip-packet-requests/src/v7/signature.rs b/common/ip-packet-requests/src/v7/signature.rs index b22ec242f3..f2d8f06eb9 100644 --- a/common/ip-packet-requests/src/v7/signature.rs +++ b/common/ip-packet-requests/src/v7/signature.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use nym_crypto::asymmetric::{ed25519::Ed25519RecoveryError, identity}; #[derive(thiserror::Error, Debug)] @@ -17,6 +19,9 @@ pub enum SignatureError { error: Box, }, + #[error("signature verification failed: request out of date")] + RequestOutOfDate, + #[error("signature verification failed")] VerificationFailed { message: String, @@ -31,8 +36,15 @@ pub trait SignedRequest { fn signature(&self) -> Option<&Vec>; + fn timestamp(&self) -> time::OffsetDateTime; + fn verify(&self) -> Result<(), SignatureError> { if let Some(signature) = self.signature() { + // First check that the request is recent enough + if time::OffsetDateTime::now_utc() - self.timestamp() > Duration::from_secs(10) { + return Err(SignatureError::RequestOutOfDate); + } + let request_as_bytes = self.request()?; let signature = identity::Signature::from_bytes(signature).map_err(|error| { SignatureError::SignatureParseError { diff --git a/service-providers/ip-packet-router/Cargo.toml b/service-providers/ip-packet-router/Cargo.toml index a2e84916e2..5852901b3e 100644 --- a/service-providers/ip-packet-router/Cargo.toml +++ b/service-providers/ip-packet-router/Cargo.toml @@ -22,6 +22,7 @@ nym-client-core = { path = "../../common/client-core" } nym-config = { path = "../../common/config" } nym-crypto = { path = "../../common/crypto" } nym-exit-policy = { path = "../../common/exit-policy" } +nym-id = { path = "../../common/nym-id" } nym-ip-packet-requests = { path = "../../common/ip-packet-requests" } nym-network-defaults = { path = "../../common/network-defaults" } nym-network-requester = { path = "../network-requester" } @@ -33,13 +34,13 @@ nym-tun = { path = "../../common/tun" } nym-types = { path = "../../common/types" } nym-wireguard = { path = "../../common/wireguard" } nym-wireguard-types = { path = "../../common/wireguard-types" } -nym-id = { path = "../../common/nym-id" } rand = "0.8.5" reqwest.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tap.workspace = true thiserror = { workspace = true } +time = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] } tokio-util = { workspace = true, features = ["codec"] } url.workspace = true diff --git a/service-providers/ip-packet-router/src/mixnet_listener.rs b/service-providers/ip-packet-router/src/mixnet_listener.rs index d087eff38f..eaf2209d2d 100644 --- a/service-providers/ip-packet-router/src/mixnet_listener.rs +++ b/service-providers/ip-packet-router/src/mixnet_listener.rs @@ -575,29 +575,17 @@ impl MixnetListener { match request.data { IpPacketRequestData::StaticConnect(signed_connect_request) => { - if let Err(err) = signed_connect_request.verify() { - if !matches!(err, SignatureError::MissingSignature) { - return Err(IpPacketRouterError::FailedToVerifyRequest { source: err }); - } - } + verify_signed_request(&signed_connect_request)?; let connect_request = signed_connect_request.request; Ok(vec![self.on_static_connect_request(connect_request).await]) } IpPacketRequestData::DynamicConnect(signed_connect_request) => { - if let Err(err) = signed_connect_request.verify() { - if !matches!(err, SignatureError::MissingSignature) { - return Err(IpPacketRouterError::FailedToVerifyRequest { source: err }); - } - } + verify_signed_request(&signed_connect_request)?; let connect_request = signed_connect_request.request; Ok(vec![self.on_dynamic_connect_request(connect_request).await]) } IpPacketRequestData::Disconnect(signed_disconnect_request) => { - if let Err(err) = signed_disconnect_request.verify() { - if !matches!(err, SignatureError::MissingSignature) { - return Err(IpPacketRouterError::FailedToVerifyRequest { source: err }); - } - } + verify_signed_request(&signed_disconnect_request)?; let disconnect_request = signed_disconnect_request.request; Ok(vec![self.on_disconnect_request(disconnect_request)]) } @@ -715,6 +703,15 @@ impl MixnetListener { } } +fn verify_signed_request(request: &impl SignedRequest) -> Result<()> { + if let Err(err) = request.verify() { + if !matches!(err, SignatureError::MissingSignature) { + return Err(IpPacketRouterError::FailedToVerifyRequest { source: err }); + } + } + Ok(()) +} + pub(crate) enum ConnectedClientEvent { Disconnect(DisconnectEvent), Connect(Box),