use mohan::{
ser,
dalek::{
scalar::Scalar,
constants::RISTRETTO_BASEPOINT_TABLE,
ristretto::CompressedRistretto,
traits::Identity
},
mohan_rand
};
use bacteria::Transcript;
use schnorr::SecretKey;
use schnorr::PublicKey;
use crate::ZeiError;
use serde::{ Serialize, Deserialize };
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct Lockbox {
pub rand: CompressedRistretto,
pub data: Vec<u8>,
}
impl Default for Lockbox {
fn default() -> Lockbox {
Lockbox {
rand: CompressedRistretto::identity(),
data: Vec::new()
}
}
}
pub fn lock(randomness: &Scalar, publickey: &PublicKey, message: &[u8]) -> Lockbox {
let mut ctx = Transcript::new(b"Zei.Lockbox");
ctx.append_message(b"PK", publickey.as_bytes());
let shared_key = randomness * publickey.as_point();
let blind_rand = randomness * &RISTRETTO_BASEPOINT_TABLE;
ctx.strobe.key(shared_key.compress().as_bytes(), false);
let mut buf = message.to_vec();
ctx.strobe.send_enc(buf.as_mut_slice(), false);
return Lockbox {
data: buf,
rand: blind_rand.compress()
};
}
pub fn unlock(boxy: &mut Lockbox, secretkey: &SecretKey, publickey: &PublicKey) -> Result<(), ZeiError> {
let mut ctx = Transcript::new(b"Zei.Lockbox");
ctx.append_message(b"PK", publickey.as_bytes());
let dec_key = boxy.rand.decompress().unwrap() * secretkey.as_scalar();
ctx.strobe.key(dec_key.compress().as_bytes(), false);
ctx.strobe.recv_enc(&mut boxy.data, false);
Ok(())
}
impl ser::Writeable for Lockbox {
fn write<W: ser::Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.rand.write(writer)?;
writer.write_u64(self.data.len() as u64)?;
self.data.write(writer)?;
Ok(())
}
}
impl ser::Readable for Lockbox {
fn read(reader: &mut dyn ser::Reader) -> Result<Lockbox, ser::Error> {
let rand = CompressedRistretto::read(reader)?;
let data_len = reader.read_u64()?;
let raw_data = reader.read_fixed_bytes(data_len as usize)?;
Ok(Lockbox{
rand: rand,
data: raw_data
})
}
}
#[cfg(test)]
mod test {
use super::*;
use mohan::mohan_rand;
use schnorr::Keypair;
use mohan::byteorder::{ByteOrder, BigEndian};
#[test]
fn test_lock_unlock() {
let mut csprng = mohan_rand();
let keypair: Keypair = Keypair::generate(&mut csprng);
let blinding_t = Scalar::random(&mut csprng);
let amount: u32 = 101;
let mut to_encrypt = Vec::new();
let mut encoded_amount = [0; 4];
BigEndian::write_u32(&mut encoded_amount, amount);
to_encrypt.extend_from_slice(&encoded_amount);
to_encrypt.extend_from_slice(&blinding_t.to_bytes());
let rand_t = Scalar::random(&mut csprng);
let mut lbox = lock(&rand_t, &keypair.public, &to_encrypt);
let unlocked = unlock(&mut lbox, &keypair.secret, &keypair.public);
assert!(unlocked.is_ok());
let (raw_amount, _raw_blind) = lbox.data.split_at(5);
let p_amount = BigEndian::read_u32(&raw_amount);
assert_eq!(p_amount, amount);
}
#[test]
fn test_box_roundtrip() {
let mut csprng = mohan_rand();
let keypair: Keypair = Keypair::generate(&mut csprng);
let blinding_t = Scalar::random(&mut csprng);
let amount: u32 = 101;
let mut to_encrypt = Vec::new();
let mut encoded_amount = [0; 4];
BigEndian::write_u32(&mut encoded_amount, amount);
to_encrypt.extend_from_slice(&encoded_amount);
to_encrypt.extend_from_slice(&blinding_t.to_bytes());
let rand_t = Scalar::random(&mut csprng);
let lbox = lock(&rand_t, &keypair.public, &to_encrypt);
use std::io::{self, Write, Read};
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
let mut read_handle = tmpfile.reopen().unwrap();
let _x = mohan::ser::serialize_default(&mut tmpfile, &lbox).expect("serialization failed");
let mut lbox: Lockbox = mohan::ser::deserialize_default(&mut read_handle).unwrap();
let unlocked = unlock(&mut lbox, &keypair.secret, &keypair.public);
assert!(unlocked.is_ok());
let (raw_amount, _raw_blind) = lbox.data.split_at(5);
let p_amount = BigEndian::read_u32(&raw_amount);
assert_eq!(p_amount, amount);
}
}