Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Networking #42

Merged
merged 11 commits into from
Jun 13, 2019
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ serde = { version = "1.0", features = ["derive"] }
bincode = "1.1.3"
rust_sodium = "0.10.2"
openssl = { version = "0.10", features = ["vendored"] }
lazy_static = "1.2.0"
log = "0.4"
mio = "0.6.19"
rayon = "1.0.3"

[dev-dependencies]
criterion = "0.2.11"
simple_logger = "1.3.0"

[[bench]]
name = "bench_crypto"
Expand Down
7 changes: 4 additions & 3 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ SUCCESS="${GREEN}ok${RC}"

# Check if rustfmt is installed
printf "${PREFIX} Checking for rustfmt ... "
command -v cargo fmt &>/dev/null
if [[ $? == 0 ]]; then
OUT=$(cargo +stable fmt --version 2>&1 > /dev/null ) # Save the output of the command to OUT for later printing
if [[ $? -eq 0 ]]; then
printf "${SUCCESS}\n"
else
printf "${FAILURE}\n"
printf "${OUT}\n"
exit 1
fi

# Check rustfmt against the git tree
printf "${PREFIX} Checking formatting ... "
command cargo fmt -- --check > /dev/null
command cargo +stable fmt -- --check > /dev/null
if [[ $? == 0 ]]; then
printf "${SUCCESS}\n"
exit 0
Expand Down
208 changes: 208 additions & 0 deletions src/community/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
use crate::serialization::{Packet, PacketDeserializer};
use crate::serialization::header::Header;
use std::error::Error;
use std::collections::HashMap;
use crate::networking::address::Address;

pub mod peer;

create_error!(
HeaderUnwrapError,
"The community experienced an error trying to deserialize the header of a packet"
);
create_error!(MidError, "Failed to get the mid");
create_error!(
UnknownCommunityError,
"No community with matching mid found"
);
create_error!(InsertionError, "Error inserting community into hashmap");

