#[cfg(not(feature = "use-mock-crust"))]
use crust::PeerId;
#[cfg(feature = "use-mock-crust")]
use mock_crust::crust::PeerId;
use maidsafe_utilities::serialisation::serialise;
use rustc_serialize::{Decoder, Encoder};
use sodiumoxide::crypto::{box_, sign};
use std::fmt::{self, Debug, Formatter};
use authority::Authority;
use data::{Data, DataRequest};
use error::RoutingError;
use id::{FullId, PublicId};
use types::MessageId;
use utils;
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum Message {
Direct(DirectMessage),
Hop(HopMessage),
TunnelDirect {
content: DirectMessage,
src: PeerId,
dst: PeerId,
},
TunnelHop {
content: HopMessage,
src: PeerId,
dst: PeerId,
},
}
#[derive(RustcEncodable, RustcDecodable)]
pub enum DirectMessage {
BootstrapIdentify {
public_id: ::id::PublicId,
current_quorum_size: usize,
},
BootstrapDeny,
ClientIdentify {
serialised_public_id: Vec<u8>,
signature: sign::Signature,
client_restriction: bool,
},
NodeIdentify {
serialised_public_id: Vec<u8>,
signature: sign::Signature,
},
ClientToNode,
Heartbeat,
NewNode(PublicId),
TunnelRequest(PeerId),
TunnelSuccess(PeerId),
TunnelClosed(PeerId),
TunnelDisconnect(PeerId),
}
#[derive(RustcEncodable, RustcDecodable)]
pub struct HopMessage {
content: SignedMessage,
signature: sign::Signature,
}
impl HopMessage {
pub fn new(content: SignedMessage,
sign_key: &sign::SecretKey)
-> Result<HopMessage, RoutingError> {
let bytes_to_sign = try!(serialise(&content));
Ok(HopMessage {
content: content,
signature: sign::sign_detached(&bytes_to_sign, sign_key),
})
}
pub fn verify(&self, verification_key: &sign::PublicKey) -> Result<(), RoutingError> {
let signed_bytes = try!(serialise(&self.content));
if sign::verify_detached(&self.signature, &signed_bytes, verification_key) {
Ok(())
} else {
Err(RoutingError::FailedSignature)
}
}
pub fn content(&self) -> &SignedMessage {
&self.content
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct SignedMessage {
content: RoutingMessage,
public_id: PublicId,
signature: sign::Signature,
}
impl SignedMessage {
pub fn new(content: RoutingMessage, full_id: &FullId) -> Result<SignedMessage, RoutingError> {
let bytes_to_sign = try!(serialise(&(&content, full_id.public_id())));
Ok(SignedMessage {
content: content,
public_id: *full_id.public_id(),
signature: sign::sign_detached(&bytes_to_sign, full_id.signing_private_key()),
})
}
pub fn check_integrity(&self) -> Result<(), RoutingError> {
let signed_bytes = try!(serialise(&(&self.content, &self.public_id)));
if sign::verify_detached(&self.signature,
&signed_bytes,
self.public_id().signing_public_key()) {
Ok(())
} else {
Err(RoutingError::FailedSignature)
}
}
pub fn content(&self) -> &RoutingMessage {
&self.content
}
pub fn public_id(&self) -> &PublicId {
&self.public_id
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum RoutingMessage {
Request(RequestMessage),
Response(ResponseMessage),
}
impl RoutingMessage {
pub fn src(&self) -> &Authority {
match *self {
RoutingMessage::Request(ref msg) => &msg.src,
RoutingMessage::Response(ref msg) => &msg.src,
}
}
pub fn dst(&self) -> &Authority {
match *self {
RoutingMessage::Request(ref msg) => &msg.dst,
RoutingMessage::Response(ref msg) => &msg.dst,
}
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct RequestMessage {
pub src: Authority,
pub dst: Authority,
pub content: RequestContent,
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct ResponseMessage {
pub src: Authority,
pub dst: Authority,
pub content: ResponseContent,
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, RustcEncodable, RustcDecodable)]
pub enum RequestContent {
GetNetworkName {
current_id: PublicId,
message_id: MessageId,
},
ExpectCloseNode {
expect_id: PublicId,
client_auth: Authority,
message_id: MessageId,
},
GetCloseGroup(MessageId),
Connect,
ConnectionInfo {
encrypted_connection_info: Vec<u8>,
nonce_bytes: [u8; box_::NONCEBYTES],
},
GetPublicId,
GetPublicIdWithConnectionInfo {
encrypted_connection_info: Vec<u8>,
nonce_bytes: [u8; box_::NONCEBYTES],
},
Refresh(Vec<u8>, MessageId),
Get(DataRequest, MessageId),
Put(Data, MessageId),
Post(Data, MessageId),
Delete(Data, MessageId),
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash, RustcEncodable, RustcDecodable)]
pub enum ResponseContent {
GetNetworkName {
relocated_id: PublicId,
close_group_ids: Vec<PublicId>,
message_id: MessageId,
},
GetPublicId {
public_id: PublicId,
},
GetPublicIdWithConnectionInfo {
public_id: PublicId,
encrypted_connection_info: Vec<u8>,
nonce_bytes: [u8; box_::NONCEBYTES],
},
GetCloseGroup {
close_group_ids: Vec<PublicId>,
message_id: MessageId,
},
GetSuccess(Data, MessageId),
PutSuccess(MessageId),
PostSuccess(MessageId),
DeleteSuccess(MessageId),
GetFailure {
id: MessageId,
request: RequestMessage,
external_error_indicator: Vec<u8>,
},
PutFailure {
id: MessageId,
request: RequestMessage,
external_error_indicator: Vec<u8>,
},
PostFailure {
id: MessageId,
request: RequestMessage,
external_error_indicator: Vec<u8>,
},
DeleteFailure {
id: MessageId,
request: RequestMessage,
external_error_indicator: Vec<u8>,
},
}
impl Debug for DirectMessage {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
DirectMessage::BootstrapIdentify { ref public_id, ref current_quorum_size } => {
write!(formatter,
"BootstrapIdentify {{ {:?}, {:?} }}",
public_id,
current_quorum_size)
}
DirectMessage::BootstrapDeny => write!(formatter, "BootstrapDeny"),
DirectMessage::ClientToNode => write!(formatter, "ClientToNode"),
DirectMessage::ClientIdentify { client_restriction: true, .. } => {
write!(formatter, "ClientIdentify (client only)")
}
DirectMessage::ClientIdentify { client_restriction: false, .. } => {
write!(formatter, "ClientIdentify (joining node)")
}
DirectMessage::NodeIdentify { .. } => write!(formatter, "NodeIdentify {{ .. }}"),
DirectMessage::NewNode(ref public_id) => write!(formatter, "NewNode({:?})", public_id),
DirectMessage::Heartbeat => write!(formatter, "Heartbeat"),
DirectMessage::TunnelRequest(peer_id) => {
write!(formatter, "TunnelRequest({:?})", peer_id)
}
DirectMessage::TunnelSuccess(peer_id) => {
write!(formatter, "TunnelSuccess({:?})", peer_id)
}
DirectMessage::TunnelClosed(peer_id) => {
write!(formatter, "TunnelClosed({:?})", peer_id)
}
DirectMessage::TunnelDisconnect(peer_id) => {
write!(formatter, "TunnelDisconnect({:?})", peer_id)
}
}
}
}
impl Debug for HopMessage {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter,
"HopMessage {{ content: {:?}, signature: .. }}",
self.content)
}
}
impl Debug for SignedMessage {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter,
"SignedMessage {{ content: {:?}, public_id: {:?}, signature: .. }}",
self.content,
self.public_id)
}
}
impl Debug for RequestContent {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
RequestContent::GetNetworkName { ref current_id, ref message_id } => {
write!(formatter,
"GetNetworkName {{ {:?}, {:?} }}",
current_id,
message_id)
}
RequestContent::ExpectCloseNode { ref expect_id, ref client_auth, ref message_id } => {
write!(formatter,
"ExpectCloseNode {{ {:?}, {:?}, {:?} }}",
expect_id,
client_auth,
message_id)
}
RequestContent::GetCloseGroup(id) => write!(formatter, "GetCloseGroup({:?})", id),
RequestContent::Connect => write!(formatter, "Connect"),
RequestContent::ConnectionInfo { .. } => write!(formatter, "ConnectionInfo {{ .. }}"),
RequestContent::GetPublicId => write!(formatter, "GetPublicId"),
RequestContent::GetPublicIdWithConnectionInfo { .. } => {
write!(formatter, "GetPublicIdWithConnectionInfo {{ .. }}")
}
RequestContent::Refresh(ref data, ref message_id) => {
write!(formatter,
"Refresh({}, {:?})",
utils::format_binary_array(data),
message_id)
}
RequestContent::Get(ref data_request, ref message_id) => {
write!(formatter, "Get({:?}, {:?})", data_request, message_id)
}
RequestContent::Put(ref data, ref message_id) => {
write!(formatter, "Put({:?}, {:?})", data, message_id)
}
RequestContent::Post(ref data, ref message_id) => {
write!(formatter, "Post({:?}, {:?})", data, message_id)
}
RequestContent::Delete(ref data, ref message_id) => {
write!(formatter, "Delete({:?}, {:?})", data, message_id)
}
}
}
}
impl Debug for ResponseContent {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
ResponseContent::GetNetworkName { ref relocated_id, ref close_group_ids, ref message_id } => {
write!(formatter,
"GetNetworkName {{ {:?}, {:?}, {:?} }}",
close_group_ids,
relocated_id,
message_id)
}
ResponseContent::GetPublicId { ref public_id } => {
write!(formatter, "GetPublicId {{ {:?} }}", public_id)
}
ResponseContent::GetPublicIdWithConnectionInfo { ref public_id, .. } => {
write!(formatter,
"GetPublicIdWithConnectionInfo {{ {:?}, .. }}",
public_id)
}
ResponseContent::GetCloseGroup { ref close_group_ids, message_id } => {
write!(formatter,
"GetCloseGroup {{ {:?}, {:?} }}",
close_group_ids,
message_id)
}
ResponseContent::GetSuccess(ref data, ref message_id) => {
write!(formatter, "GetSuccess {{ {:?}, {:?} }}", data, message_id)
}
ResponseContent::PutSuccess(ref message_id) => {
write!(formatter, "PutSuccess {{ {:?} }}", message_id)
}
ResponseContent::PostSuccess(ref message_id) => {
write!(formatter, "PostSuccess {{ {:?} }}", message_id)
}
ResponseContent::DeleteSuccess(ref message_id) => {
write!(formatter, "DeleteSuccess {{ {:?} }}", message_id)
}
ResponseContent::GetFailure { ref id, ref request, .. } => {
write!(formatter, "GetFailure {{ {:?}, {:?}, .. }}", id, request)
}
ResponseContent::PutFailure { ref id, ref request, .. } => {
write!(formatter, "PutFailure {{ {:?}, {:?}, .. }}", id, request)
}
ResponseContent::PostFailure { ref id, ref request, .. } => {
write!(formatter, "PostFailure {{ {:?}, {:?}, .. }}", id, request)
}
ResponseContent::DeleteFailure { ref id, ref request, .. } => {
write!(formatter, "DeleteFailure {{ {:?}, {:?}, .. }}", id, request)
}
}
}
}
#[cfg(test)]
mod test {
extern crate rand;
use super::{HopMessage, SignedMessage, RoutingMessage, RequestMessage, RequestContent};
use id::FullId;
use authority::Authority;
use xor_name::XorName;
use sodiumoxide::crypto::sign;
use maidsafe_utilities::serialisation::serialise;
#[test]
fn signed_message_check_integrity() {
let name: XorName = rand::random();
let routing_message = RoutingMessage::Request(RequestMessage {
src: Authority::ClientManager(name),
dst: Authority::ClientManager(name),
content: RequestContent::Connect,
});
let full_id = FullId::new();
let signed_message_result = SignedMessage::new(routing_message.clone(), &full_id);
assert!(signed_message_result.is_ok());
let mut signed_message = unwrap_result!(signed_message_result);
assert_eq!(routing_message, *signed_message.content());
assert_eq!(full_id.public_id(), signed_message.public_id());
let check_integrity_result = signed_message.check_integrity();
assert!(check_integrity_result.is_ok());
let full_id = FullId::new();
let bytes_to_sign = unwrap_result!(serialise(&(&routing_message, full_id.public_id())));
let signature = sign::sign_detached(&bytes_to_sign, full_id.signing_private_key());
signed_message.signature = signature;
let check_integrity_result = signed_message.check_integrity();
assert!(check_integrity_result.is_err());
}
#[test]
fn hop_message_verify() {
let name: XorName = rand::random();
let routing_message = RoutingMessage::Request(RequestMessage {
src: Authority::ClientManager(name),
dst: Authority::ClientManager(name),
content: RequestContent::Connect,
});
let full_id = FullId::new();
let signed_message_result = SignedMessage::new(routing_message.clone(), &full_id);
assert!(signed_message_result.is_ok());
let signed_message = unwrap_result!(signed_message_result);
let (public_signing_key, secret_signing_key) = sign::gen_keypair();
let hop_message_result = HopMessage::new(signed_message.clone(), &secret_signing_key);
assert!(hop_message_result.is_ok());
let hop_message = unwrap_result!(hop_message_result);
assert_eq!(signed_message, *hop_message.content());
let verify_result = hop_message.verify(&public_signing_key);
assert!(verify_result.is_ok());
let (public_signing_key, _) = sign::gen_keypair();
let verify_result = hop_message.verify(&public_signing_key);
assert!(verify_result.is_err());
}
}