Skip to content

Commit

Permalink
feat: [sc-44563] Wrap Encryption related APIs (#170)
Browse files Browse the repository at this point in the history
* Add Encryption enum

* Enable encryption on arrays
  • Loading branch information
rroelke authored Sep 27, 2024
1 parent 862bae1 commit 89dee14
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 6 deletions.
199 changes: 199 additions & 0 deletions tiledb/api/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::convert::TryFrom;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::num::NonZeroU32;
use std::ops::Deref;
use std::str::FromStr;

use anyhow::anyhow;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -169,6 +170,89 @@ impl TryFrom<ffi::tiledb_layout_t> for CellOrder {
}
}

/// Method of encryption.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Encryption {
Unencrypted,
Aes256Gcm,
}

impl Encryption {
/// Returns the corresponding C API constant.
pub(crate) fn capi_enum(&self) -> ffi::tiledb_encryption_type_t {
match *self {
Self::Unencrypted => {
ffi::tiledb_encryption_type_t_TILEDB_NO_ENCRYPTION
}
Self::Aes256Gcm => ffi::tiledb_encryption_type_t_TILEDB_AES_256_GCM,
}
}
}

impl Display for Encryption {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let c_encryption = self.capi_enum();
let mut c_str = out_ptr!();

let c_ret = unsafe {
ffi::tiledb_encryption_type_to_str(
c_encryption,
&mut c_str as *mut *const ::std::os::raw::c_char,
)
};
if c_ret == ffi::TILEDB_OK {
let s =
unsafe { std::ffi::CStr::from_ptr(c_str) }.to_string_lossy();
write!(f, "{}", s)
} else {
write!(f, "<Internal error>")
}
}
}

impl FromStr for Encryption {
type Err = crate::error::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let c_value = cstring!(s);
let mut c_encryption = out_ptr!();

let c_ret = unsafe {
ffi::tiledb_encryption_type_from_str(
c_value.as_ptr(),
&mut c_encryption as *mut ffi::tiledb_encryption_type_t,
)
};
if c_ret == ffi::TILEDB_OK {
Self::try_from(c_encryption)
} else {
Err(Error::InvalidArgument(anyhow!(format!(
"Invalid encryption type: {}",
s
))))
}
}
}

impl TryFrom<ffi::tiledb_encryption_type_t> for Encryption {
type Error = crate::error::Error;

fn try_from(value: ffi::tiledb_encryption_type_t) -> TileDBResult<Self> {
match value {
ffi::tiledb_encryption_type_t_TILEDB_NO_ENCRYPTION => {
Ok(Self::Unencrypted)
}
ffi::tiledb_encryption_type_t_TILEDB_AES_256_GCM => {
Ok(Self::Aes256Gcm)
}
_ => Err(Self::Error::LibTileDB(format!(
"Invalid encryption type: {}",
value
))),
}
}
}

pub enum RawArray {
Owned(*mut ffi::tiledb_array_t),
}
Expand Down Expand Up @@ -238,6 +322,25 @@ impl Array {
))
}

/// Returns the manner in which the array located at `uri` is encrypted.
pub fn encryption<S>(context: &Context, uri: S) -> TileDBResult<Encryption>
where
S: AsRef<str>,
{
let c_uri = cstring!(uri.as_ref());
let mut c_encryption_type: ffi::tiledb_encryption_type_t = out_ptr!();

context.capi_call(|c_ctx| unsafe {
ffi::tiledb_array_encryption_type(
c_ctx,
c_uri.as_ptr(),
&mut c_encryption_type as *mut ffi::tiledb_encryption_type_t,
)
})?;

Encryption::try_from(c_encryption_type)
}

/// Opens the array located at `uri` for queries of type `mode` using default configurations.
pub fn open<S>(context: &Context, uri: S, mode: Mode) -> TileDBResult<Self>
where
Expand Down Expand Up @@ -867,6 +970,17 @@ impl ArrayOpener {
})
}

/// Sets configuration options for this array.
pub fn config(self, config: &Config) -> TileDBResult<Self> {
let c_array = **self.array.capi();
let c_config = config.capi();

self.array.capi_call(|c_context| unsafe {
ffi::tiledb_array_set_config(c_context, c_array, c_config)
})?;
Ok(self)
}

