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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
//! Functions for the core crypto.

pub use sodiumoxide::randombytes::randombytes_into;
pub use sodiumoxide::crypto::box_::*;
pub use sodiumoxide::crypto::hash::{sha256, sha512};
pub use sodiumoxide::crypto::secretbox;

pub use sodiumoxide::crypto::pwhash;

// TODO: check if `#[inline]` is actually useful

/** Run before using crypto.

Runs [`sodiumoxide::init()`](../../../sodiumoxide/fn.init.html).

Returns `Ok` on success, `Err` otherwise.

E.g.

```
use tox_crypto::crypto_init;

crypto_init().unwrap();
// second call should yield same result
crypto_init().unwrap();
```
*/
pub fn crypto_init() -> Result<(), ()> {
    ::sodiumoxide::init()
}


/// Return a random number.
pub fn random_u32() -> u32 {
    let mut array = [0; 4];
    randombytes_into(&mut array);
    // The order we use here doesn't matter, we just want random bytes.
    u32::from_be_bytes(array)
}

/// Return a random number.
pub fn random_u64() -> u64 {
    let mut array = [0; 8];
    randombytes_into(&mut array);
    // The order we use here doesn't matter, we just want random bytes.
    u64::from_be_bytes(array)
}

/// Return a random number.
#[cfg(target_pointer_width = "32")]
pub fn random_usize() -> usize {
    random_u32() as usize
}

/// Return a random number.
#[cfg(target_pointer_width = "64")]
pub fn random_usize() -> usize {
    random_u64() as usize
}

/// Return unbiased random number from `[0, limit)` interval.
pub fn random_limit_usize(limit: usize) -> usize {
    // TODO: possibly migrate to rand crate, it has more performant version
    // of this algorithm implemented with UniformSampler trait
    let cap = usize::max_value() - usize::max_value() % limit;
    loop {
        let n = random_usize();
        if n < cap {
            return n % limit;
        }
    }
}

/** Check if Tox public key `PUBLICKEYBYTES` is valid. Should be used only for
    input validation.

    Returns `true` if valid, `false` otherwise.
*/
pub fn public_key_valid(&PublicKey(ref pk): &PublicKey) -> bool {
    pk[PUBLICKEYBYTES - 1] <= 127 // Last bit of key is always zero.
}


/** Precomputes the shared key from `their_public_key` and `our_secret_key`.

    For fast encrypt/decrypt - this way we can avoid an expensive elliptic
    curve scalar multiply for each encrypt/decrypt operation.

    Use if communication is not one-time.

    `encrypt_precompute` does the shared-key generation once, so that it does
    not have to be performed on every encrypt/decrypt.

    This a wrapper for the
    [`precompute()`](../../../sodiumoxide/crypto/box_/curve25519xsalsa20poly1305/fn.precompute.html)
    function from `sodiumoxide` crate.
*/
#[inline]
pub fn encrypt_precompute(their_public_key: &PublicKey,
                          our_secret_key: &SecretKey) -> PrecomputedKey {
    precompute(their_public_key, our_secret_key)
}
// ↓ can't use, since there's no way to add additional docs
//pub use sodiumoxide::crypto::box_::precompute as encrypt_precompute;


/** Returns encrypted data from `plain`, with length of `plain + 16` due to
    padding.

    Encryption is done using precomputed key (from the public key (32 bytes)
    of receiver and the secret key of sender) and a 24 byte nonce.

    `sodiumoxide` takes care of padding the data, so the resulting encrypted
    data has length of `plain + 16`.

    A wrapper for the
    [`seal_precomputed()`](../../../sodiumoxide/crypto/box_/curve25519xsalsa20poly1305/fn.seal_precomputed.html)
    function from `sodiumoxide`.
*/
#[inline]
pub fn encrypt_data_symmetric(precomputed_key: &PrecomputedKey,
                              nonce: &Nonce,
                              plain: &[u8]) -> Vec<u8> {
    seal_precomputed(plain, nonce, precomputed_key)
}
// not using ↓ since it doesn't allow to add additional documentation
//pub use sodiumoxide::crypto::box_::seal_precomputed as encrypt_data_symmetric;


/** Returns plain data from `encrypted`, with length of `encrypted - 16` due to
    padding, or `()` if data couldn't be decrypted.

    Decryption is done using precomputed key (from the secret key of receiver
    and the public key of sender) and a 24 byte nonce.

    `sodiumoxide` takes care of removing padding from the data, so the
    resulting plain data has length of `encrypted - 16`.

    This function is a wrapper for the
    [`open_precomputed()`](../../../sodiumoxide/crypto/box_/curve25519xsalsa20poly1305/fn.open_precomputed.html)
    function from `sodiumoxide`.
*/
#[inline]
pub fn decrypt_data_symmetric(precomputed_key: &PrecomputedKey,
                              nonce: &Nonce,
                              encrypted: &[u8]) -> Result<Vec<u8>, ()> {
    open_precomputed(encrypted, nonce, precomputed_key)
}


