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
//! Low-level bindings to shs1-c. You probably don't need to use this
//! module directly.

use std::mem::uninitialized;
use std::marker::PhantomData;

use sodiumoxide::crypto::{box_, sign, scalarmult, secretbox, auth};
use sodiumoxide::crypto::hash::sha256;
use sodiumoxide::utils::memzero;

/// Length of a network identifier in bytes.
pub const NETWORK_IDENTIFIER_BYTES: usize = 32;

/// Length of msg1 in bytes.
pub const MSG1_BYTES: usize = 64;
/// Length of msg2 in bytes.
pub const MSG2_BYTES: usize = 64;
/// Length of msg3 in bytes.
pub const MSG3_BYTES: usize = 112;
/// Length of msg4 in bytes.
pub const MSG4_BYTES: usize = 80;

/// The data resulting from a handshake: Keys and nonces suitable for encrypted
/// two-way communication with the peer via box-stream-rs, and the longterm
/// public key of the peer.
#[repr(C)]
#[derive(Debug)]
pub struct Outcome {
    encryption_key: [u8; secretbox::KEYBYTES],
    encryption_nonce: [u8; secretbox::NONCEBYTES],
    padding_encryption: [u8; 8],
    decryption_key: [u8; secretbox::KEYBYTES],
    decryption_nonce: [u8; secretbox::NONCEBYTES],
    padding_decryption: [u8; 8],
    peer_longterm_pk: [u8; sign::PUBLICKEYBYTES],
}

/// Zero out all sensitive data when going out of scope
impl Drop for Outcome {
    fn drop(&mut self) {
        memzero(&mut self.encryption_key);
        memzero(&mut self.encryption_nonce);
        memzero(&mut self.decryption_key);
        memzero(&mut self.decryption_nonce);
    }
}

impl Outcome {
    /// The negotiated key that should be used to encrypt messages to the peer.
    pub fn encryption_key(&self) -> secretbox::Key {
        secretbox::Key(self.encryption_key)
    }

    /// The negotiated initial nonce that should be used to encrypt messages to the peer.
    pub fn encryption_nonce(&self) -> secretbox::Nonce {
        secretbox::Nonce(self.encryption_nonce)
    }

    /// The negotiated key that should be used to decrypt messages from the peer.
    pub fn decryption_key(&self) -> secretbox::Key {
        secretbox::Key(self.decryption_key)
    }

    /// The negotiated initial nonce that should be used to decrypt messages from the peer.
    pub fn decryption_nonce(&self) -> secretbox::Nonce {
        secretbox::Nonce(self.decryption_nonce)
    }

    /// The longterm public key of the peer.
    pub fn peer_longterm_pk(&self) -> sign::PublicKey {
        sign::PublicKey(self.peer_longterm_pk)
    }
}

/// The struct used in the C code to perform the client side of a handshake.
#[repr(C)]
// #[derive(Debug)]
pub struct Client<'a> {
    // inputs
    app: *const [u8; auth::KEYBYTES],
    pub_: *const [u8; sign::PUBLICKEYBYTES],
    sec: *const [u8; sign::SECRETKEYBYTES],
    eph_pub: *const [u8; box_::PUBLICKEYBYTES],
    eph_sec: *const [u8; box_::SECRETKEYBYTES],
    server_pub: *const [u8; sign::PUBLICKEYBYTES],
    // intermediate results
    shared_secret: [u8; scalarmult::GROUPELEMENTBYTES],
    server_lterm_shared: [u8; scalarmult::GROUPELEMENTBYTES],
    hello: [u8; sign::SIGNATUREBYTES + sign::PUBLICKEYBYTES],
    shared_hash: [u8; sha256::DIGESTBYTES],
    server_eph_pub: [u8; box_::PUBLICKEYBYTES],
    _lifetime: PhantomData<&'a [u8; 0]>,
}

impl<'a> Client<'a> {
    /// Creates and initializes a new `Client`.
    pub fn new(app: *const [u8; auth::KEYBYTES],
               pub_: *const [u8; sign::PUBLICKEYBYTES],
               sec: *const [u8; sign::SECRETKEYBYTES],
               eph_pub: *const [u8; box_::PUBLICKEYBYTES],
               eph_sec: *const [u8; box_::SECRETKEYBYTES],
               server_pub: *const [u8; sign::PUBLICKEYBYTES])
               -> Client<'a> {
        Client {
            app,
            pub_,
            sec,
            eph_pub,
            eph_sec,
            server_pub,
            shared_secret: unsafe { uninitialized() },
            server_lterm_shared: unsafe { uninitialized() },
            hello: unsafe { uninitialized() },
            shared_hash: unsafe { uninitialized() },
            server_eph_pub: unsafe { uninitialized() },
            _lifetime: PhantomData,
        }
    }

    /// Writes the client challenge into `challenge` and updates the client state.
    pub fn create_msg1(&mut self, challenge: &mut [u8; MSG1_BYTES]) {
        unsafe { shs1_create_client_challenge(challenge, self) }
    }

    /// Verifies the given server `challenge` and updates the client state.
    pub fn verify_msg2(&mut self, challenge: &[u8; MSG1_BYTES]) -> bool {
        unsafe { shs1_verify_server_challenge(challenge, self) }
    }

    /// Writes the client authentication into `auth` and updates the client state.
    pub fn create_msg3(&mut self, auth: &mut [u8; MSG3_BYTES]) -> i32 {
        unsafe { shs1_create_client_auth(auth, self) }
    }

    /// Verifies the given server `ack`knowledgement and updates the client state.
    pub fn verify_msg4(&mut self, ack: &[u8; MSG4_BYTES]) -> bool {
        unsafe { shs1_verify_server_ack(ack, self) }
    }

    /// Computes the outcome of the handshake and writes it into `outcome`.
    pub fn outcome(&mut self, outcome: &mut Outcome) {
        unsafe { shs1_client_outcome(outcome, self) }
    }

    /// Zeros out all sensitive data in the `Client`.
    fn clean(&mut self) {
        unsafe { shs1_client_clean(self) }
    }
}

