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
//! Storj DCS Encryption key.
use crate::uplink_c::Ensurer;
use crate::{helpers, Error, Result};
use uplink_sys as ulksys;
/// Represents a key for encrypting and decrypting data.
#[derive(Debug)]
pub struct EncryptionKey {
/// The encryption key type of the FFI that an instance of this struct represents and guards its
/// lifetime until the instances drops.
/// It's an encryption result because it's the one that holds the encryption
/// key and allows to free its memory.
inner: ulksys::UplinkEncryptionKeyResult,
}
impl EncryptionKey {
/// Derives a salted encryption key for `passphrase` using the passed salt.
///
/// It's mostly useful for implementing multitenancy in a single app bucket.
/// See [Multitenancy in a Single Application Bucket](https://pkg.go.dev/storj.io/uplink#hdr-Multitenancy_in_a_Single_Application_Bucket)
/// section in the original Uplink library.
pub fn derive(passphrase: &str, salt: &[u8]) -> Result<Self> {
use std::ffi::c_void;
use std::os::raw::c_char;
let passphrase = helpers::cstring_from_str_fn_arg("passphrase", passphrase)?;
// SAFETY: we trust that the FFI is safe creating an instance of its own types.
// Note that we get a non-mutable pointer to the `salt` argument but we apply a conversion
// to to mutable rather than using the `as_mut_ptr` method because otherwise it will require
// the `salt` parameter to be mutable but the FFI function doesn't mutate it despite that
// the function parameters is specified as mutable.
let uc_res = unsafe {
ulksys::uplink_derive_encryption_key(
passphrase.as_ptr() as *mut c_char,
salt.as_ptr() as *mut c_void,
salt.len(),
)
};
uc_res.ensure();
if let Some(err) = Error::new_uplink(uc_res.error) {
// SAFETY: we trust the FFI is safe freeing the memory of a valid pointer.
unsafe { ulksys::uplink_free_encryption_key_result(uc_res) };
return Err(err);
}
Ok(Self { inner: uc_res })
}
/// Returns the FFI representation of this encryption key.
pub(crate) fn as_ffi_encryption_key(&self) -> *mut ulksys::UplinkEncryptionKey {
self.inner.encryption_key
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::error;
use crate::Error;
#[test]
fn test_derive_invalid_argument() {
if let Error::InvalidArguments(error::Args { names, msg }) =
EncryptionKey::derive("pass\0phrase", &[0])
.expect_err("when passing a passphrase with NULL bytes")
{
assert_eq!(names, "passphrase", "invalid error argument name");
assert_eq!(
msg, "cannot contains null bytes (0 byte). Null byte found at 4",
"invalid error argument message"
);
} else {
panic!("expected an invalid argument error");
}
}
}