/** Inrement given nonce by 1.

    Treats `Nonce` as BE number.

    If nonce can't be incremented (all bits are `1`), nonce is zeroed.

    *Note that behaviour of this function might change to not increment supplied
    nonces, but rather, return an increased nonce.*

    Spec: https://zetok.github.io/tox-spec#nonce-2
*/
// TODO: needs to be tested on BE arch
#[inline]
pub fn increment_nonce(nonce: &mut Nonce) {
    let Nonce(ref mut bytes) = *nonce;
    bytes.reverse(); // treat nonce as LE number
    ::sodiumoxide::utils::increment_le(bytes);
    bytes.reverse(); // treat nonce as BE number again
}

/// Inrement given nonce by number `num`.
pub fn increment_nonce_number(nonce: &mut Nonce, num: u64) {
    let Nonce(ref mut bytes) = *nonce;
    bytes.reverse(); // treat nonce as LE number
    let mut num_bytes = [0; NONCEBYTES];
    num_bytes[..8].copy_from_slice(&u64::to_le_bytes(num));
    ::sodiumoxide::utils::add_le(bytes, &num_bytes).unwrap(); // sizes are equal
    bytes.reverse(); // treat nonce as BE number again
}

/// Convert `PublicKey` to sha256 `Digest` type.
pub fn pk_as_digest(pk: PublicKey) -> sha256::Digest {
    // can not fail since PublicKey has the same length as sha256 Digest
    sha256::Digest::from_slice(pk.as_ref()).unwrap()
}

