lib.rs 9.69 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//  Copyright (C) 2018  The Duniter Project Developers.
//
// 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/>.

16 17
//! Handles WebSocketToPeer API Messages.

18
#![allow(clippy::large_enum_variant)]
19 20 21 22 23 24 25 26 27 28 29
#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

Éloïs's avatar
Éloïs committed
30 31 32 33 34 35
/*#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;*/

#[macro_use]
extern crate serde_derive;
36 37
#[macro_use]
extern crate log;
Éloïs's avatar
Éloïs committed
38

39
/// WS2Pv2 Messages
40
pub mod v2;
41

42
use crate::v2::WS2Pv2Message;
Éloïs's avatar
Éloïs committed
43 44
use dup_crypto::hashs::Hash;
use dup_crypto::keys::bin_signable::BinSignable;
45
use dup_crypto::keys::*;
46
use durs_common_tools::fatal_error;
47

Éloïs's avatar
Éloïs committed
48 49
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
/// WS2Pv2Message
50
pub enum WS2PMessage {
51 52 53 54
    /// Old version not used
    _V0,
    /// Old version not used
    _V1,
55
    /// Version 2
Éloïs's avatar
Éloïs committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    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,
81 82 83
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
Éloïs's avatar
Éloïs committed
84 85
        }
    }
86

Éloïs's avatar
Éloïs committed
87 88 89 90
    /// 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();
91
        //debug!("parse_and_check_bin_message: hash={:?}", hash);
92 93 94 95 96 97
        // Compute hash len
        let hash_len = 33;
        // Compute signature len
        let sig_len = if let Some(sig) = msg.signature() {
            match sig {
                Sig::Ed25519(_) => 69,
98
                Sig::Schnorr() => fatal_error!("Schnorr algo not yet implemented !"),
99 100 101 102 103 104 105 106 107
            }
        } else {
            1
        };

        if hash.is_none()
            || Hash::compute(&bin_msg[0..(bin_msg.len() - hash_len - sig_len)])
                == hash.expect("safe unwrap")
        {
Éloïs's avatar
Éloïs committed
108 109 110 111 112 113 114 115
            match msg.verify() {
                Ok(()) => Ok(msg),
                Err(e) => Err(WS2PMessageError::SigError(e)),
            }
        } else {
            Err(WS2PMessageError::InvalidHash)
        }
    }
116 117
}

118 119 120 121
impl<'de> BinSignable<'de> for WS2PMessage {
    fn issuer_pubkey(&self) -> PubKey {
        match *self {
            WS2PMessage::V2(ref msg_v2) => msg_v2.issuer_pubkey(),
122 123 124
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
125 126 127 128 129
        }
    }
    fn store_hash(&self) -> bool {
        match *self {
            WS2PMessage::V2(ref msg_v2) => msg_v2.store_hash(),
130 131 132
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
133 134 135 136 137
        }
    }
    fn hash(&self) -> Option<Hash> {
        match *self {
            WS2PMessage::V2(ref msg_v2) => msg_v2.hash(),
138 139 140
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
141 142 143 144 145
        }
    }
    fn set_hash(&mut self, hash: Hash) {
        match *self {
            WS2PMessage::V2(ref mut msg_v2) => msg_v2.set_hash(hash),
146 147 148
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
149 150 151 152 153
        }
    }
    fn signature(&self) -> Option<Sig> {
        match *self {
            WS2PMessage::V2(ref msg_v2) => msg_v2.signature(),
154 155 156
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
157 158 159 160 161
        }
    }
    fn set_signature(&mut self, signature: Sig) {
        match *self {
            WS2PMessage::V2(ref mut msg_v2) => msg_v2.set_signature(signature),
162 163 164
            WS2PMessage::_V0 | WS2PMessage::_V1 => {
                fatal_error!("Dev error: must not use WS2PMessage version < 2 in WS2Pv2+ !")
            }
165 166 167 168
        }
    }
}

169 170
#[cfg(test)]
mod tests {
171 172
    use crate::v2::payload_container::WS2Pv2MessagePayload;
    use crate::v2::WS2Pv2Message;
Éloïs's avatar
Éloïs committed
173 174
    use bincode;
    use bincode::{deserialize, serialize};
175
    use dubp_documents::documents::certification::*;
176
    use dubp_documents::{BlockNumber, Blockstamp};
177 178
    use dup_crypto::keys::bin_signable::BinSignable;
    use dup_crypto::keys::*;
179
    use dup_currency_params::CurrencyName;
Éloïs's avatar
Éloïs committed
180 181 182
    use durs_network_documents::network_endpoint::*;
    use durs_network_documents::network_peer::*;
    use durs_network_documents::*;
183 184 185 186 187 188 189 190 191 192
    use std::net::Ipv4Addr;
    use std::str::FromStr;

