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

use sodiumoxide::crypto::secretbox;

/// The size of an encrypted header: The header's mac, the length of the
/// following packet, and the max of the following packet.
pub const CYPHER_HEADER_SIZE: usize = secretbox::MACBYTES + 2 + secretbox::MACBYTES;
/// The maximum allowed size of a single packet passed to `encrypt_packet`.
pub const MAX_PACKET_SIZE: u16 = 4096;
/// Same as `MAX_PACKET_SIZE`, but as a `usize`.
pub const MAX_PACKET_USIZE: usize = MAX_PACKET_SIZE as usize;

/// The result of decrypting a cypher_header. This is
/// `sodiumoxide::crypto::secretbox::MACBYTES` smaller than the encrypted header
/// since the leading mac is not needed anymore.
#[repr(C)]
#[derive(Debug)]
pub struct PlainHeader {
    packet_len: u16,
    packet_mac: [u8; secretbox::MACBYTES],
}

impl PlainHeader {
    /// Create a new PlainHeader, initially zeroed out.
    pub fn new() -> PlainHeader {
        PlainHeader {
            packet_len: 0,
            packet_mac: [0u8; secretbox::MACBYTES],
        }
    }

    /// Returns the length of the packet this header describes.
    pub fn get_packet_len(&self) -> u16 {
        self.packet_len
    }

    /// Returns the mac of the packet this header describes.
    pub fn get_packet_mac(&self) -> [u8; secretbox::MACBYTES] {
        self.packet_mac
    }

    /// Returns whether this header signals the end of the stream.
    pub fn is_final_header(&self) -> bool {
        unsafe { bs_is_final_header(self) }
    }
}

extern "C" {
    fn bs_encrypt_packet(out: *mut u8,
                         plain_packet: *const u8,
                         packet_len: u16,
                         encryption_key: *const [u8; secretbox::KEYBYTES],
                         nonce: *mut [u8; secretbox::NONCEBYTES]);

    fn bs_final_header(out: *mut [u8; CYPHER_HEADER_SIZE],
                       encryption_key: *const [u8; secretbox::KEYBYTES],
                       nonce: *const [u8; secretbox::NONCEBYTES]);


    fn bs_is_final_header(plain_header: *const PlainHeader) -> bool;

    fn bs_decrypt_header(out: *mut PlainHeader,
                         cypher_header: *const [u8; CYPHER_HEADER_SIZE],
                         decryption_key: *const [u8; secretbox::KEYBYTES],
                         nonce: *mut [u8; secretbox::NONCEBYTES])
                         -> bool;

    fn bs_decrypt_header_inplace(cypher_header: *mut [u8; CYPHER_HEADER_SIZE],
                                 decryption_key: *const [u8; secretbox::KEYBYTES],
                                 nonce: *mut [u8; secretbox::NONCEBYTES])
                                 -> bool;

    fn bs_decrypt_packet(out: *mut u8,
                         cypher_packet: *const u8,
                         plain_header: *const PlainHeader,
                         decryption_key: *const [u8; secretbox::KEYBYTES],
                         nonce: *mut [u8; secretbox::NONCEBYTES])
                         -> bool;

    fn bs_decrypt_packet_inplace(cypher_packet: *mut u8,
                                 plain_header: *const PlainHeader,
                                 decryption_key: *const [u8; secretbox::KEYBYTES],
                                 nonce: *mut [u8; secretbox::NONCEBYTES])
                                 -> bool;
}

/// Writes the encrypted header and payload for a given plaintext packet into `out`.
///
/// `out` must be a pointer to at least `CYPHER_HEADER_SIZE + packet_len` bytes.
///
/// `packet_len` must be at most MAX_PACKET_SIZE
pub unsafe fn encrypt_packet(out: *mut u8,
                             plain_packet: *const u8,
                             packet_len: u16,
                             encryption_key: &[u8; secretbox::KEYBYTES],
                             nonce: &mut [u8; secretbox::NONCEBYTES]) {
    debug_assert!(packet_len <= MAX_PACKET_SIZE);
    bs_encrypt_packet(out, plain_packet, packet_len, encryption_key, nonce);
}

/// Writes the final header that signals the end of the box stream into `out`.
pub unsafe fn final_header(out: &mut [u8; CYPHER_HEADER_SIZE],
                           encryption_key: &[u8; secretbox::KEYBYTES],
                           nonce: &[u8; secretbox::NONCEBYTES]) {
    bs_final_header(out, encryption_key, nonce);
}

/// If this returns true, it decrypts a received header into `out`. Returns false
/// if the cyper_header was invalid.
#[must_use]
pub unsafe fn decrypt_header(out: &mut PlainHeader,
                             cypher_header: &[u8; CYPHER_HEADER_SIZE],
                             decryption_key: &[u8; secretbox::KEYBYTES],
                             nonce: &mut [u8; secretbox::NONCEBYTES])
                             -> bool {
    bs_decrypt_header(out, cypher_header, decryption_key, nonce)
}

/// Same as `decrypt_header`, but writes the result into `cypher_header`. If this
/// returns true, `cypher_header` can be safely cast to a `PlainHeader`.
#[must_use]
pub unsafe fn decrypt_header_inplace(cypher_header: &mut [u8; CYPHER_HEADER_SIZE],
                                     decryption_key: &[u8; secretbox::KEYBYTES],
                                     nonce: &mut [u8; secretbox::NONCEBYTES])
                                     -> bool {
    bs_decrypt_header_inplace(cypher_header, decryption_key, nonce)
}

/// Decrypts a received packet, given a pointer to the corresponding
/// plain_header, and writes the result into `out`. Returns false on invalid
/// input, in which case the content of `out` is unspecified.
#[must_use]
pub unsafe fn decrypt_packet(out: *mut u8,
                             cypher_packet: *const u8,
                             plain_header: &PlainHeader,
                             decryption_key: &[u8; secretbox::KEYBYTES],
                             nonce: &mut [u8; secretbox::NONCEBYTES])
                             -> bool {
    bs_decrypt_packet(out, cypher_packet, plain_header, decryption_key, nonce)
}

/// Same as `decrypt_packet`, but writes the result into `cypher_packet`.
#[must_use]
pub unsafe fn decrypt_packet_inplace(cypher_packet: *mut u8,
                                     plain_header: &PlainHeader,
                                     decryption_key: &[u8; secretbox::KEYBYTES],
                                     nonce: &mut [u8; secretbox::NONCEBYTES])
                                     -> bool {
    bs_decrypt_packet_inplace(cypher_packet, plain_header, decryption_key, nonce)
}