/// Convert sha256 `Digest` to `PublicKey` type.
pub fn digest_as_pk(d: sha256::Digest) -> PublicKey {
    // can not fail since sha256 Digest has the same length as PublicKey
    PublicKey::from_slice(d.as_ref()).unwrap()
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    // test comparing empty keys
    // testing since it would appear that sodiumoxide doesn't do testing for it
    fn public_key_cmp_test_empty() {
        let alice_publickey = PublicKey([0; PUBLICKEYBYTES]);
        let bob_publickey = PublicKey([0; PUBLICKEYBYTES]);

        assert_eq!(alice_publickey.eq(&bob_publickey), true);
        assert_eq!(bob_publickey.eq(&alice_publickey), true);
    }

    #[test]
    // test comparing random public keys
    // testing since it would appear that sodiumoxide doesn't do testing for it
    fn public_key_cmp_test_random() {
        crypto_init().unwrap();
        let (alice_publickey, _alice_secretkey) = gen_keypair();
        let (bob_publickey, _bob_secretkey) = gen_keypair();

        assert_eq!(alice_publickey.eq(&bob_publickey), false);
        assert_eq!(bob_publickey.eq(&alice_publickey), false);

        assert_eq!(alice_publickey.eq(&alice_publickey), true);
        assert_eq!(bob_publickey.eq(&bob_publickey), true);
    }


    #[test]
    fn random_u32_test() {
        crypto_init().unwrap();
        let a = random_u32();
        let b = random_u32();
        assert_ne!(a, 0);
        assert_ne!(b, 0);
        // The probability to fail equals 5.4*10^-20
        assert_ne!(a, b);
    }


    #[test]
    fn random_u64_test() {
        crypto_init().unwrap();
        let a = random_u64();
        let b = random_u64();
        assert_ne!(a, 0);
        assert_ne!(b, 0);
        // The probability to fail equals 2.9*10^-39
        assert_ne!(a, b);
    }

    #[test]
    fn random_usize_test() {
        crypto_init().unwrap();
        let a = random_usize();
        let b = random_usize();
        assert_ne!(a, 0);
        assert_ne!(b, 0);
        // The probability to fail equals 2.9*10^-39
        assert_ne!(a, b);
    }

    #[test]
    fn random_limit_usize_test() {
        crypto_init().unwrap();
        let n = random_limit_usize(7);
        assert!(n < 7);
    }

    #[test]
    fn public_key_valid_test() {
        crypto_init().unwrap();
        let (pk, _) = gen_keypair();
        assert!(public_key_valid(&pk));

        assert!(public_key_valid(&PublicKey([0; PUBLICKEYBYTES]))); // 0
        assert!(public_key_valid(&PublicKey([0b01_11_11_11; PUBLICKEYBYTES]))); // 127
        assert!(!public_key_valid(&PublicKey([0b10_00_00_00; PUBLICKEYBYTES]))); // 128
        assert!(!public_key_valid(&PublicKey([0b11_11_11_11; PUBLICKEYBYTES]))); // 255
    }


    #[test]
    // test uses "bare" functions provided by `sodiumoxide`, with an exception
    // of the tested function
    fn encrypt_precompute_test() {
        crypto_init().unwrap();
        let (alice_pk, alice_sk) = gen_keypair();
        let (bob_pk, bob_sk) = gen_keypair();

        let alice_plaintext = b"Hi, Bob.";
        let alice_precomputed_key = encrypt_precompute(&bob_pk, &alice_sk);

        let nonce = gen_nonce();

        let ciphertext = seal_precomputed(alice_plaintext, &nonce, &alice_precomputed_key);

        let bob_precomputed_key = encrypt_precompute(&alice_pk, &bob_sk);
        let bob_plaintext = open_precomputed(&ciphertext, &nonce, &bob_precomputed_key).unwrap();

        assert_eq!(alice_plaintext, &bob_plaintext[..]);
    }


    #[test]
    // test uses "bare" functions provided by `sodiumoxide`, with an "exception"
    // of the tested function
    fn encrypt_data_symmetric_test() {
        crypto_init().unwrap();
        let (alice_pk, alice_sk) = gen_keypair();
        let (bob_pk, bob_sk) = gen_keypair();

        let alice_plain = b"Hi, Bob.";

        let precomputed_key = precompute(&bob_pk, &alice_sk);
        let nonce = gen_nonce();

        let ciphertext = encrypt_data_symmetric(&precomputed_key, &nonce, alice_plain);

        let bob_plain = open(&ciphertext, &nonce, &alice_pk, &bob_sk).unwrap();

        assert_eq!(alice_plain, &bob_plain[..]);
    }

    /* TODO: test for pubkey/skey/nonce being all `0`s, which would produce
       ciphertext that should be compared to already known result of this
       computation. This way it would be ensured that cipher algorithm is
       actually working as it should.
       There should be also some additional variations of this test, with different
       pkey/skey/nonce values that would produce known ciphertext.

       Also, similar test for decrypting.
    */


    #[test]
    // test uses "bare" functions provided by `sodiumoxide`, with an exception
    // of the tested function
    fn decrypt_data_symmetric_test() {
        crypto_init().unwrap();
        let (alice_pk, alice_sk) = gen_keypair();
        let (bob_pk, bob_sk) = gen_keypair();

        let alice_plain = b"Hi, Bob.";

        let precomputed_key = precompute(&alice_pk, &bob_sk);
        let nonce = gen_nonce();

        let ciphertext = seal(alice_plain, &nonce, &bob_pk, &alice_sk);

        let bob_plain = decrypt_data_symmetric(&precomputed_key, &nonce, &ciphertext).unwrap();

        assert_eq!(alice_plain, &bob_plain[..]);
    }

    #[test]
    fn increment_nonce_test_zero_plus_one() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 1]);

        let mut nonce = Nonce([0; NONCEBYTES]);
        increment_nonce(&mut nonce);
        assert_eq!(nonce, cmp_nonce);
    }

    #[test]
    fn increment_nonce_test_0xf_plus_one() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0x10]);

        let mut nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0xf]);
        increment_nonce(&mut nonce);
        assert_eq!(nonce, cmp_nonce);
    }

    #[test]
    fn increment_nonce_test_0xff_plus_one() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 1, 0]);

        let mut nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0xff]);
        increment_nonce(&mut nonce);
        assert_eq!(nonce, cmp_nonce);
    }

    #[test]
    fn increment_nonce_test_0xff_max() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0; NONCEBYTES]);
        let mut nonce = Nonce([0xff; NONCEBYTES]);
        increment_nonce(&mut nonce);
        assert_eq!(cmp_nonce, nonce);
    }

    #[test]
    fn increment_nonce_test_random() {
        crypto_init().unwrap();
        let mut nonce = gen_nonce();
        let cmp_nonce = nonce;
        increment_nonce(&mut nonce);
        assert_ne!(nonce, cmp_nonce);
    }

    // increment_nonce_number()

    #[test]
    fn increment_nonce_number_test_zero_plus_0xff00() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0xff, 0]);
        let mut nonce = Nonce([0; NONCEBYTES]);

        increment_nonce_number(&mut nonce, 0xff00);
        assert_eq!(nonce, cmp_nonce);
    }

    #[test]
    fn increment_nonce_number_test_0xff0000_plus_0x011000() {
        crypto_init().unwrap();
        let cmp_nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 1, 0, 0x10, 0]);

        let mut nonce = Nonce([0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0xff, 0, 0]);

        increment_nonce_number(&mut nonce, 0x01_10_00);
        assert_eq!(nonce, cmp_nonce);
    }
}