use rlua::{UserData, UserDataMethods, Error as LuaError, Lua, Value};
use sodiumoxide::crypto::box_;
use base64;
use std::cell::Ref;
use crate::error::Error;
pub struct KeyPair {
secret: box_::SecretKey,
public: box_::PublicKey,
}
pub struct Nonce(box_::Nonce);
pub fn new_keypair(_lua: &Lua, _: ()) -> Result<KeyPair, LuaError> {
let (pk, sk) = box_::gen_keypair();
Ok(KeyPair {
secret: sk,
public: pk,
})
}
pub fn load_keypair(_lua: &Lua, (secret_key_base64, public_key_base64): (String, String)) -> Result<KeyPair, LuaError> {
let secret = base64::decode(&secret_key_base64).map_err(LuaError::external)?;
let public = base64::decode(&public_key_base64).map_err(LuaError::external)?;
let keypair = KeyPair {
secret: box_::SecretKey::from_slice(&secret).ok_or(LuaError::external(Error::InvalidKeys))?,
public: box_::PublicKey::from_slice(&public).ok_or(LuaError::external(Error::InvalidKeys))?
};
Ok(keypair)
}
pub fn load_nonce(_lua: &Lua, nonce_str: String) -> Result<Nonce, LuaError> {
let nonce_bytes = base64::decode(&nonce_str).map_err(LuaError::external)?;
box_::Nonce::from_slice(&nonce_bytes).map(|n| Nonce(n)).ok_or(LuaError::external(Error::InvalidNonce))
}
pub fn new_nonce(_lua: &Lua, _: ()) -> Result<Nonce, LuaError> {
let nonce = box_::gen_nonce();
Ok(Nonce(nonce))
}
fn get_nonce<'a>(val: &'a Option<Value<'a>>) -> Result<Ref<'a, Nonce>, LuaError> {
let user_data = match val {
Some(Value::UserData(data)) => Ok(data),
_ => Err(LuaError::external(Error::InvalidNonceObject))
}?;
user_data.borrow::<Nonce>().map_err(LuaError::external)
}
pub fn seal(_lua: &Lua, this: &KeyPair, (msg, nonce_value): (String, Option<Value>)) -> Result<String, LuaError> {
let nonce = get_nonce(&nonce_value).map_err(LuaError::external)?;
let cipher = box_::seal(msg.as_bytes(), &nonce.0, &this.public, &this.secret);
Ok(base64::encode(&cipher))
}
pub fn open(_lua: &Lua, this: &KeyPair, (msg, nonce_value): (String, Option<Value>)) -> Result<String, LuaError> {
let nonce = get_nonce(&nonce_value).map_err(LuaError::external)?;
let msg_bytes = base64::decode(&msg).map_err(LuaError::external)?;
let text_bytes = box_::open(&msg_bytes, &nonce.0, &this.public, &this.secret)
.map_err(|_| LuaError::external(Error::FailedToDecrypt))?;
String::from_utf8(text_bytes).map_err(LuaError::external)
}
pub fn seal_detached(_lua: &Lua, this: &KeyPair, (msg, nonce_value): (String, Option<Value>)) -> Result<(String, String), LuaError> {
let msg: &mut [u8] = &mut msg.into_bytes().clone();
let nonce = get_nonce(&nonce_value).map_err(LuaError::external)?;
let tag = box_::seal_detached(msg, &nonce.0, &this.public, &this.secret);
Ok((base64::encode(&msg), base64::encode(tag.0.as_ref())))
}
pub fn get_keys(_lua: &Lua, this: &KeyPair, _: ()) -> Result<(String, String), LuaError> {
let secret = base64::encode(this.secret.0.as_ref());
let public = base64::encode(this.public.0.as_ref());
Ok((secret, public))
}
impl UserData for KeyPair {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("get_keys", get_keys);
methods.add_method("seal", seal);
methods.add_method("open", open);
methods.add_method("seal_detached", seal_detached);
}
}
pub fn nonce_to_string(_lua: &Lua, this: &Nonce, _: ()) -> Result<String, LuaError> {
Ok(base64::encode((this.0).0.as_ref()))
}
impl UserData for Nonce {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("tostring", nonce_to_string);
}
}