use std::error::Error;
use std::str;
use rand::Rng;
mod strobe_rng;
use strobe_rng::StrobeRng;
use strobe_rs::{SecParam, Strobe};
use zeroize::{Zeroize, ZeroizeOnDrop};
use adss::{recover, Commune};
pub use {adss::load_bytes, adss::store_bytes, adss::Share as InternalShare};
#[cfg(feature = "star2")]
use ppoprf::ppoprf::{end_to_end_evaluation, Server as PPOPRFServer};
pub const AES_BLOCK_LEN: usize = 24;
pub const DIGEST_LEN: usize = 32;
#[derive(Clone, Debug, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct SingleMeasurement(Vec<u8>);
impl SingleMeasurement {
pub fn new(x: &[u8]) -> Self {
Self(x.to_vec())
}
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
pub fn as_vec(&self) -> Vec<u8> {
self.0.clone()
}
pub fn byte_len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<&str> for SingleMeasurement {
fn from(s: &str) -> Self {
SingleMeasurement::new(s.as_bytes())
}
}
#[derive(Debug)]
pub struct AssociatedData(Vec<u8>);
impl AssociatedData {
pub fn new(buf: &[u8]) -> Self {
Self(buf.to_vec())
}
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
pub fn as_vec(&self) -> Vec<u8> {
self.0.clone()
}
}
impl From<&str> for AssociatedData {
fn from(s: &str) -> Self {
AssociatedData::from(s.as_bytes())
}
}
impl From<&[u8]> for AssociatedData {
fn from(buf: &[u8]) -> Self {
AssociatedData::new(buf)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Zeroize)]
pub struct Share(InternalShare);
impl Share {
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
Some(Self(InternalShare::from_bytes(bytes)?))
}
}
impl Drop for Share {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ciphertext {
bytes: Vec<u8>,
}
impl Ciphertext {
pub fn new(enc_key_buf: &[u8], data: &[u8], label: &str) -> Self {
let mut s = Strobe::new(label.as_bytes(), SecParam::B128);
s.key(enc_key_buf, false);
let mut x = vec![0u8; data.len()];
x.copy_from_slice(data);
s.send_enc(&mut x, false);
Self { bytes: x.to_vec() }
}
pub fn decrypt(&self, enc_key_buf: &[u8], label: &str) -> Vec<u8> {
let mut s = Strobe::new(label.as_bytes(), SecParam::B128);
s.key(enc_key_buf, false);
let mut m = vec![0u8; self.bytes.len()];
m.copy_from_slice(&self.bytes);
s.recv_enc(&mut m, false);
m
}
pub fn to_bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
pub fn from_bytes(bytes: &[u8]) -> Ciphertext {
Self {
bytes: bytes.to_vec(),
}
}
}
impl From<Vec<u8>> for Ciphertext {
fn from(bytes: Vec<u8>) -> Self {
Self { bytes }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Message {
pub ciphertext: Ciphertext,
pub share: Share,
pub tag: Vec<u8>,
}
impl Message {
fn new(c: Ciphertext, share: Share, tag: &[u8]) -> Self {
Self {
ciphertext: c,
share,
tag: tag.to_vec(),
}
}
pub fn generate(
mg: &MessageGenerator,
rnd: &[u8; 32],
aux: Option<AssociatedData>,
) -> Result<Self, Box<dyn Error>> {
let r = mg.derive_random_values(rnd);
let key = mg.derive_key(&r[0]);
let share = mg.share(&r[0], &r[1])?;
let tag = r[2];
let mut data: Vec<u8> = Vec::new();
store_bytes(mg.x.as_slice(), &mut data);
if let Some(ad) = aux {
store_bytes(ad.as_slice(), &mut data);
}
let ciphertext = Ciphertext::new(&key, &data, "star_encrypt");
Ok(Message::new(ciphertext, share, &tag))
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut out: Vec<u8> = Vec::new();
store_bytes(&self.ciphertext.to_bytes(), &mut out);
store_bytes(&self.share.to_bytes(), &mut out);
store_bytes(&self.tag, &mut out);
out
}
pub fn from_bytes(bytes: &[u8]) -> Option<Message> {
let mut slice = bytes;
let cb = load_bytes(slice)?;
let ciphertext = Ciphertext::from_bytes(cb);
slice = &slice[4 + cb.len()..];
let sb = load_bytes(slice)?;
let share = Share::from_bytes(sb)?;
slice = &slice[4 + sb.len()..];
let tag = load_bytes(slice)?;
Some(Message {
ciphertext,
share,
tag: tag.to_vec(),
})
}
}
#[derive(Zeroize)]
pub struct WASMSharingMaterial {
pub key: [u8; 16],
pub share: Share,
pub tag: [u8; 32],
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct MessageGenerator {
pub x: SingleMeasurement,
threshold: u32,
epoch: Vec<u8>,
}
impl MessageGenerator {
pub fn new(x: SingleMeasurement, threshold: u32, epoch: &[u8]) -> Self {
Self {
x,
threshold,
epoch: epoch.into(),
}
}
pub fn share_with_local_randomness(
&self,
) -> Result<WASMSharingMaterial, Box<dyn Error>> {
let mut rnd = vec![0u8; 32];
self.sample_local_randomness(&mut rnd);
let r = self.derive_random_values(&rnd);
let key = self.derive_key(&r[0]);
let share = self.share(&r[0], &r[1])?;
let tag = r[2];
Ok(WASMSharingMaterial { key, share, tag })
}
#[cfg(feature = "star2")]
pub fn share_with_oprf_randomness(
&self,
oprf_server: &PPOPRFServer,
) -> WASMSharingMaterial {
let mut rnd = vec![0u8; 32];
self.sample_oprf_randomness(oprf_server, &mut rnd);
let r = self.derive_random_values(&rnd);
let key = self.derive_key(&r[0]);
let share = self.share(&r[0], &r[1]);
let tag = r[2].clone();
WASMSharingMaterial { key, share, tag }
}
fn derive_random_values(&self, randomness: &[u8]) -> Vec<[u8; 32]> {
let mut output = Vec::new();
for i in 0..3 {
let mut to_fill = [0u8; 32];
strobe_digest(
randomness,
&[&[i as u8]],
"star_derive_randoms",
&mut to_fill,
);
output.push(to_fill);
}
output
}
fn derive_key(&self, r1: &[u8]) -> [u8; 16] {
let mut enc_key = [0u8; 16];
derive_ske_key(r1, &self.epoch, &mut enc_key);
enc_key
}
fn share(&self, r1: &[u8], r2: &[u8]) -> Result<Share, Box<dyn Error>> {
let c = Commune::new(self.threshold, r1.to_vec(), r2.to_vec(), None);
Ok(Share(c.share()?))
}
pub fn sample_local_randomness(&self, out: &mut [u8]) {
if out.len() != DIGEST_LEN {
panic!(
"Output buffer length ({}) does not match randomness length ({})",
out.len(),
DIGEST_LEN
);
}
strobe_digest(
self.x.as_slice(),
&[&self.epoch, &self.threshold.to_le_bytes()],
"star_sample_local",
out,
);
}
#[cfg(feature = "star2")]
pub fn sample_oprf_randomness(
&self,
oprf_server: &PPOPRFServer,
out: &mut [u8],
) {
let mds = oprf_server.get_valid_metadata_tags();
let index = mds.iter().position(|r| r == &self.epoch).unwrap();
end_to_end_evaluation(oprf_server, self.x.as_slice(), index, true, out);
}
}
pub fn share_recover(shares: &[Share]) -> Result<Commune, Box<dyn Error>> {
recover(
&shares
.iter()
.map(|share| share.0.clone())
.collect::<Vec<InternalShare>>(),
)
}
pub fn derive_ske_key(r1: &[u8], epoch: &[u8], key_out: &mut [u8]) {
let mut to_fill = vec![0u8; 32];
strobe_digest(r1, &[epoch], "star_derive_ske_key", &mut to_fill);
key_out.copy_from_slice(&to_fill[..16]);
}
pub fn strobe_digest(key: &[u8], ad: &[&[u8]], label: &str, out: &mut [u8]) {
if out.len() != DIGEST_LEN {
panic!(
"Output buffer length ({}) does not match intended output length ({})",
out.len(),
DIGEST_LEN
);
} else if ad.is_empty() {
panic!("No additional data provided");
}
let mut t = Strobe::new(label.as_bytes(), SecParam::B128);
t.key(key, false);
for x in ad.iter() {
t.ad(x, false);
}
let mut rng: StrobeRng = t.into();
rng.fill(out);
}