1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 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
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
//! Contains the `HandshakePacket` structure, and a `HandshakePacketBuilder` to
//! construct it field by field.
//!
//! # Example
//!
//! ```
//! use fcp_cryptoauth::handshake_packet::{HandshakePacketBuilder, HandshakePacket, HandshakePacketType};
//! let packet = HandshakePacketBuilder::new()
//!         .packet_type(&HandshakePacketType::Key)
//!         .auth_challenge(&[1u8; 12])
//!         .random_nonce(&[2u8; 24])
//!         .sender_perm_pub_key(&[3u8; 32])
//!         .msg_auth_code(&[4u8; 16])
//!         .sender_encrypted_temp_pub_key(&[5u8; 32])
//!         .encrypted_data(&vec![6u8, 7u8, 8u8])
//!         .finalize()
//!         .unwrap();
//! assert_eq!(packet.packet_type(), Ok(HandshakePacketType::Key));
//! assert_eq!(packet.auth_challenge(), [1u8; 12]);
//! assert_eq!(packet.random_nonce(), [2u8; 24]);
//! assert_eq!(packet.sender_perm_pub_key(), [3u8; 32]);
//! assert_eq!(packet.msg_auth_code(), [4u8; 16]);
//! assert_eq!(packet.sender_encrypted_temp_pub_key(), [5u8; 32]);
//! assert_eq!(packet.encrypted_data(), [6u8, 7u8, 8u8]);
//! assert_eq!(packet.raw, vec![
//!         // session state
//!         0, 0, 0, 2,
//!
//!         // auth challenge
//!                      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
//!
//!         // random nonce
//!         2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,
//!         2, 2, 2, 2,  2, 2, 2, 2,
//!
//!         // sender's permanent public key
//!                                   3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,
//!
//!         // message authentication code 
//!                                   4, 4, 4, 4,  4, 4, 4, 4,
//!         4, 4, 4, 4,  4, 4, 4, 4,
//!
//!         // sender's encrypted temporary public key
//!                                   5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,
//!
//!         // encrypted data
//!                                   6, 7, 8,
//!         ])
//! ```

extern crate hex;
extern crate byteorder;
use std::fmt;

use hex::ToHex;
use handshake_packet::byteorder::ByteOrder;
use keys::ToBase32;
use keys::crypto_box::PublicKey;

/// Represents the CryptoAuth session state, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
#[derive(Eq)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub enum HandshakePacketType {
    Hello,       // 0u32
    RepeatHello, // 1u32
    Key,         // 2u32
    RepeatKey,   // 3u32
}

/// Represents a raw CryptoAuth packet, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#packet-layout
pub struct HandshakePacket {
    pub raw: Vec<u8>,
}

impl fmt::Debug for HandshakePacket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HandshakePacket {{
        raw:                     0x{},
        state:                   {:?},
        auth_challenge:          0x{},
        nonce:                   0x{},
        sender_perm_pub_key:     0x{} ({}),
        msg_auth_code:           0x{},
        sender_enc_temp_pub_key: 0x{},
        encrypted_data:          0x{}
        }}",
            self.raw.to_vec().to_hex(),
            self.packet_type(),
            self.auth_challenge().to_vec().to_hex(),
            self.random_nonce().to_vec().to_hex(),
            self.sender_perm_pub_key().to_vec().to_hex(), PublicKey(self.sender_perm_pub_key()).to_base32(),
            self.msg_auth_code().to_vec().to_hex(),
            self.sender_encrypted_temp_pub_key().to_vec().to_hex(),
            self.encrypted_data().to_vec().to_hex()
            )
    }
}

impl HandshakePacket {
    /// Returns the session state of the packet if it is a known session
    /// state, as defined by
    /// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
    ///
    /// Returns Err(value of field) if the session state field is set
    /// to an unknown value.
    pub fn packet_type(&self) -> Result<HandshakePacketType, u32> {
        match byteorder::BigEndian::read_u32(&self.raw[0..4]) {
            0u32 => Ok(HandshakePacketType::Hello),
            1u32 => Ok(HandshakePacketType::RepeatHello),
            2u32 => Ok(HandshakePacketType::Key),
            3u32 => Ok(HandshakePacketType::RepeatKey),
            n => Err(n),
        }
    }

    /// Returns a copy of the packet's Authorization Challenge.
    pub fn auth_challenge(&self) -> [u8; 12] {
        let mut auth_challenge = [0u8; 12];
        auth_challenge.copy_from_slice(&self.raw[4..16]);
        auth_challenge
    }

    /// Returns a copy of the packet's Nonce.
    pub fn random_nonce(&self) -> [u8; 24] {
        let mut random_nonce = [0u8; 24];
        random_nonce.copy_from_slice(&self.raw[16..40]);
        random_nonce
    }

    /// Returns a copy of the packet's sender permanent public key,
    /// as a byte array.
    pub fn sender_perm_pub_key(&self) -> [u8; 32] {
        let mut sender_perm_pub_key = [0u8; 32];
        sender_perm_pub_key.copy_from_slice(&self.raw[40..72]);
        sender_perm_pub_key
    }

