// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Handles WebSocketToPeer API Messages.
#![allow(clippy::large_enum_variant)]
#![deny(
clippy::option_unwrap_used,
clippy::result_unwrap_used,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
/*#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;*/
#[macro_use]
extern crate serde_derive;
/// WS2Pv2 Messages
pub mod v2;
use crate::v2::WS2Pv2Message;
use dup_crypto::hashs::Hash;
use dup_crypto::keys::bin_signable::BinSignable;
use dup_crypto::keys::*;
use durs_common_tools::fatal_error;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
/// WS2Pv2Message
pub enum WS2PMessage {
/// Old version not used
_V0,
/// Old version not used
_V1,
/// Version 2
V2(WS2Pv2Message),
}
/// Enumerate errors can happen when parsing and checking messages
#[derive(Debug)]
pub enum WS2PMessageError {
/// Error at deserialization
DeserError(bincode::Error),
/// Invalid hash
InvalidHash,
/// Invalid signature
SigError(SigError),
}
impl From<bincode::Error> for WS2PMessageError {
fn from(e: bincode::Error) -> Self {
WS2PMessageError::DeserError(e)
}
}
impl WS2PMessage {
/// Get message hash
pub fn hash(&self) -> Option<Hash> {
match *self {
WS2PMessage::V2(ref msg_v2) => msg_v2.message_hash,
WS2PMessage::_V0 | WS2PMessage::_V1 => {
fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
}
}
}
/// Parse and check bin message
pub fn parse_and_check_bin_message(bin_msg: &[u8]) -> Result<WS2PMessage, WS2PMessageError> {
let msg: WS2PMessage = bincode::deserialize(&bin_msg)?;
let hash = msg.hash();
//debug!("parse_and_check_bin_message: hash={:?}", hash);
// Compute hash len
let hash_len = 33;
// Compute signature len
let sig_len = if let Some(sig) = msg.signature() {
match sig {
Sig::Ed25519(_) => 69,
Sig::Schnorr() => fatal_error!("Schnorr algo not yet implemented !"),
}
} else {
1
};
if hash.is_none()
|| Hash::compute(&bin_msg[0..(bin_msg.len() - hash_len - sig_len)])
== hash.expect("safe unwrap")
{
match msg.verify() {
Ok(()) => Ok(msg),
Err(e) => Err(WS2PMessageError::SigError(e)),
}
} else {
Err(WS2PMessageError::InvalidHash)
}
}
}
impl<'de> BinSignable<'de> for WS2PMessage {
#[inline]
fn add_sig_to_bin_datas(&self, bin_datas: &mut Vec<u8>) {
bin_datas.extend_from_slice(
&bincode::serialize(&self.signature()).expect("Fail to binarize sig !"),
);
}
#[inline]
fn get_bin_without_sig(&self) -> Result<Vec<u8>, failure::Error> {
let mut bin_msg = bincode::serialize(&self)?;
let sig_size = bincode::serialized_size(&self.signature())?;
let bin_msg_len = bin_msg.len();
bin_msg.truncate(bin_msg_len - (sig_size as usize));
Ok(bin_msg)
}
fn issuer_pubkey(&self) -> PubKey {
match *self {
WS2PMessage::V2(ref msg_v2) => msg_v2.issuer_pubkey,
WS2PMessage::_V0 | WS2PMessage::_V1 => {
fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
}
}
}
fn signature(&self) -> Option<Sig> {
match *self {
WS2PMessage::V2(ref msg_v2) => msg_v2.signature,
WS2PMessage::_V0 | WS2PMessage::_V1 => {
fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
}
}
}
fn set_signature(&mut self, signature: Sig) {
match *self {
WS2PMessage::V2(ref mut msg_v2) => msg_v2.signature = Some(signature),
WS2PMessage::_V0 | WS2PMessage::_V1 => {
fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
}
}
}
}
#[cfg(test)]
mod tests {
use crate::v2::payload_container::WS2Pv2MessagePayload;
use crate::v2::WS2Pv2Message;
use crate::WS2PMessage;
use bincode;
use bincode::{deserialize, serialize};
use dubp_common_doc::{BlockNumber, Blockstamp};
use dubp_currency_params::CurrencyName;
use dubp_user_docs::documents::certification::*;
use dup_crypto::keys::bin_signable::BinSignable;
use dup_crypto::keys::*;
use durs_network_documents::network_endpoint::*;
use durs_network_documents::network_peer::*;
use durs_network_documents::*;
use std::net::Ipv4Addr;
use std::str::FromStr;
use unwrap::unwrap;
pub fn keypair1() -> ed25519::Ed25519KeyPair {
let seed = Seed32::new([
228, 125, 124, 120, 57, 212, 246, 250, 139, 246, 62, 26, 56, 241, 175, 123, 151, 209,
5, 106, 2, 148, 43, 101, 118, 160, 233, 7, 112, 222, 0, 169,
]);
ed25519::KeyPairFromSeed32Generator::generate(seed)
}
pub fn create_endpoint_v11() -> EndpointV2 {
EndpointV2 {
api: ApiName(String::from("WS2P")),
api_version: 2,
network_features: EndpointV2NetworkFeatures(vec![1u8]),
api_features: ApiFeatures(vec![7u8]),
ip_v4: None,
ip_v6: None,
domain: Some(String::from("g1.durs.ifee.fr")),
port: 443u16,
path: Some(String::from("ws2p")),
}
}
pub fn create_second_endpoint_v11() -> EndpointV2 {
EndpointV2 {
api: ApiName(String::from("WS2P")),
api_version: 2,
network_features: EndpointV2NetworkFeatures(vec![1u8]),
api_features: ApiFeatures(vec![7u8]),
ip_v4: Some(unwrap!(Ipv4Addr::from_str("84.16.72.210"))),
ip_v6: None,
domain: None,
port: 443u16,
path: Some(String::from("ws2p")),
}
}
pub fn create_peer_card_v11() -> PeerCardV11 {
PeerCardV11 {
currency_name: CurrencyName(String::from("g1")),
issuer: PubKey::Ed25519(keypair1().pubkey),
node_id: NodeId(0),
created_on: BlockNumber(50),
endpoints: vec![create_endpoint_v11(), create_second_endpoint_v11()],
endpoints_str: vec![],
sig: None,
}
}
pub fn test_ws2p_message(payload: WS2Pv2MessagePayload) {
let keypair1 = keypair1();
let signator =
SignatorEnum::Ed25519(keypair1.generate_signator().expect("fail to gen signator"));
let mut ws2p_message = WS2PMessage::V2(WS2Pv2Message {
currency_name: CurrencyName(String::from("g1")),
issuer_node_id: NodeId(0),
issuer_pubkey: PubKey::Ed25519(keypair1.public_key()),
payload,
message_hash: None,
signature: None,
});
let sign_result = ws2p_message.sign(&signator);
match sign_result {
Ok(bin_msg) => {
// Test binarization
assert_eq!(
serialize(&ws2p_message).expect("Fail to serialize WS2Pv2Message !"),
bin_msg
);
// Test sign
ws2p_message
.verify()
.expect("WS2Pv2Message : Invalid signature !");
// Test debinarization
let debinarization_result: Result<WS2PMessage, bincode::Error> =
deserialize(&bin_msg);
match debinarization_result {
Ok(ws2p_message2) => assert_eq!(ws2p_message, ws2p_message2),
Err(e) => panic!("Fail to debinarize ws2p_message : {:?}", e),
};
}
Err(e) => panic!("Fail to sign ws2p_message : {:?}", e),
};
}
pub fn create_cert_doc() -> CompactCertificationDocumentV10 {
let sig = Sig::Ed25519(unwrap!(ed25519::Signature::from_base64(
"qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw==",
)));
let target = PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"
)));
let blockstamp = unwrap!(Blockstamp::from_string(
"36-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B865"
));
CompactCertificationDocumentV10 {
issuer: PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
"4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR"
))),
target,
block_number: blockstamp.id,
signature: sig,
}
}
}