Verify signature if present
This commit is contained in:
Generated
+1
@@ -4781,6 +4781,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"bytes",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-sphinx",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
|
||||
@@ -12,6 +12,7 @@ license.workspace = true
|
||||
bincode = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
rand = "0.8.5"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod conversion;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod signature;
|
||||
|
||||
const VERSION: u8 = 7;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::{make_bincode_serializer, IpPair};
|
||||
|
||||
use super::VERSION;
|
||||
use super::{
|
||||
signature::{SignatureError, SignedRequest},
|
||||
VERSION,
|
||||
};
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
@@ -236,6 +240,25 @@ pub struct SignedStaticConnectRequest {
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl SignedRequest for SignedStaticConnectRequest {
|
||||
fn identity(&self) -> &identity::PublicKey {
|
||||
self.request.reply_to.identity()
|
||||
}
|
||||
|
||||
fn request(&self) -> Result<Vec<u8>, SignatureError> {
|
||||
self.request
|
||||
.to_bytes()
|
||||
.map_err(|error| SignatureError::RequestSerializationError {
|
||||
message: "failed to serialize request to binary".to_string(),
|
||||
error,
|
||||
})
|
||||
}
|
||||
|
||||
fn signature(&self) -> Option<&Vec<u8>> {
|
||||
self.signature.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// A dynamic connect request is when the client does not provide the internal IP address it will use
|
||||
// on the ip packet router, and instead requests one to be assigned to it.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -274,6 +297,25 @@ pub struct SignedDynamicConnectRequest {
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl SignedRequest for SignedDynamicConnectRequest {
|
||||
fn identity(&self) -> &identity::PublicKey {
|
||||
self.request.reply_to.identity()
|
||||
}
|
||||
|
||||
fn request(&self) -> Result<Vec<u8>, SignatureError> {
|
||||
self.request
|
||||
.to_bytes()
|
||||
.map_err(|error| SignatureError::RequestSerializationError {
|
||||
message: "failed to serialize request to binary".to_string(),
|
||||
error,
|
||||
})
|
||||
}
|
||||
|
||||
fn signature(&self) -> Option<&Vec<u8>> {
|
||||
self.signature.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// A disconnect request is when the client wants to disconnect from the ip packet router and free
|
||||
// up the allocated IP address.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -300,6 +342,25 @@ pub struct SignedDisconnectRequest {
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl SignedRequest for SignedDisconnectRequest {
|
||||
fn identity(&self) -> &identity::PublicKey {
|
||||
self.request.reply_to.identity()
|
||||
}
|
||||
|
||||
fn request(&self) -> Result<Vec<u8>, SignatureError> {
|
||||
self.request
|
||||
.to_bytes()
|
||||
.map_err(|error| SignatureError::RequestSerializationError {
|
||||
message: "failed to serialize request to binary".to_string(),
|
||||
error,
|
||||
})
|
||||
}
|
||||
|
||||
fn signature(&self) -> Option<&Vec<u8>> {
|
||||
self.signature.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// A data request is when the client wants to send an IP packet to a destination.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DataRequest {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
use nym_crypto::asymmetric::{ed25519::Ed25519RecoveryError, identity};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SignatureError {
|
||||
#[error("signature is missing")]
|
||||
MissingSignature,
|
||||
|
||||
#[error("failed to parse signature: {error}")]
|
||||
SignatureParseError {
|
||||
message: String,
|
||||
error: Ed25519RecoveryError,
|
||||
},
|
||||
|
||||
#[error("failed to serialize request to binary: {message}")]
|
||||
RequestSerializationError {
|
||||
message: String,
|
||||
error: Box<bincode::ErrorKind>,
|
||||
},
|
||||
|
||||
#[error("signature verification failed")]
|
||||
VerificationFailed {
|
||||
message: String,
|
||||
error: identity::SignatureError,
|
||||
},
|
||||
}
|
||||
|
||||
pub trait SignedRequest {
|
||||
fn identity(&self) -> &identity::PublicKey;
|
||||
|
||||
fn request(&self) -> Result<Vec<u8>, SignatureError>;
|
||||
|
||||
fn signature(&self) -> Option<&Vec<u8>>;
|
||||
|
||||
fn verify(&self) -> Result<(), SignatureError> {
|
||||
if let Some(signature) = self.signature() {
|
||||
let request_as_bytes = self.request()?;
|
||||
let signature = identity::Signature::from_bytes(signature).map_err(|error| {
|
||||
SignatureError::SignatureParseError {
|
||||
message: "failed to parse signature".to_string(),
|
||||
error,
|
||||
}
|
||||
})?;
|
||||
|
||||
self.identity()
|
||||
.verify(request_as_bytes, &signature)
|
||||
.map_err(|error| SignatureError::VerificationFailed {
|
||||
message: "signature verification failed".to_string(),
|
||||
error,
|
||||
})
|
||||
} else {
|
||||
Err(SignatureError::MissingSignature)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,11 @@ pub enum IpPacketRouterError {
|
||||
|
||||
#[error("received empty packet")]
|
||||
EmptyPacket,
|
||||
|
||||
#[error("failed to verify request: {source}")]
|
||||
FailedToVerifyRequest {
|
||||
source: nym_ip_packet_requests::v7::signature::SignatureError,
|
||||
},
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, IpPacketRouterError>;
|
||||
|
||||
@@ -4,16 +4,19 @@ use std::{collections::HashMap, net::SocketAddr};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::StreamExt;
|
||||
use nym_ip_packet_requests::v7::request::IpPacketRequestData;
|
||||
use nym_ip_packet_requests::v7::signature::SignatureError;
|
||||
use nym_ip_packet_requests::{
|
||||
codec::MultiIpPacketCodec,
|
||||
v6::response::{
|
||||
DynamicConnectFailureReason, InfoLevel, InfoResponseReply, IpPacketResponse,
|
||||
StaticConnectFailureReason,
|
||||
},
|
||||
v7::request::{
|
||||
DataRequest, DisconnectRequest, DynamicConnectRequest, IpPacketRequest,
|
||||
StaticConnectRequest,
|
||||
v7::{
|
||||
request::{
|
||||
DataRequest, DisconnectRequest, DynamicConnectRequest, IpPacketRequest,
|
||||
IpPacketRequestData, StaticConnectRequest,
|
||||
},
|
||||
signature::SignedRequest,
|
||||
},
|
||||
IpPair,
|
||||
};
|
||||
@@ -543,67 +546,59 @@ impl MixnetListener {
|
||||
reconstructed.sender_tag
|
||||
);
|
||||
|
||||
// Check version of request
|
||||
// let version = if let Some(version) = reconstructed.message.first() {
|
||||
// // The idea is that in the future we can add logic here to parse older versions to stay
|
||||
// // backwards compatible.
|
||||
// if *version != nym_ip_packet_requests::CURRENT_VERSION
|
||||
// && *version != nym_ip_packet_requests::CURRENT_VERSION + 1
|
||||
// {
|
||||
// log::info!("Received packet with invalid version: v{version}");
|
||||
// return Ok(vec![self.on_version_mismatch(*version, &reconstructed)]);
|
||||
// }
|
||||
// *version
|
||||
// };
|
||||
|
||||
// Make sure the current version is set to something that we can handle
|
||||
const _: () = assert!(
|
||||
nym_ip_packet_requests::CURRENT_VERSION == 6
|
||||
|| nym_ip_packet_requests::CURRENT_VERSION == 7
|
||||
);
|
||||
|
||||
let version = *reconstructed
|
||||
let request_version = *reconstructed
|
||||
.message
|
||||
.first()
|
||||
.ok_or(IpPacketRouterError::EmptyPacket)?;
|
||||
|
||||
// Check version of request
|
||||
let request = if version == 6 {
|
||||
nym_ip_packet_requests::v6::request::IpPacketRequest::from_reconstructed_message(
|
||||
// Check version of the request and convert to the latest version if necessary
|
||||
let request = match request_version {
|
||||
6 => nym_ip_packet_requests::v6::request::IpPacketRequest::from_reconstructed_message(
|
||||
&reconstructed,
|
||||
)
|
||||
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })?
|
||||
.into()
|
||||
} else if version == 7 {
|
||||
nym_ip_packet_requests::v7::request::IpPacketRequest::from_reconstructed_message(
|
||||
.into(),
|
||||
7 => nym_ip_packet_requests::v7::request::IpPacketRequest::from_reconstructed_message(
|
||||
&reconstructed,
|
||||
)
|
||||
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })?
|
||||
} else {
|
||||
log::info!("Received packet with invalid version: v{version}");
|
||||
return Ok(vec![self.on_version_mismatch(version, &reconstructed)]);
|
||||
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })?,
|
||||
_ => {
|
||||
log::info!("Received packet with invalid version: v{request_version}");
|
||||
return Ok(vec![
|
||||
self.on_version_mismatch(request_version, &reconstructed)
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
// Once we start to require clients to send v7 requests, we will enfore checking
|
||||
// signatures. Until then, we only check if they are present.
|
||||
|
||||
match request.data {
|
||||
IpPacketRequestData::StaticConnect(signed_connect_request) => {
|
||||
if let Some(_signature) = signed_connect_request.signature {
|
||||
// TODO: verify the signature
|
||||
if let Err(err) = signed_connect_request.verify() {
|
||||
if !matches!(err, SignatureError::MissingSignature) {
|
||||
return Err(IpPacketRouterError::FailedToVerifyRequest { source: err });
|
||||
}
|
||||
}
|
||||
let connect_request = signed_connect_request.request;
|
||||
Ok(vec![self.on_static_connect_request(connect_request).await])
|
||||
}
|
||||
IpPacketRequestData::DynamicConnect(signed_connect_request) => {
|
||||
if let Some(_signature) = signed_connect_request.signature {
|
||||
// TODO: verify the signature
|
||||
if let Err(err) = signed_connect_request.verify() {
|
||||
if !matches!(err, SignatureError::MissingSignature) {
|
||||
return Err(IpPacketRouterError::FailedToVerifyRequest { source: err });
|
||||
}
|
||||
}
|
||||
let connect_request = signed_connect_request.request;
|
||||
Ok(vec![self.on_dynamic_connect_request(connect_request).await])
|
||||
}
|
||||
IpPacketRequestData::Disconnect(disconnect_request) => {
|
||||
if let Some(_signature) = disconnect_request.signature {
|
||||
// TODO: verify the signature
|
||||
IpPacketRequestData::Disconnect(signed_disconnect_request) => {
|
||||
if let Err(err) = signed_disconnect_request.verify() {
|
||||
if !matches!(err, SignatureError::MissingSignature) {
|
||||
return Err(IpPacketRouterError::FailedToVerifyRequest { source: err });
|
||||
}
|
||||
}
|
||||
let disconnect_request = disconnect_request.request;
|
||||
let disconnect_request = signed_disconnect_request.request;
|
||||
Ok(vec![self.on_disconnect_request(disconnect_request)])
|
||||
}
|
||||
IpPacketRequestData::Data(data_request) => self.on_data_request(data_request).await,
|
||||
|
||||
Reference in New Issue
Block a user