    /// Returns a copy of the packet's Message Authentication Code.
    pub fn msg_auth_code(&self) -> [u8; 16] {
        let mut msg_auth_code = [0u8; 16];
        msg_auth_code.copy_from_slice(&self.raw[72..88]);
        msg_auth_code
    }

    /// Returns a copy of the packet's sender temporary public key,
    /// encrypted and as a byte array.
    pub fn sender_encrypted_temp_pub_key(&self) -> [u8; 32] {
        let mut sender_encrypted_temp_pub_key = [0u8; 32];
        sender_encrypted_temp_pub_key.copy_from_slice(&self.raw[88..120]);
        sender_encrypted_temp_pub_key
    }

    /// Returns a reference to the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(&self) -> &[u8] {
        // Returning a reference to a slice here because:
        // * the data can potentially be large
        // * this is likely to be the last field read, ie. the last use of
        //   the Package object so a copy would be useless.
        &self.raw[120..]
    }

    /// Returns msg_auth_code, sender_encrypted_temp_pub_key, and encrypted_data
    /// concatenated in that order.
    /// The purpose is to use it as input to the 'open' cryptographic primitive.
    pub fn sealed_data(&self) -> &[u8] {
        &self.raw[72..]
    }
}

/// Used to construct a `HandshakePacket` object field-by-field.
#[derive(Debug)]
pub struct HandshakePacketBuilder {
    pub raw: Vec<u8>,

    // Store whether these fields have been set.
    // Checked by the finalization method.
    packet_type: bool,
    auth_challenge: bool,
    random_nonce: bool,
    sender_perm_pub_key: bool,
    msg_auth_code: bool,
    sender_encrypted_temp_pub_key: bool,
    //encrypted_data: bool,
}

impl HandshakePacketBuilder {
    pub fn new() -> HandshakePacketBuilder {
        HandshakePacketBuilder {
            raw: vec![0; 120],

            packet_type: false,
            auth_challenge: false,
            random_nonce: false,
            sender_perm_pub_key: false,
            msg_auth_code: false,
            sender_encrypted_temp_pub_key: false,
            //encrypted_data: false, // Not mandatory
        }
    }

    /// Sets the session state of the packet.
    pub fn packet_type(mut self, packet_type: &HandshakePacketType) -> HandshakePacketBuilder {
        self.packet_type = true;
        let value = match *packet_type {
            HandshakePacketType::Hello => 0u32,
            HandshakePacketType::RepeatHello => 1u32,
            HandshakePacketType::Key => 2u32,
            HandshakePacketType::RepeatKey => 3u32,
        };
        byteorder::BigEndian::write_u32(&mut self.raw[0..4], value);
        self
    }

    /// Sets the packet's Authorization Challenge.
    pub fn auth_challenge(mut self, auth_challenge: &[u8; 12]) -> HandshakePacketBuilder {
        self.auth_challenge = true;
        self.raw[4..16].clone_from_slice(auth_challenge);
        self
    }

    /// Sets the packet's Nonce.
    pub fn random_nonce(mut self, random_nonce: &[u8; 24]) -> HandshakePacketBuilder {
        self.random_nonce = true;
        self.raw[16..40].clone_from_slice(random_nonce);
        self
    }

    /// Sets the packet's sender permanent public key, provided as a byte array.
    pub fn sender_perm_pub_key(mut self, sender_perm_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_perm_pub_key = true;
        self.raw[40..72].clone_from_slice(sender_perm_pub_key);
        self
    }

    /// Sets the packet's Message Authentication Code.
    pub fn msg_auth_code(mut self, msg_auth_code: &[u8; 16]) -> HandshakePacketBuilder {
        self.msg_auth_code = true;
        self.raw[72..88].clone_from_slice(msg_auth_code);
        self
    }

    /// Sets the packet's sender temporary public key, provided encrypted and
    /// as a byte array.
    pub fn sender_encrypted_temp_pub_key(mut self, sender_encrypted_temp_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_encrypted_temp_pub_key = true;
        self.raw[88..120].clone_from_slice(sender_encrypted_temp_pub_key);
        self
    }

    /// Sets the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(mut self, encrypted_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(120);
        self.raw.extend_from_slice(encrypted_data);
        self
    }

    /// Sets the packet's msg_auth_code, sender_encrypted_temp_pub_key,
    /// and encrypted_data concatenated in that order.
    /// The purpose is to use it as output of the 'seal' cryptographic primitive.
    pub fn sealed_data(mut self, sealed_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(72);
        self.raw.extend_from_slice(sealed_data);
        self
    }

    /// If all mandatory fields have been provided, returns the constructed packet.
    /// Otherwise, returns `Err([packet_type, auth_challenge, random_nonce,
    /// sender_perm_pub_key, msg_auth_code, sender_encrypted_temp_pub_key])`,
    /// Each of the items of the array being a boolean set to true if and only
    /// if the corresponding mandatory field was provided.
    pub fn finalize(self) -> Result<HandshakePacket, [bool; 6]> {
        let booleans = [self.packet_type, self.auth_challenge, self.random_nonce, self.sender_perm_pub_key, self.msg_auth_code, self.sender_encrypted_temp_pub_key];
        if booleans.contains(&false) {
            Err(booleans)
        }
        else {
            Ok(HandshakePacket { raw: self.raw, })
        }
    }
}