use super::utils::{cross_product_32, h2, h3, h4};
use alloc::vec;
use ark_ec::PrimeGroup;
use ark_ff::Zero;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{ops::Mul, rand::Rng, vec::Vec};
use serde::{Deserialize, Serialize};
use crate::Message;
use crate::engines::EngineBLS;
#[derive(
Debug, Clone, PartialEq, CanonicalDeserialize, CanonicalSerialize, Serialize, Deserialize,
)]
pub struct Ciphertext<E: EngineBLS> {
pub u: E::PublicKeyGroup,
pub v: Vec<u8>,
pub w: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum IbeError {
DecryptionFailed,
}
#[derive(Debug, PartialEq)]
pub enum InputError {
InvalidLength,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Input<E: EngineBLS> {
data: Vec<u8>,
_phantom: ark_std::marker::PhantomData<E>,
}
impl<E: EngineBLS> Input<E> {
pub fn new(data: Vec<u8>) -> Result<Self, InputError> {
if data.len() != E::SECRET_KEY_SIZE {
return Err(InputError::InvalidLength);
}
Ok(Self { data, _phantom: ark_std::marker::PhantomData })
}
pub fn from_array(data: [u8; 32]) -> Result<Self, InputError> {
Self::new(data.to_vec())
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
}
#[derive(Debug, Clone)]
pub struct Identity(pub Vec<Message>);
impl Identity {
pub fn new(ctx: &[u8], identities: Vec<Vec<u8>>) -> Self {
Self(
identities
.iter()
.map(|identity| Message::new(ctx, identity))
.collect::<Vec<_>>(),
)
}
pub fn extract<E: EngineBLS>(&self, sk: E::Scalar) -> IBESecret<E> {
IBESecret(self.public::<E>() * sk)
}
pub fn public<E: EngineBLS>(&self) -> E::SignatureGroup {
self.0
.iter()
.map(|message| message.hash_to_signature_curve::<E>())
.fold(E::SignatureGroup::zero(), |acc, val| acc + val)
}
pub fn encrypt<E, R>(
&self,
message: &Input<E>,
p_pub: E::PublicKeyGroup,
mut rng: R,
) -> Ciphertext<E>
where
E: EngineBLS,
R: Rng + Sized,
{
let bytes = message.as_bytes();
let mut sigma = vec![0u8;E::SECRET_KEY_SIZE];
rng.fill_bytes(&mut sigma);
let r: E::Scalar = h3::<E>(&sigma, bytes);
let p = E::PublicKeyGroup::generator();
let u = p * r;
let g_id = E::pairing(p_pub.mul(r), self.public::<E>());
let v_rhs = h2(g_id);
let v_out = cross_product_32(&sigma, &v_rhs);
let w_rhs = h4(&sigma);
let w_out = cross_product_32(bytes, &w_rhs);
Ciphertext::<E> { u, v: v_out.to_vec(), w: w_out.to_vec() }
}
}
#[derive(Debug, Clone, CanonicalDeserialize, CanonicalSerialize, Serialize, Deserialize)]
pub struct IBESecret<E: EngineBLS>(pub E::SignatureGroup);
impl<E: EngineBLS> IBESecret<E> {
pub fn decrypt(&self, ciphertext: &Ciphertext<E>) -> Result<Vec<u8>, IbeError> {
let sigma_rhs = h2(E::pairing(ciphertext.u, self.0));
let sigma = cross_product_32(&ciphertext.v, &sigma_rhs);
let m_rhs = h4(&sigma);
let m = cross_product_32(&ciphertext.w, &m_rhs);
let p = E::PublicKeyGroup::generator();
let r = h3::<E>(&sigma, &m);
let u_check = p * r;
if !u_check.eq(&ciphertext.u) {
return Err(IbeError::DecryptionFailed);
}
Ok(m)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::engines::drand::TinyBLS381;
use alloc::vec;
use ark_std::{test_rng, UniformRand};
enum TestStatusReport {
DecryptionResult { data: Vec<u8>, verify: Vec<u8> },
DecryptionFailure { error: IbeError },
}
fn run_test<EB: EngineBLS>(
identity: Identity,
message: [u8; 32],
derive_bad_sk: bool,
insert_bad_ciphertext: bool,
handler: &dyn Fn(TestStatusReport) -> (),
) {
let (msk, sk) = extract::<EB>(identity.clone(), derive_bad_sk);
let p_pub = <<EB as EngineBLS>::PublicKeyGroup as PrimeGroup>::generator() * msk;
let mut ct = Ciphertext { u: EB::PublicKeyGroup::generator(), v: vec![], w: vec![] };
if !insert_bad_ciphertext {
ct = identity.encrypt(&Input::from_array(message).unwrap(), p_pub, &mut test_rng());
}
match sk.decrypt(&ct) {
Ok(data) => {
handler(TestStatusReport::DecryptionResult { data, verify: message.to_vec() });
},
Err(e) => {
handler(TestStatusReport::DecryptionFailure { error: e });
},
}
}
fn extract<E: EngineBLS>(identity: Identity, derive_bad_sk: bool) -> (E::Scalar, IBESecret<E>) {
let msk = <E as EngineBLS>::Scalar::rand(&mut test_rng());
if derive_bad_sk {
return (msk, IBESecret(E::SignatureGroup::generator()));
}
let sk = identity.extract::<E>(msk);
(msk, sk)
}
#[test]
pub fn fullident_identity_construction_works() {
let id_string = b"example@test.com";
let identity = Identity::new(b"", vec![id_string.to_vec()]);
let expected_message = Message::new(b"", id_string);
assert_eq!(identity.0[0], expected_message);
}
#[test]
pub fn fullident_encrypt_and_decrypt() {
let id_string = b"example@test.com";
let identity = Identity::new(b"", vec![id_string.to_vec()]);
let message: [u8; 32] = [2; 32];
run_test::<TinyBLS381>(identity, message, false, false, &|status: TestStatusReport| {
match status {
TestStatusReport::DecryptionResult { data, verify } => {
assert_eq!(data, verify);
},
_ => panic!("Decryption should work"),
}
});
}
#[test]
pub fn fullident_decryption_fails_with_bad_ciphertext() {
let id_string = b"example@test.com";
let identity = Identity::new(b"", vec![id_string.to_vec()]);
let message: [u8; 32] = [2; 32];
run_test::<TinyBLS381>(identity, message, false, true, &|status: TestStatusReport| {
match status {
TestStatusReport::DecryptionFailure { error } => {
assert_eq!(error, IbeError::DecryptionFailed);
},
_ => panic!("all other conditions invalid"),
}
});
}
#[test]
pub fn fullident_decryption_fails_with_bad_key() {
let id_string = b"example@test.com";
let identity = Identity::new(b"", vec![id_string.to_vec()]);
let message: [u8; 32] = [2; 32];
run_test::<TinyBLS381>(identity, message, true, false, &|status: TestStatusReport| {
match status {
TestStatusReport::DecryptionFailure { error } => {
assert_eq!(error, IbeError::DecryptionFailed);
},
_ => panic!("all other conditions invalid"),
}
});
}
}