/// Configures the start timestamp for an array.
/// The start and end timestamps determine the set of fragments
/// which will be loaded and used for queries.
Expand Down Expand Up @@ -921,6 +1035,7 @@ pub mod tests {

use super::*;
use crate::array::dimension::DimensionConstraints;
use crate::config::CommonOption;
use crate::metadata::Value;
use crate::query::{
Query, QueryBuilder, QueryLayout, QueryType, WriteBuilder,
Expand Down Expand Up @@ -1354,4 +1469,88 @@ pub mod tests {

Ok(())
}

#[test]
fn encryption_type_str() {
assert_eq!(
Encryption::Unencrypted,
Encryption::from_str(&Encryption::Unencrypted.to_string()).unwrap()
);
assert_eq!(
Encryption::Aes256Gcm,
Encryption::from_str(&Encryption::Aes256Gcm.to_string()).unwrap()
);
}

#[test]
fn encryption_type_capi() {
assert_eq!(
Encryption::Unencrypted,
Encryption::try_from(Encryption::Unencrypted.capi_enum()).unwrap()
);
assert_eq!(
Encryption::Aes256Gcm,
Encryption::try_from(Encryption::Aes256Gcm.capi_enum()).unwrap()
);
}

#[test]
fn encrypted_array() -> TileDBResult<()> {
let test_uri = tiledb_test_utils::get_uri_generator()
.map_err(|e| Error::Other(e.to_string()))?;

let key = "0123456789abcdeF0123456789abcdeF";
let key_config =
CommonOption::Aes256GcmEncryptionKey(key.as_bytes().to_vec());

// create array and try opening array using the same configured context
let uri = {
let context = {
let mut config = Config::new()?;
config.set_common_option(key_config.clone())?;

Context::from_config(&config)
}?;

let uri = create_quickstart_dense(&test_uri, &context)?;

assert_eq!(
Encryption::Aes256Gcm,
Array::encryption(&context, &uri)?
);

// re-using the configured context should be fine
let _ = ArrayOpener::new(&context, &uri, Mode::Read)?.open()?;
let _ = ArrayOpener::new(&context, &uri, Mode::Write)?.open()?;

uri
};

// try opening from an un-configured context and it should fail
{
let context = Context::new()?;

let open_read = Array::open(&context, &uri, Mode::Read);
assert!(matches!(open_read, Err(Error::LibTileDB(_))));

let open_write = Array::open(&context, &uri, Mode::Read);
assert!(matches!(open_write, Err(Error::LibTileDB(_))));
}

// try opening from an un-configured context with the right array config should succeed
{
let context = Context::new()?;
let array_config =
Config::new()?.with_common_option(key_config.clone())?;

let _ = ArrayOpener::new(&context, &uri, Mode::Read)?
.config(&array_config)?
.open()?;
let _ = ArrayOpener::new(&context, &uri, Mode::Write)?
.config(&array_config)?
.open()?;
}

Ok(())
}
}
49 changes: 44 additions & 5 deletions tiledb/api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ impl Config {
Self { raw }
}

pub fn set(&mut self, key: &str, val: &str) -> TileDBResult<()> {
let c_key =
std::ffi::CString::new(key).expect("Error creating CString");
let c_val =
std::ffi::CString::new(val).expect("Error creating CString");
pub fn set<B>(&mut self, key: &str, val: B) -> TileDBResult<()>
where
B: AsRef<[u8]>,
{
let c_key = cstring!(key);
let c_val = cstring!(val.as_ref());

let mut c_err: *mut ffi::tiledb_error_t = std::ptr::null_mut();
let res = unsafe {
ffi::tiledb_config_set(
Expand All @@ -98,6 +100,15 @@ impl Config {
}
}

pub fn with<B>(self, key: &str, val: B) -> TileDBResult<Self>
where
B: AsRef<[u8]>,
{
let mut s = self;
s.set(key, val)?;
Ok(s)
}

pub fn get(&self, key: &str) -> TileDBResult<Option<String>> {
let c_key =
std::ffi::CString::new(key).expect("Error creating CString");
Expand All @@ -121,6 +132,16 @@ impl Config {
}
}

pub fn set_common_option(&mut self, opt: CommonOption) -> TileDBResult<()> {
opt.apply(self)
}

pub fn with_common_option(self, opt: CommonOption) -> TileDBResult<Self> {
let mut s = self;
s.set_common_option(opt)?;
Ok(s)
}

pub fn unset(&mut self, key: &str) -> TileDBResult<()> {
let c_key =
std::ffi::CString::new(key).expect("Error creating CString");
Expand Down Expand Up @@ -265,6 +286,24 @@ impl<'cfg> Iterator for ConfigIterator<'cfg> {
}
}

/// Convenience for setting some of the more commonly-used configuration options.
#[derive(Clone, Debug)]
pub enum CommonOption {
/// Sets an AES256GCM encryption key.
Aes256GcmEncryptionKey(Vec<u8>),
}

impl CommonOption {
fn apply(&self, config: &mut Config) -> TileDBResult<()> {
match self {
Self::Aes256GcmEncryptionKey(ref key) => {
config.set("sm.encryption_type", "AES_256_GCM")?;
config.set("sm.encryption_key", key)
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 7 additions & 1 deletion tiledb/sys/src/array.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::capi_enum::tiledb_query_type_t;
use crate::capi_enum::{tiledb_encryption_type_t, tiledb_query_type_t};
use crate::tiledb_datatype_t;
use crate::types::{
capi_return_t, tiledb_array_schema_t, tiledb_array_t, tiledb_config_t,
Expand Down Expand Up @@ -60,6 +60,12 @@ extern "C" {
array_schema: *mut *mut tiledb_array_schema_t,
) -> capi_return_t;

pub fn tiledb_array_encryption_type(
ctx: *mut tiledb_ctx_t,
array_uri: *const ::std::os::raw::c_char,
encryption_type: *mut tiledb_encryption_type_t,
) -> capi_return_t;

pub fn tiledb_array_put_metadata(
ctx: *mut tiledb_ctx_t,
array: *mut tiledb_array_t,
Expand Down
14 changes: 14 additions & 0 deletions tiledb/sys/src/encryption.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::capi_enum::tiledb_encryption_type_t;
use crate::capi_return_t;

extern "C" {
pub fn tiledb_encryption_type_to_str(
encryption_type: tiledb_encryption_type_t,
str_: *mut *const ::std::os::raw::c_char,
) -> capi_return_t;

pub fn tiledb_encryption_type_from_str(
str_: *const ::std::os::raw::c_char,
encryption_type: *mut tiledb_encryption_type_t,
) -> capi_return_t;
}
2 changes: 2 additions & 0 deletions tiledb/sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod context;
mod datatype;
mod dimension;
mod domain;
mod encryption;
mod enumeration;
mod error;
mod filesystem;
Expand Down Expand Up @@ -44,6 +45,7 @@ pub use context::*;
pub use datatype::*;
pub use dimension::*;
pub use domain::*;
pub use encryption::*;
pub use enumeration::*;
pub use error::*;
pub use filesystem::*;
Expand Down

0 comments on commit 89dee14

Please sign in to comment.