/// Zero out all sensitive data when going out of scope.
impl<'a> Drop for Client<'a> {
    fn drop(&mut self) {
        self.clean();
    }
}

/// The struct used in the C code to perform the server side of a handshake.
#[repr(C)]
// #[derive(Debug)]
pub struct Server<'a> {
    app: *const [u8; auth::KEYBYTES],
    pub_: *const [u8; sign::PUBLICKEYBYTES],
    sec: *const [u8; sign::SECRETKEYBYTES],
    eph_pub: *const [u8; box_::PUBLICKEYBYTES],
    eph_sec: *const [u8; box_::SECRETKEYBYTES],
    //intermediate results
    client_hello: [u8; sign::SIGNATUREBYTES + sign::PUBLICKEYBYTES],
    shared_hash: [u8; sha256::DIGESTBYTES],
    client_eph_pub: [u8; box_::PUBLICKEYBYTES],
    client_pub: [u8; sign::PUBLICKEYBYTES],
    box_sec: [u8; sha256::DIGESTBYTES],
    _lifetime: PhantomData<&'a [u8; 0]>,
}

impl<'a> Server<'a> {
    /// Creates and initializes a new `Server`.
    pub fn new(app: *const [u8; auth::KEYBYTES],
               pub_: *const [u8; sign::PUBLICKEYBYTES],
               sec: *const [u8; sign::SECRETKEYBYTES],
               eph_pub: *const [u8; box_::PUBLICKEYBYTES],
               eph_sec: *const [u8; box_::SECRETKEYBYTES])
               -> Server<'a> {
        Server {
            app,
            pub_,
            sec,
            eph_pub,
            eph_sec,
            client_hello: unsafe { uninitialized() },
            shared_hash: unsafe { uninitialized() },
            client_eph_pub: unsafe { uninitialized() },
            client_pub: unsafe { uninitialized() },
            box_sec: unsafe { uninitialized() },
            _lifetime: PhantomData,
        }
    }

    /// Verifies the given client `challenge` and updates the server state.
    pub fn verify_msg1(&mut self, challenge: &[u8; MSG1_BYTES]) -> bool {
        unsafe { shs1_verify_client_challenge(challenge, self) }
    }

    /// Writes the server challenge into `challenge` and updates the server state.
    pub fn create_msg2(&mut self, challenge: &mut [u8; MSG2_BYTES]) {
        unsafe { shs1_create_server_challenge(challenge, self) }
    }

    /// Verifies the given client `auth`entication and updates the server state.
    pub fn verify_msg3(&mut self, auth: &[u8; MSG3_BYTES]) -> bool {
        unsafe { shs1_verify_client_auth(auth, self) }
    }

    /// Writes the server acknowledgement into `ack` and updates the server state.
    pub fn create_msg4(&mut self, ack: *mut [u8; MSG4_BYTES]) {
        unsafe { shs1_create_server_ack(ack, self) }
    }

    /// Computes the outcome of the handshake and writes it into `outcome`.
    pub fn outcome(&mut self, outcome: &mut Outcome) {
        unsafe { shs1_server_outcome(outcome, self) }
    }

    /// Zeros out all sensitive data in the `Server`.
    pub fn clean(&mut self) {
        unsafe { shs1_server_clean(self) }
    }

    /// Returns the longterm public key of the client. This will return
    /// uninitialized memory if called before the server verified msg3.
    pub unsafe fn client_longterm_pub(&self) -> [u8; sign::PUBLICKEYBYTES] {
        self.client_pub
    }
}

extern "C" {
    // client side
    fn shs1_create_client_challenge(challenge: *mut [u8; MSG1_BYTES], client: *mut Client);
    fn shs1_verify_server_challenge(challenge: *const [u8; MSG1_BYTES],
                                    client: *mut Client)
                                    -> bool;
    fn shs1_create_client_auth(auth: *mut [u8; MSG3_BYTES], client: *mut Client) -> i32;
    fn shs1_verify_server_ack(ack: *const [u8; MSG4_BYTES], client: *mut Client) -> bool;
    fn shs1_client_outcome(outcome: *mut Outcome, client: *mut Client);
    fn shs1_client_clean(client: *mut Client);
    // server side
    fn shs1_verify_client_challenge(challenge: *const [u8; MSG1_BYTES],
                                    server: *mut Server)
                                    -> bool;
    fn shs1_create_server_challenge(challenge: *mut [u8; MSG2_BYTES], server: *mut Server);
    fn shs1_verify_client_auth(auth: *const [u8; MSG3_BYTES], server: *mut Server) -> bool;
    fn shs1_create_server_ack(ack: *mut [u8; MSG4_BYTES], server: *mut Server);
    fn shs1_server_outcome(outcome: *mut Outcome, server: *mut Server);
    fn shs1_server_clean(server: *mut Server);
}

/// Zero out all sensitive data when going out of scope.
impl<'a> Drop for Server<'a> {
    fn drop(&mut self) {
        self.clean();
    }
}