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
// Copyright 2020 Johannes Hayeß
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This is a wrapper for [`libolm`](https://git.matrix.org/git/olm/about/).
//! It exposes all original functionality, split into task oriented modules.
//!
//! This wrapper takes care of memory allocation for you, so all functions
//! in the original library exposing the buffer length of certain read/write
//! buffers (and similar functionality) are not exposed by this wrapper.
//!
//! Random number generation is also handled internally and hence there are no
//! function arguments for supplying random data.
//!
//! String arguments will vary in ownership requirements when being provided as a
//! function argument. This is because `libolm` will sometimes mutate this data and
//! sometimes won't. To avoid breaking the ownership model, full ownership is required
//! for the former case.
//!
//! All errors of the type `NOT_ENOUGH_RANDOM` and `OUTPUT_BUFFER_TOO_SMALL` from
//! `libolm` that are encountered result in a panic, as they are unrecoverably fatal.
//! Similarly the output from `libolm` is assumed to be trusted, except for encryption
//! functions. If the string output from any other function is not properly encoded
//! as UTF-8, a panic will occur as well.
//! In case a function can panic, it is annotated as such in the documentation.
//!
//! All panics can be considered unreachable, they are documented however for the
//! purpose of transparency.

pub mod account;
pub mod errors;
pub mod inbound_group_session;
pub mod outbound_group_session;
pub mod pk;
pub mod sas;
pub mod session;
pub mod utility;

use std::alloc::{GlobalAlloc as _, Layout, System};
use std::fmt;
use std::os::raw::c_void;
use std::ptr;

use getrandom as random;
use zeroize::Zeroizing;

/// A [`mod@getrandom`] wrapper that panics if the call is unsuccessful.
///
/// # Arguments
///
/// * `buffer` - The buffer that should be filled with random data.
///
/// # Panics
///
/// Panics if the operating system can't provide enough random data.
pub(crate) fn getrandom(buffer: &mut Zeroizing<Vec<u8>>) {
    random::getrandom(buffer).expect(
        "Operating system didn't provide enough random data to securely generate the private keys.",
    );
}

/// Marking these as Send is safe because nothing will modify the pointer under
/// us from the C side. Sync on the other hand is unsafe since libolm doesn't do
/// any synchronization.
unsafe impl Send for account::OlmAccount {}
unsafe impl Send for session::OlmSession {}
unsafe impl Send for sas::OlmSas {}
unsafe impl Send for pk::OlmPkDecryption {}
unsafe impl Send for pk::OlmPkEncryption {}
unsafe impl Send for pk::OlmPkSigning {}
unsafe impl Send for inbound_group_session::OlmInboundGroupSession {}
unsafe impl Send for outbound_group_session::OlmOutboundGroupSession {}

/// Used for storing the version number of libolm.
/// Solely returned by [`get_library_version()`](fn.get_library_version.html).
#[derive(Debug, PartialEq)]
pub struct OlmVersion {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
}

/// Used for setting the encryption parameter for pickling (serialisation) functions.
/// `Unencrypted` is functionally equivalent to `Encrypted{key: [].to_vec() }`, but is much more clear.
/// Pickling modes have to be equivalent for pickling and unpickling operations to succeed.
/// `Encrypted` takes ownership of `key`, in order to properly destroy it after use.
pub enum PicklingMode {
    Unencrypted,
    Encrypted { key: Vec<u8> },
}

/// Convenience function that maps `Unencrypted` to an empty key, or
/// unwraps `Encrypted`. Mostly for reducing code duplication.
pub(crate) fn convert_pickling_mode_to_key(mode: PicklingMode) -> Vec<u8> {
    match mode {
        PicklingMode::Unencrypted => Vec::new(),
        PicklingMode::Encrypted { key: x } => x,
    }
}

/// Returns the version number of the currently utilised `libolm`.
///
/// # C-API equivalent
/// `olm_get_library_version`
pub fn get_library_version() -> OlmVersion {
    let mut major = 0;
    let mut minor = 0;
    let mut patch = 0;
    let major_ptr: *mut u8 = &mut major;
    let minor_ptr: *mut u8 = &mut minor;
    let patch_ptr: *mut u8 = &mut patch;

    unsafe {
        olm_sys::olm_get_library_version(major_ptr, minor_ptr, patch_ptr);
    }

    OlmVersion {
        major,
        minor,
        patch,
    }
}

/// Helper type that just holds a one byte aligned allocation but doesn't allow
/// safe code to access its contents.
///
/// For use with the olm functions that take a buffer and return a typed pointer
/// into the same allocation after initializing it.
struct ByteBuf(*mut [u8]);

impl ByteBuf {
    fn new(size: usize) -> Self {
        assert!(size != 0);

        let layout = Layout::from_size_align(size, 1).unwrap();
        let data = unsafe { System.alloc(layout) };
        Self(ptr::slice_from_raw_parts_mut(data, size))
    }

    fn as_mut_void_ptr(&mut self) -> *mut c_void {
        self.0 as _
    }
}

impl fmt::Debug for ByteBuf {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "<ByteBuf>")
    }
}

impl Drop for ByteBuf {
    fn drop(&mut self) {
        // Should ideally use the safe <*mut [T]>::len(), but that is unstable.
        // https://github.com/rust-lang/rust/issues/71146
        let size = unsafe { (*self.0).len() };
        let layout = Layout::from_size_align(size, 1).unwrap();

        // Should ideally use <*mut [T]>::as_mut_ptr(), but that is unstable.
        // https://github.com/rust-lang/rust/issues/74265
        let data = self.0 as *mut u8;

        unsafe {
            System.dealloc(data, layout);
        }
    }
}