/// Example Community
///
/// _**Note:** Try to avoid the use of .unwrap() in actual production code, this is just an example_
/// ```
/// use ipv8::community::peer::Peer;
/// use ipv8::community::Community;
/// use ipv8::serialization::header::Header;
/// use ipv8::serialization::{PacketDeserializer, Packet};
/// use std::net::Ipv4Addr;
/// use ipv8::networking::address::Address;
/// use std::error::Error;
/// use ipv8::IPv8;
/// use ipv8::configuration::Config;
/// use ipv8::serialization::header::HeaderVersion::PyIPV8Header;
/// use ipv8::crypto::keytypes::PublicKey;
///
/// pub struct TestCommunity{
/// peer: Peer
/// }
///
/// impl TestCommunity{
/// fn new() -> Option<Self> {
///
/// // Use the highest available key
/// let pk: PublicKey = PublicKey::from_vec(vec![
/// 48, 129, 167, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 39, 3,
/// 129, 146, 0, 4, 2, 86, 251, 75, 206, 159, 133, 120, 63, 176, 235, 178, 14, 8, 197, 59,
/// 107, 51, 179, 139, 3, 155, 20, 194, 112, 113, 15, 40, 67, 115, 37, 223, 152, 7, 102,
/// 154, 214, 90, 110, 180, 226, 5, 190, 99, 163, 54, 116, 173, 121, 40, 80, 129, 142, 82,
/// 118, 154, 96, 127, 164, 248, 217, 91, 13, 80, 91, 94, 210, 16, 110, 108, 41, 57, 4,
/// 243, 49, 52, 194, 254, 130, 98, 229, 50, 84, 21, 206, 134, 223, 157, 189, 133, 50, 210,
/// 181, 93, 229, 32, 179, 228, 179, 132, 143, 147, 96, 207, 68, 48, 184, 160, 47, 227, 70,
/// 147, 23, 159, 213, 105, 134, 60, 211, 226, 8, 235, 186, 20, 241, 85, 170, 4, 3, 40,
/// 183, 98, 103, 80, 164, 128, 87, 205, 101, 67, 254, 83, 142, 133,
/// ])?;
///
/// // Actually create the community
/// Some(TestCommunity {
/// peer: Peer::new(
/// pk,
/// Address{
/// address: Ipv4Addr::new(0,0,0,0),
/// port: 0
/// },
/// true,
/// )
/// })
/// }
/// }
///
/// impl Community for TestCommunity{
///
/// // Returns the hash of our master peer
/// fn get_mid(&self) -> Option<Vec<u8>> {
/// Some(self.peer.get_sha1()?.to_vec())
/// }
///
/// // The function which will be called when the community receives a packet
/// fn on_receive(&self, header: Header, deserializer: PacketDeserializer, address: Address) -> Result<(),Box<dyn Error>>{
/// # assert_eq!(header.mid_hash, self.get_mid());
/// # assert_eq!(header.version, PyIPV8Header);
/// # assert_eq!(header.message_type, Some(42));
/// // Do some stuff here like to distribute the message based on it's message_type (in the header)
/// Ok(())
/// }
/// }
///
/// let mut config = Config::default();
/// let community = TestCommunity::new().unwrap();
/// let mid = community.get_mid();
/// config.communities.add_community(Box::new(community));
/// let ipv8 = IPv8::new(config).unwrap();
///
/// // now simulate a packet coming in
///
/// // Create a packet to test the community with
/// let packet = Packet::new(Header{
/// size: 23,
/// version: PyIPV8Header,
/// mid_hash: mid,
/// message_type: Some(42),
/// }).unwrap();
///
/// // Normally you would want to sign the packet here
///
/// // Yeet the packet
/// ipv8.config.communities.forward_message(packet,Address{
/// address: Ipv4Addr::new(42,42,42,42),
/// port: 42,
/// });
///
/// ```
pub trait Community {
// fn new() -> Option<Self>
// where
// Self: Sized;

/// Returns the hash of our master peer public key
fn get_mid(&self) -> Option<Vec<u8>>;

/// Gets called whenever a packet is received directed at this community
/// DO NOT OVERRIDE
fn receive(
&self,
header: Header,
deserializer: PacketDeserializer,
address: Address,
) -> Result<(), Box<dyn Error>> {
// DO NOT OVERRIDE
//! used to pre-decode the header and filter out messages

fn warn_deprecated(message: &str, address: Address) -> Result<(), Box<dyn Error>> {
warn!(
"Received deprecated message {} from ({:?})",
message, address
);
Ok(())
}
match header.message_type.ok_or(HeaderUnwrapError)? {
255 => warn_deprecated("reserved-255", address),
254 => warn_deprecated("on-missing-sequence", address),
253 => warn_deprecated("missing-proof", address),
252 => warn_deprecated("signature-request", address),
251 => warn_deprecated("signature-response", address),
248 => warn_deprecated("on-identity", address),
247 => warn_deprecated("on-missing-identity", address),
244 => warn_deprecated("destroy-community", address),
243 => warn_deprecated("authorize", address),
242 => warn_deprecated("revoke", address),
241 => warn_deprecated("subjective-set", address),
240 => warn_deprecated("missing-subjective-set", address),
239 => warn_deprecated("on-missing-message", address),
238 => warn_deprecated("undo-own", address),
237 => warn_deprecated("undo-other", address),
236 => warn_deprecated("dynamic-settings", address),
235 => warn_deprecated("missing-last-message", address),
_ => self.on_receive(header, deserializer, address),
}
}

fn on_receive(
&self,
header: Header,
deserializer: PacketDeserializer,
address: Address,
) -> Result<(), Box<dyn Error>>;
}

pub struct CommunityRegistry {
// mid, community
communities: HashMap<Vec<u8>, Box<dyn Community>>,
}

impl CommunityRegistry {
pub fn add_community(&mut self, item: Box<dyn Community>) -> Result<(), Box<dyn Error>> {
self.communities
.insert(item.get_mid().ok_or(MidError)?, item)
.ok_or(InsertionError)?;
Ok(())
}

/// Forwards the message to the corresponding community
pub fn forward_message(&self, packet: Packet, address: Address) -> Result<(), Box<dyn Error>> {
// deserialize the header
let deserializer = packet.start_deserialize();

// We use peek here instead of get, even though we give the header along with the receive call
// this is because at this point, the header is not verified yet so we still assume the message is valid.
// We can't verify the header here yet as not all messages have a signature. Communities will have to decide
// on their own if they want to verify the header. We do give it along as only having to deserialize the header once
// makes it slightly more efficient.
let header = deserializer.peek_header()?;

// get the mid from the header and use it for a hashtable lookup
let mid = header.mid_hash.as_ref().ok_or(MidError)?;
let community = self.communities.get(mid).ok_or(UnknownCommunityError)?;

// Actually forward it
community.receive(header, deserializer, address)
}
}

