use crate::helpers::*;
use crate::impls::inner_types::*;
use crate::traits::{HashToPoint, HashToScalar, Pairing};
use crate::*;
use rand::RngExt;
use sha2::{Digest, Sha256};
use subtle::CtOption;
const SALT: &[u8] = b"TIMELOCK_BLS12381_XOF:HKDF-SHA2-256_";
pub trait BlsTimeCrypt:
Pairing
+ HashToPoint<Output = Self::Signature>
+ HashToScalar<Output = <Self::Signature as Group>::Scalar>
{
fn seal(
pk: Self::PublicKey,
message: &[u8],
id: &[u8],
dst: &[u8],
) -> BlsResult<(Self::PublicKey, [u8; 32], Vec<u8>)> {
if pk.is_identity().into() {
return Err(BlsError::InvalidInputs(
"public key is the identity point".to_string(),
));
}
let mut rng = get_crypto_rng();
let alpha = Self::hash_to_scalar(rng.random::<[u8; 32]>(), SALT);
debug_assert_eq!(alpha.is_zero().unwrap_u8(), 0u8);
let msg_dst = Sha256::digest(message);
let alpha_bytes = alpha.to_repr();
let mut r_input = Vec::with_capacity(alpha_bytes.as_ref().len() + msg_dst.len());
r_input.extend_from_slice(alpha_bytes.as_ref());
r_input.extend_from_slice(&msg_dst);
let r = Self::hash_to_scalar(r_input.as_slice(), SALT);
debug_assert_eq!(r.is_zero().unwrap_u8(), 0u8);
let k_rhs = pk * r;
debug_assert_eq!(k_rhs.is_identity().unwrap_u8(), 0u8);
let k_lhs = Self::hash_to_point(id, dst);
debug_assert_eq!(k_lhs.is_identity().unwrap_u8(), 0u8);
let k = Self::pairing(&[(k_lhs, k_rhs)]);
debug_assert_eq!(k.is_identity().unwrap_u8(), 0u8);
let u = Self::PublicKey::generator() * r;
debug_assert_eq!(u.is_identity().unwrap_u8(), 0u8);
let v = Self::compute_v(k, alpha.to_repr().as_ref());
let overhead_bytes = encode_message_with_len(message, 32);
let w = Self::compute_w(alpha.to_repr().as_ref(), overhead_bytes.as_slice());
Ok((u, v, w))
}
fn unseal(
u: Self::PublicKey,
v: &[u8; 32],
w: &[u8],
decryption_key: Self::Signature,
is_valid: Choice,
) -> CtOption<Vec<u8>> {
let valid_sk = !decryption_key.is_identity() & !u.is_identity();
let k = Self::pairing(&[(decryption_key, u)]);
let alpha = Self::compute_v(k, v);
let plaintext = Self::compute_w(&alpha, w);
let mut message = vec![];
if let Some(overhead) = uint_zigzag::Uint::peek(plaintext.as_slice()) {
let Ok(encoded_len) = uint_zigzag::Uint::try_from(&plaintext[..overhead]) else {
return CtOption::new(w.to_vec(), 0u8.into());
};
let len = encoded_len.0 as usize;
if len <= plaintext.len() - overhead {
message = plaintext[overhead..overhead + len].to_vec();
} else {
return CtOption::new(w.to_vec(), 0u8.into());
}
}
let msg_dst = Sha256::digest(&message);
let mut r_input = Vec::with_capacity(alpha.len() + msg_dst.len());
r_input.extend_from_slice(&alpha);
r_input.extend_from_slice(&msg_dst);
let r = Self::hash_to_scalar(r_input.as_slice(), SALT);
debug_assert_eq!(r.is_zero().unwrap_u8(), 0u8);
CtOption::new(
message,
((Self::PublicKey::generator() * r) - u).is_identity() & is_valid & valid_sk,
)
}
fn compute_v(k_tick: Self::PairingResult, alpha_or_v: &[u8]) -> [u8; 32] {
let output = Sha256::digest(k_tick.to_bytes().as_ref());
let result = byte_xor(alpha_or_v, &output);
let mut value = [0u8; 32];
value.copy_from_slice(&result);
value
}
fn compute_w(alpha: &[u8], msg: &[u8]) -> Vec<u8> {
shake128_xor(alpha, msg)
}
}