rtj/
header.rs

1// rtj provides a generic job execution framework in Rust
2// Copyright 2021-2024 Anthony Martinez
3//
4// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6// http://opensource.org/licenses/MIT>, at your option. This file may not be
7// copied, modified, or distributed except according to those terms.
8
9use crate::*;
10
11/// [`Header`] contains identifying information about the [`Job`]
12/// that follows.
13///
14/// Specifically this contains the fields:
15/// - msgtype, the [`Job`] type: `u8`
16/// - psize, the following payload size in bytes as: `u32`
17/// - pubkey, the crypto-box pubkey of the sender as: `[u8; 32]`
18/// - nonce, the message nonce as: `[u8; 24]`
19/// - encrypted, `bool` indicating the encryption status of the following payload.
20#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
21pub struct Header {
22    pub msgtype: u8,
23    pub psize: u32,
24    pub pubkey: [u8; 32],
25    pub nonce: [u8; 24],
26    pub encrypted: bool,
27}
28
29impl Header {
30    /// Creates a new [`Header`] with default values
31    pub fn new() -> Header {
32        Header::default()
33    }
34
35    /// Sets the value of `msgtype`.
36    pub fn set_msgtype<T: Into<u8>>(mut self, msgtype: T) -> Header {
37        self.msgtype = msgtype.into();
38        self
39    }
40
41    /// Sets the value of `psize`.
42    pub fn set_psize<T: Into<u32>>(mut self, psize: T) -> Header {
43        self.psize = psize.into();
44        self
45    }
46
47    /// Sets the value of `pubkey`.
48    pub fn set_pubkey<T: Into<[u8; 32]>>(mut self, pubkey: T) -> Header {
49        self.pubkey = pubkey.into();
50        self
51    }
52
53    /// Sets the value of `nonce` by generating one using the specified csprng.
54    pub fn set_nonce<T: RngCore + CryptoRng>(mut self, csprng: &mut T) -> Result<Header> {
55        let mut nonce: [u8; 24] = [0u8; 24];
56        csprng.try_fill_bytes(&mut nonce)?;
57        self.nonce = nonce;
58        Ok(self)
59    }
60
61    /// Sets the value of `encrypted`.
62    pub fn set_encrypted(mut self, encrypted: bool) -> Header {
63        self.encrypted = encrypted;
64        self
65    }
66
67    /// Returns [`Header`] as a byte array `[u8; 64]`.
68    pub fn to_bytes(self) -> [u8; 64] {
69        let mut header_bytes = [0u8; 64];
70        let reserved = [0u8; 2];
71        [self.msgtype]
72            .iter()
73            .chain(self.psize.to_be_bytes().iter())
74            .chain(self.pubkey.iter())
75            .chain(self.nonce.iter())
76            .chain([self.encrypted as u8].iter())
77            .chain(reserved.iter())
78            .enumerate()
79            .for_each(|(i, x)| header_bytes[i] = *x);
80
81        header_bytes
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    // Testing with Rust MessagePack implementation for Serialization/Deserialization
90    use rmp_serde as rmps;
91
92    enum MsgType {
93        Hello,
94        Unknown,
95    }
96
97    impl From<u8> for MsgType {
98        fn from(t: u8) -> MsgType {
99            match t {
100                0 => MsgType::Hello,
101                _ => MsgType::Unknown,
102            }
103        }
104    }
105
106    impl From<MsgType> for u8 {
107        fn from(t: MsgType) -> u8 {
108            match t {
109                MsgType::Hello => 0,
110                MsgType::Unknown => 255,
111            }
112        }
113    }
114
115    #[derive(Default, Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
116    pub struct Hello {
117        name: String,
118        age: u8,
119    }
120
121    impl Job for Hello {
122        fn encode(&self) -> Vec<u8> {
123            rmps::to_vec(&self).unwrap()
124        }
125
126        fn decode(input: &[u8]) -> Hello {
127            let hello: Hello = rmps::from_read(input).unwrap();
128            hello
129        }
130
131        fn ack(&self) -> Vec<u8> {
132	    let name = &self.name;
133	    let age = &self.age;
134            let ack_string = format!("Hello from {name}, aged {age}");
135            Vec::from(ack_string)
136        }
137
138        fn run(&self) -> std::result::Result<(), Box<dyn std::error::Error>> {
139            self.ack();
140            Ok(())
141        }
142    }
143
144    #[test]
145    fn test_header_new() {
146        let header = Header::new();
147
148        assert_eq!(header.msgtype, 0u8);
149        assert_eq!(header.psize, 0u32);
150        assert_eq!(header.pubkey, [0u8; 32]);
151        assert_eq!(header.nonce, [0u8; 24]);
152        assert!(!header.encrypted);
153    }
154
155    #[test]
156    fn test_header_msgtype() {
157        let header = Header::new().set_msgtype(MsgType::Hello);
158
159        assert_eq!(header.msgtype, 0u8)
160    }
161
162    #[test]
163    fn test_header_psize() {
164        let header = Header::new().set_psize(10u32);
165
166        assert_eq!(header.psize, 10);
167    }
168
169    #[test]
170    fn test_header_pubkey() {
171        let mut rng = rand::thread_rng();
172        let secret_key = crypto_box::SecretKey::generate(&mut rng);
173        let pubkey = secret_key.public_key().as_bytes().to_owned();
174        let header = Header::new().set_pubkey(pubkey);
175
176        assert_eq!(header.pubkey, pubkey);
177    }
178
179    #[test]
180    fn test_header_nonce() {
181        let mut rng = rand::thread_rng();
182        let header = Header::new().set_nonce(&mut rng).unwrap();
183
184        assert_ne!(header.nonce, [0u8; 24]);
185    }
186
187    #[test]
188    fn test_header_encrypted() {
189        let header = Header::new().set_encrypted(true);
190
191        assert!(header.encrypted)
192    }
193    #[test]
194    fn test_header_bytes() {
195        let mut rng = rand::thread_rng();
196        let secret_key = crypto_box::SecretKey::generate(&mut rng);
197        let pubkey = secret_key.public_key().as_bytes().to_owned();
198        let header = Header::new()
199            .set_msgtype(MsgType::Hello)
200            .set_psize(10u32)
201            .set_pubkey(pubkey)
202            .set_nonce(&mut rng)
203            .unwrap();
204
205        let header_bytes = header.to_bytes();
206        assert_eq!(header_bytes[..5], [0, 0, 0, 0, 10]);
207        assert_eq!(header_bytes[5..37], pubkey);
208        assert_ne!(header_bytes[37..61], [0u8; 24]);
209        assert_eq!(header_bytes[61], 0u8);
210        assert_eq!(header_bytes[62..], [0u8; 2]);
211    }
212}