impl Default for CommunityRegistry {
fn default() -> Self {
Self {
communities: HashMap::new(),
}
}
}
22 changes: 22 additions & 0 deletions src/community/peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::crypto::keytypes::PublicKey;
use crate::networking::address::Address;

pub struct Peer {
key: PublicKey,
address: Address,
intro: bool,
}

impl Peer {
pub fn new(key: PublicKey, address: Address, intro: bool) -> Self {
Self {
key,
address,
intro,
}
}

pub fn get_sha1(&self) -> Option<[u8; 20]> {
self.key.sha1()
}
}
24 changes: 24 additions & 0 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
use crate::networking::address::Address;
use std::net::Ipv4Addr;
use crate::community::CommunityRegistry;
use std::time::Duration;

pub struct Config {
/// the amount of space reserved for queueing up incoming messages (messages)
pub queuesize: usize,
/// the size of the buffer reserved for incoming messages (bytes)
pub buffersize: usize,
/// frequency at which polling times out and events are checked (ms)
/// None is as fast as possible
pub pollinterval: Option<Duration>,
/// the max number of threads to use in the network manager. 0 is #cores
pub threadcount: usize,

/// Default list of host used for peer discovery
pub default_hosts: Vec<Address>,
/// from py-ipv8 configuration. UDP socket address.
/// There split up in "address" and "port"
pub socketaddress: Address,

/// The registry containing all the communities
pub communities: CommunityRegistry,
}

impl Default for Config {
fn default() -> Self {
Config {
queuesize: 100,
buffersize: 2048,
pollinterval: None,

// zero means equal to number of cores
threadcount: 0,

socketaddress: Address {
address: Ipv4Addr::new(0, 0, 0, 0),
port: 8090,
},

communities: CommunityRegistry::default(),

default_hosts: vec![
// Dispersy
Address {
Expand Down
29 changes: 28 additions & 1 deletion src/crypto/keytypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
//! | HIGH_SIGNATURE_LENGTH | 144 | 153 | 9 | 144 |
//! | ED25519_SIGNATURE_LENGTH | 64 | 64 | 0 | 64 |

use std::fmt;

use openssl;
use openssl::sha::sha1;
use rust_sodium::crypto::sign::ed25519;
use std::fmt;

// TODO: when ed25519 becomes available for rust OpenSSL, rust_sodium will be removed.

Expand Down Expand Up @@ -129,6 +131,10 @@ impl PublicKey {
Some(m)
}
}

pub fn sha1(&self) -> Option<[u8; 20]> {
Some(sha1(&self.to_vec()?))
}
}

impl PartialEq for PublicKey {
Expand Down Expand Up @@ -350,4 +356,25 @@ mod tests {
keyvec
);
}

#[test]
fn test_sha1() {
let keyvec = vec![
48, 64, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 1, 3, 44, 0, 4,
0, 80, 239, 172, 104, 165, 76, 172, 6, 229, 136, 156, 105, 23, 249, 46, 30, 148, 87,
105, 57, 6, 105, 134, 2, 229, 115, 169, 44, 162, 41, 190, 228, 56, 20, 100, 64, 79,
167, 224, 118, 14,
];

let key = PublicKey::from_vec(keyvec.clone()).unwrap();
let sha1sum = key.sha1().unwrap();

assert_eq!(
[
254, 127, 138, 49, 67, 126, 206, 58, 169, 85, 132, 14, 211, 247, 13, 170, 244, 166,
152, 180,
],
sha1sum
)
}
}
3 changes: 1 addition & 2 deletions src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use openssl::sign::Signer;
use rust_sodium;
use rust_sodium::crypto::sign::ed25519;
use std::error::Error;
use std::fmt;
use std::os::raw::c_int;

create_error!(SignatureError, "Invalid signature");
Expand All @@ -34,7 +33,7 @@ pub fn verify_signature_ed25519(
pkey: ed25519::PublicKey,
) -> Result<bool, Box<dyn Error>> {
let verify = ed25519::verify_detached(
&ed25519::Signature::from_slice(&*signature).ok_or(Box::new(SignatureError))?,
&ed25519::Signature::from_slice(&*signature).ok_or_else(|| Box::new(SignatureError))?,
data,
&pkey,
);
Expand Down
1 change: 0 additions & 1 deletion src/crypto/signature.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::error::Error;
use std::fmt;

use openssl::bn::BigNum;
use serde::ser::SerializeTuple;
Expand Down
Loading