saito_core/core/msg/
handshake.rs

1use std::io::{Error, ErrorKind};
2
3use log::{trace, warn};
4
5use crate::core::consensus::peers::peer_service::PeerService;
6use crate::core::defs::{SaitoHash, SaitoPublicKey, SaitoSignature};
7use crate::core::process::version::Version;
8use crate::core::util::serialize::Serialize;
9
10#[derive(Debug)]
11pub struct HandshakeChallenge {
12    pub challenge: SaitoHash,
13}
14
15#[derive(Debug)]
16pub struct HandshakeResponse {
17    pub public_key: SaitoPublicKey,
18    pub signature: SaitoSignature,
19    pub is_lite: bool,
20    pub block_fetch_url: String,
21    pub challenge: SaitoHash,
22    pub services: Vec<PeerService>,
23    pub wallet_version: Version,
24    pub core_version: Version,
25}
26
27impl Serialize<Self> for HandshakeChallenge {
28    fn serialize(&self) -> Vec<u8> {
29        let buffer = [self.challenge.to_vec()].concat();
30        return buffer;
31    }
32    fn deserialize(buffer: &Vec<u8>) -> Result<Self, Error> {
33        if buffer.len() < 32 {
34            warn!(
35                "Deserializing Handshake Challenge, buffer size is :{:?}",
36                buffer.len()
37            );
38            return Err(Error::from(ErrorKind::InvalidData));
39        }
40
41        let mut challenge = HandshakeChallenge { challenge: [0; 32] };
42        challenge.challenge = buffer[0..32]
43            .to_vec()
44            .try_into()
45            .or(Err(Error::from(ErrorKind::InvalidInput)))?;
46
47        Ok(challenge)
48    }
49}
50
51impl Serialize<Self> for HandshakeResponse {
52    fn serialize(&self) -> Vec<u8> {
53        [
54            self.core_version.serialize(),
55            self.wallet_version.serialize(),
56            self.public_key.to_vec(),
57            self.signature.to_vec(),
58            self.challenge.to_vec(),
59            (self.is_lite as u8).to_be_bytes().to_vec(),
60            (self.block_fetch_url.len() as u32).to_be_bytes().to_vec(),
61            self.block_fetch_url.as_bytes().to_vec(),
62            PeerService::serialize_services(&self.services),
63        ]
64        .concat()
65    }
66    fn deserialize(buffer: &Vec<u8>) -> Result<Self, Error> {
67        trace!("deserializing handshake buffer : {:?}", buffer.len());
68
69        const MIN_LEN: usize = 142;
70
71        if buffer.len() < MIN_LEN {
72            warn!(
73                "Deserializing failed for handshake response, buffer size is :{:?}",
74                buffer.len()
75            );
76            return Err(Error::from(ErrorKind::InvalidData));
77        }
78
79        let mut response = HandshakeResponse {
80            core_version: Version::deserialize(&buffer[0..4].to_vec())?,
81            wallet_version: Version::deserialize(&buffer[4..8].to_vec())?,
82            public_key: buffer[8..41]
83                .to_vec()
84                .try_into()
85                .or(Err(Error::from(ErrorKind::InvalidInput)))?,
86            signature: buffer[41..105]
87                .to_vec()
88                .try_into()
89                .or(Err(Error::from(ErrorKind::InvalidInput)))?,
90            challenge: buffer[105..137]
91                .to_vec()
92                .try_into()
93                .or(Err(Error::from(ErrorKind::InvalidInput)))?,
94            is_lite: buffer[137] != 0,
95            block_fetch_url: "".to_string(),
96            services: vec![],
97        };
98        let url_length = u32::from_be_bytes(
99            buffer[138..142]
100                .try_into()
101                .or(Err(Error::from(ErrorKind::InvalidInput)))?,
102        ) as usize;
103
104        // if we detect a block fetch url, we will retrieve it
105        if url_length > 0 {
106            if buffer.len() < MIN_LEN + url_length {
107                warn!(
108                    "cannot read block fetch url of size : {:?} from buffer size : {:?}",
109                    url_length,
110                    buffer.len()
111                );
112                return Err(Error::from(ErrorKind::InvalidData));
113            }
114            trace!("reading URL with length : {:?}", url_length);
115            let result = String::from_utf8(buffer[MIN_LEN..(MIN_LEN + url_length)].to_vec());
116            if result.is_err() {
117                warn!(
118                    "failed decoding block fetch url. {:?}",
119                    result.err().unwrap()
120                );
121                return Err(Error::from(ErrorKind::InvalidData));
122            }
123
124            response.block_fetch_url = result.unwrap();
125            trace!("block fetch url read as : {:?}", response.block_fetch_url);
126        }
127
128        // if we detect services, we deserialize that too
129        if buffer.len() > (MIN_LEN + url_length) {
130            trace!("reading peer services");
131            let service_buffer = buffer[(MIN_LEN + url_length)..].to_vec();
132
133            let services = PeerService::deserialize_services(service_buffer);
134            if services.is_err() {
135                let len = buffer.len() - (MIN_LEN + url_length);
136                warn!(
137                "Deserializing failed for handshake response, remaining buffer of size :{:?} cannot be parsed for peer services", len);
138                return Err(Error::from(ErrorKind::InvalidData));
139            }
140            let services = services.unwrap();
141            trace!("{:?} services read from handshake response", services.len());
142            response.services = services;
143        }
144
145        Ok(response)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use crate::core::msg::handshake::{HandshakeChallenge, HandshakeResponse};
152    use crate::core::process::version::Version;
153    use crate::core::util::serialize::Serialize;
154
155    #[test]
156    fn test_handshake() {
157        let crypto = secp256k1::Secp256k1::new();
158
159        let (_secret_key_1, _public_key_1) =
160            crypto.generate_keypair(&mut secp256k1::rand::thread_rng());
161        let (secret_key_2, public_key_2) =
162            crypto.generate_keypair(&mut secp256k1::rand::thread_rng());
163        let challenge = HandshakeChallenge {
164            challenge: rand::random(),
165        };
166        let buffer = challenge.serialize();
167        assert_eq!(buffer.len(), 32);
168        let challenge2 = HandshakeChallenge::deserialize(&buffer).expect("deserialization failed");
169        assert_eq!(challenge.challenge, challenge2.challenge);
170
171        let signature = crypto.sign_ecdsa(
172            &secp256k1::Message::from_slice(&challenge.challenge).unwrap(),
173            &secret_key_2,
174        );
175        let response = HandshakeResponse {
176            public_key: public_key_2.serialize(),
177            signature: signature.serialize_compact(),
178            challenge: rand::random(),
179            is_lite: false,
180            block_fetch_url: "http://url/test2".to_string(),
181            services: vec![],
182            wallet_version: Version {
183                major: 1,
184                minor: 2,
185                patch: 3,
186            },
187            core_version: Version::new(10, 20, 30),
188        };
189        let buffer = response.serialize();
190        assert_eq!(buffer.len(), 158);
191        let response2 = HandshakeResponse::deserialize(&buffer).expect("deserialization failed");
192        assert_eq!(response.challenge, response2.challenge);
193        assert_eq!(response.public_key, response2.public_key);
194        assert_eq!(response.block_fetch_url, response2.block_fetch_url);
195
196        assert_eq!(response.signature, response2.signature);
197        assert_eq!(response.wallet_version, response2.wallet_version);
198        assert_eq!(response.core_version, response2.core_version);
199    }
200}