    pub fn keypair1() -> ed25519::KeyPair {
        ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(
            "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV".as_bytes(),
            "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_".as_bytes(),
        )
    }

193 194
    pub fn create_endpoint_v11() -> EndpointV2 {
        EndpointV2 {
195
            api: ApiName(String::from("WS2P")),
196
            api_version: 2,
197
            network_features: EndpointV2NetworkFeatures(vec![1u8]),
198 199 200
            api_features: ApiFeatures(vec![7u8]),
            ip_v4: None,
            ip_v6: None,
201
            domain: Some(String::from("g1.durs.ifee.fr")),
202 203
            port: 443u16,
            path: Some(String::from("ws2p")),
204
        }
205
    }
206 207
    pub fn create_second_endpoint_v11() -> EndpointV2 {
        EndpointV2 {
208
            api: ApiName(String::from("WS2P")),
209
            api_version: 2,
210
            network_features: EndpointV2NetworkFeatures(vec![1u8]),
211 212 213
            api_features: ApiFeatures(vec![7u8]),
            ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
            ip_v6: None,
214
            domain: None,
215 216
            port: 443u16,
            path: Some(String::from("ws2p")),
217
        }
218 219 220 221
    }

    pub fn create_peer_card_v11() -> PeerCardV11 {
        PeerCardV11 {
Éloïs's avatar
Éloïs committed
222
            currency_name: CurrencyName(String::from("g1")),
223 224
            issuer: PubKey::Ed25519(keypair1().pubkey),
            node_id: NodeId(0),
225
            created_on: BlockNumber(50),
226
            endpoints: vec![create_endpoint_v11(), create_second_endpoint_v11()],
Éloïs's avatar
Éloïs committed
227
            endpoints_str: vec![],
228 229 230 231
            sig: None,
        }
    }

Éloïs's avatar
Éloïs committed
232
    pub fn test_ws2p_message(payload: WS2Pv2MessagePayload) {
233
        let keypair1 = keypair1();
Éloïs's avatar
Éloïs committed
234 235
        let mut ws2p_message = WS2Pv2Message {
            currency_name: CurrencyName(String::from("g1")),
236 237 238 239 240 241 242 243 244 245
            issuer_node_id: NodeId(0),
            issuer_pubkey: PubKey::Ed25519(keypair1.public_key()),
            payload,
            message_hash: None,
            signature: None,
        };

        let sign_result = ws2p_message.sign(PrivKey::Ed25519(keypair1.private_key()));
        if let Ok(bin_msg) = sign_result {
            // Test binarization
Éloïs's avatar
Éloïs committed
246
            assert_eq!(
Éloïs's avatar
Éloïs committed
247
                serialize(&ws2p_message).expect("Fail to serialize WS2Pv2Message !"),
Éloïs's avatar
Éloïs committed
248 249
                bin_msg
            );
250
            // Test sign
Éloïs's avatar
Éloïs committed
251 252
            ws2p_message
                .verify()
Éloïs's avatar
Éloïs committed
253
                .expect("WS2Pv2Message : Invalid signature !");
254
            // Test debinarization
Éloïs's avatar
Éloïs committed
255
            let debinarization_result: Result<WS2Pv2Message, bincode::Error> =
Éloïs's avatar
Éloïs committed
256
                deserialize(&bin_msg);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
            if let Ok(ws2p_message2) = debinarization_result {
                assert_eq!(ws2p_message, ws2p_message2);
            } else {
                panic!(
                    "Fail to debinarize ws2p_message : {:?}",
                    debinarization_result.err().unwrap()
                );
            }
        } else {
            panic!(
                "Fail to sign ws2p_message : {:?}",
                sign_result.err().unwrap()
            );
        }
    }
Éloïs's avatar
Éloïs committed
272 273 274 275 276 277 278 279 280 281 282 283 284

    pub fn create_cert_doc() -> CompactCertificationDocument {
        let sig = Sig::Ed25519(ed25519::Signature::from_base64(
            "qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw==",
        ).unwrap());

        let target = PubKey::Ed25519(
            ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
                .unwrap(),
        );

        let blockstamp = Blockstamp::from_string(
            "36-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B865",
285 286
        )
        .unwrap();
Éloïs's avatar
Éloïs committed
287 288 289 290 291 292

        CompactCertificationDocument {
            issuer: PubKey::Ed25519(
                ed25519::PublicKey::from_base58("4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR")
                    .unwrap(),
            ),
Éloïs's avatar
Éloïs committed
293
            target,
Éloïs's avatar
Éloïs committed
294 295 296 297
            block_number: blockstamp.id,
            signature: sig,
        }
    }
298
}