#![allow(unsafe_code)]
use crate::client::{MDataInfo, SafeKey};
use crate::crypto::{shared_box, shared_secretbox};
use crate::ffi::ipc::resp as ffi;
use crate::ipc::req::{
container_perms_from_repr_c, container_perms_into_repr_c, permission_set_clone_from_repr_c,
permission_set_into_repr_c, ContainerPermissions,
};
use crate::ipc::IpcError;
use crate::utils::{symmetric_encrypt, SymEncKey, SymEncNonce, SYM_ENC_NONCE_LEN};
use crate::CoreError;
use bincode::{deserialize, serialize};
use ffi_utils::{vec_clone_from_raw_parts, vec_into_raw_parts, ReprC, StringError};
use rand::thread_rng;
use safe_nd::{
AppFullId, ClientPublicId, MDataAddress, MDataPermissionSet, MDataSeqValue, PublicKey, XorName,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::{CString, NulError};
use std::ptr;
use std::slice;
use tiny_keccak::sha3_256;
use unwrap::unwrap;
#[no_mangle]
pub static METADATA_KEY: &[u8] = b"_metadata";
#[no_mangle]
pub static METADATA_KEY_LEN: usize = 9;
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct AppKeys {
pub app_full_id: AppFullId,
pub enc_key: shared_secretbox::Key,
pub enc_public_key: threshold_crypto::PublicKey,
pub enc_secret_key: shared_box::SecretKey,
}
impl AppKeys {
pub fn new(client_public_id: ClientPublicId) -> Self {
let (enc_public_key, enc_secret_key) = shared_box::gen_keypair();
let app_full_id = AppFullId::new_bls(&mut thread_rng(), client_public_id);
Self {
app_full_id,
enc_key: shared_secretbox::gen_key(),
enc_public_key,
enc_secret_key,
}
}
pub fn app_safe_key(&self) -> SafeKey {
SafeKey::app(self.app_full_id.clone())
}
pub fn public_key(&self) -> PublicKey {
*self.app_full_id.public_id().public_key()
}
pub fn into_repr_c(self) -> Result<ffi::AppKeys, IpcError> {
let Self {
app_full_id,
enc_key,
enc_public_key,
enc_secret_key,
} = self;
let app_full_id = serialize(&app_full_id)?;
let (full_id, full_id_len) = vec_into_raw_parts(app_full_id);
let asym_sk = serialize(&enc_secret_key)?;
let (sk, enc_sk_len) = vec_into_raw_parts(asym_sk);
Ok(ffi::AppKeys {
full_id,
full_id_len,
enc_key: *enc_key,
enc_public_key: enc_public_key.to_bytes(),
enc_secret_key: sk,
enc_secret_key_len: enc_sk_len,
})
}
}
impl ReprC for AppKeys {
type C = *const ffi::AppKeys;
type Error = IpcError;
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let raw_full_id = vec_clone_from_raw_parts((*repr_c).full_id, (*repr_c).full_id_len);
let app_full_id = deserialize(&raw_full_id)?;
let raw_sk =
vec_clone_from_raw_parts((*repr_c).enc_secret_key, (*repr_c).enc_secret_key_len);
let enc_sk = deserialize(&raw_sk)?;
Ok(Self {
app_full_id,
enc_key: shared_secretbox::Key::from_raw(&(*repr_c).enc_key),
enc_public_key: threshold_crypto::PublicKey::from_bytes((*repr_c).enc_public_key)?,
enc_secret_key: enc_sk,
})
}
}
pub type AccessContainerEntry = HashMap<String, (MDataInfo, ContainerPermissions)>;
pub fn access_container_entry_into_repr_c(
entry: AccessContainerEntry,
) -> Result<ffi::AccessContainerEntry, NulError> {
let mut vec = Vec::with_capacity(entry.len());
for (name, (mdata_info, permissions)) in entry {
vec.push(ffi::ContainerInfo {
name: CString::new(name)?.into_raw(),
mdata_info: mdata_info.into_repr_c(),
permissions: container_perms_into_repr_c(&permissions),
})
}
let (containers, containers_len) = vec_into_raw_parts(vec);
Ok(ffi::AccessContainerEntry {
containers,
containers_len,
})
}
pub unsafe fn access_container_entry_clone_from_repr_c(
entry: *const ffi::AccessContainerEntry,
) -> Result<AccessContainerEntry, IpcError> {
let input = slice::from_raw_parts((*entry).containers, (*entry).containers_len);
let mut output = AccessContainerEntry::with_capacity(input.len());
for container in input {
let name = String::clone_from_repr_c(container.name)?;
let mdata_info = MDataInfo::clone_from_repr_c(&container.mdata_info)?;
let permissions = container_perms_from_repr_c(container.permissions)?;
let _ = output.insert(name, (mdata_info, permissions));
}
Ok(output)
}
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct AccessContInfo {
pub id: XorName,
pub tag: u64,
pub nonce: SymEncNonce,
}
impl AccessContInfo {
pub fn into_repr_c(self) -> ffi::AccessContInfo {
let Self { id, tag, nonce } = self;
ffi::AccessContInfo {
id: id.0,
tag,
nonce,
}
}
pub fn into_mdata_info(self, enc_key: shared_secretbox::Key) -> MDataInfo {
MDataInfo::new_private(
MDataAddress::Seq {
name: self.id,
tag: self.tag,
},
(enc_key, self.nonce),
)
}
pub fn from_mdata_info(md: &MDataInfo) -> Result<Self, IpcError> {
if let Some((_, nonce)) = md.enc_info {
Ok(Self {
id: md.name(),
tag: md.type_tag(),
nonce,
})
} else {
Err(IpcError::Unexpected(
"MDataInfo doesn't contain nonce".to_owned(),
))
}
}
}
impl ReprC for AccessContInfo {
type C = ffi::AccessContInfo;
type Error = IpcError;
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
Ok(Self {
id: XorName(repr_c.id),
tag: repr_c.tag,
nonce: repr_c.nonce,
})
}
}
pub fn access_container_enc_key(
app_id: &str,
app_enc_key: &SymEncKey,
access_container_nonce: &SymEncNonce,
) -> Result<Vec<u8>, CoreError> {
let key = app_id.as_bytes();
let mut key_pt = key.to_vec();
key_pt.extend_from_slice(&access_container_nonce[..]);
let key_nonce: SymEncNonce = unwrap!(sha3_256(&key_pt)[..SYM_ENC_NONCE_LEN].try_into());
let cipher_text = symmetric_encrypt(key, app_enc_key, Some(&key_nonce))?;
Ok(cipher_text)
}
#[derive(Debug)]
pub struct AppAccess {
pub sign_key: PublicKey,
pub permissions: MDataPermissionSet,
pub name: Option<String>,
pub app_id: Option<String>,
}
impl AppAccess {
pub fn into_repr_c(self) -> Result<ffi::AppAccess, IpcError> {
let Self {
sign_key,
permissions,
name,
app_id,
} = self;
let name = match name {
Some(name) => CString::new(name).map_err(StringError::from)?.into_raw(),
None => ptr::null(),
};
let app_id = match app_id {
Some(app_id) => CString::new(app_id).map_err(StringError::from)?.into_raw(),
None => ptr::null(),
};
let sign_key = match sign_key {
PublicKey::Bls(sec_key) => sec_key.to_bytes(),
_ => return Err(IpcError::from("Unsupported key type")),
};
Ok(ffi::AppAccess {
sign_key,
permissions: permission_set_into_repr_c(permissions),
name,
app_id,
})
}
}
impl ReprC for AppAccess {
type C = *const ffi::AppAccess;
type Error = IpcError;
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let ffi::AppAccess {
sign_key,
permissions,
name,
app_id,
} = *repr_c;
Ok(Self {
sign_key: PublicKey::from(
threshold_crypto::PublicKey::from_bytes(sign_key)
.map_err(|_| IpcError::EncodeDecodeError)?,
),
permissions: permission_set_clone_from_repr_c(permissions)?,
name: if name.is_null() {
None
} else {
Some(String::clone_from_repr_c(name)?)
},
app_id: if name.is_null() {
None
} else {
Some(String::clone_from_repr_c(app_id)?)
},
})
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct UserMetadata {
pub name: Option<String>,
pub description: Option<String>,
}
impl UserMetadata {
pub fn into_md_response(
self,
xor_name: XorName,
type_tag: u64,
) -> Result<ffi::MetadataResponse, NulError> {
Ok(ffi::MetadataResponse {
name: match self.name {
Some(name) => CString::new(name)?.into_raw(),
None => ptr::null(),
},
description: match self.description {
Some(description) => CString::new(description)?.into_raw(),
None => ptr::null(),
},
xor_name: xor_name.0,
type_tag,
})
}
}
impl ReprC for UserMetadata {
type C = *const ffi::MetadataResponse;
type Error = IpcError;
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let ffi::MetadataResponse {
name, description, ..
} = *repr_c;
Ok(Self {
name: if name.is_null() {
None
} else {
Some(String::clone_from_repr_c(name)?)
},
description: if description.is_null() {
None
} else {
Some(String::clone_from_repr_c(description)?)
},
})
}
}
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Debug)]
pub struct MDataKey(
pub Vec<u8>,
);
impl MDataKey {
pub fn from_bytes(key: &[u8]) -> Self {
Self(key.into())
}
pub fn into_repr_c(self) -> ffi::MDataKey {
let (key, key_len) = vec_into_raw_parts(self.0);
ffi::MDataKey { key, key_len }
}
}
impl ReprC for MDataKey {
type C = *const ffi::MDataKey;
type Error = ();
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let ffi::MDataKey { key, key_len, .. } = *repr_c;
let key = vec_clone_from_raw_parts(key, key_len);
Ok(Self(key))
}
}
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Debug)]
pub struct MDataValue {
pub content: Vec<u8>,
pub entry_version: u64,
}
impl MDataValue {
pub fn from_routing(value: MDataSeqValue) -> Self {
Self {
content: value.data,
entry_version: value.version,
}
}
pub fn into_repr_c(self) -> ffi::MDataValue {
let (content, content_len) = vec_into_raw_parts(self.content);
ffi::MDataValue {
content,
content_len,
entry_version: self.entry_version,
}
}
}
impl ReprC for MDataValue {
type C = *const ffi::MDataValue;
type Error = ();
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let ffi::MDataValue {
content,
content_len,
entry_version,
..
} = *repr_c;
let content = vec_clone_from_raw_parts(content, content_len);
Ok(Self {
content,
entry_version,
})
}
}
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Debug)]
pub struct MDataEntry {
pub key: MDataKey,
pub value: MDataValue,
}
impl MDataEntry {
pub fn into_repr_c(self) -> ffi::MDataEntry {
ffi::MDataEntry {
key: self.key.into_repr_c(),
value: self.value.into_repr_c(),
}
}
}
impl ReprC for MDataEntry {
type C = *const ffi::MDataEntry;
type Error = ();
unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
let ffi::MDataEntry { ref key, ref value } = *repr_c;
Ok(Self {
key: MDataKey::clone_from_repr_c(key)?,
value: MDataValue::clone_from_repr_c(value)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils;
use crate::utils::test_utils::gen_client_id;
use safe_nd::{XorName, XOR_NAME_LEN};
use unwrap::unwrap;
#[test]
fn app_keys() {
let client_id = gen_client_id();
let ak = AppKeys::new(client_id.public_id().clone());
let AppKeys {
enc_key,
enc_public_key: enc_pk,
enc_secret_key: enc_sk,
app_full_id,
} = ak.clone();
let ffi_ak: ffi::AppKeys = unwrap!(ak.into_repr_c());
assert_eq!(
ffi_ak.enc_key.iter().collect::<Vec<_>>(),
(*enc_key).iter().collect::<Vec<_>>()
);
assert_eq!(
ffi_ak.enc_public_key.iter().cloned().collect::<Vec<_>>(),
enc_pk.to_bytes().to_vec()
);
let sk_raw =
unsafe { vec_clone_from_raw_parts(ffi_ak.enc_secret_key, ffi_ak.enc_secret_key_len) };
let sk = unwrap!(serialize(&enc_sk));
assert_eq!(sk_raw, sk);
let ak = unsafe { unwrap!(AppKeys::clone_from_repr_c(&ffi_ak)) };
assert_eq!(ak.enc_key, enc_key);
assert_eq!(ak.enc_public_key, enc_pk);
assert_eq!(ak.enc_secret_key, enc_sk);
assert_eq!(ak.app_full_id, app_full_id);
}
#[test]
fn access_container_mdata_info() {
let (key, nonce) = (shared_secretbox::gen_key(), utils::generate_nonce());
let a = AccessContInfo {
id: XorName([2; XOR_NAME_LEN]),
tag: 681,
nonce,
};
let md = a.clone().into_mdata_info(key.clone());
let a2 = AccessContInfo::from_mdata_info(&md).unwrap();
assert_eq!(a, a2);
let md2 = a.into_mdata_info(key);
assert_eq!(md, md2);
}
#[test]
fn access_container_ffi() {
let nonce = utils::generate_nonce();
let a = AccessContInfo {
id: XorName([2; XOR_NAME_LEN]),
tag: 681,
nonce,
};
let ffi = a.into_repr_c();
assert_eq!(ffi.id.iter().sum::<u8>() as usize, 2 * XOR_NAME_LEN);
assert_eq!(ffi.tag, 681);
assert_eq!(
ffi.nonce.iter().collect::<Vec<_>>(),
nonce.iter().collect::<Vec<_>>()
);
let a = unsafe { unwrap!(AccessContInfo::clone_from_repr_c(ffi)) };
assert_eq!(a.id.0.iter().sum::<u8>() as usize, 2 * XOR_NAME_LEN);
assert_eq!(a.tag, 681);
assert_eq!(a.nonce, nonce);
}
}