mod ibe;
use crate::ibe::Ciphertext;
use ibe::GAffine;
use sha2::Digest;
use std::io;
use thiserror::Error;
use tracing::info_span;
#[derive(Error, Debug)]
pub enum TLockError {
#[error(transparent)]
IBE(#[from] crate::ibe::IBEError),
#[error(transparent)]
IOError(#[from] io::Error),
}
pub fn encrypt<W: io::Write, R: io::Read>(
mut dst: W,
mut src: R,
public_key_bytes: &[u8],
round_number: u64,
) -> anyhow::Result<()> {
let mut message = [0; 16];
src.read(&mut message).map_err(TLockError::IOError)?;
let ct = info_span!("ibe::encryption")
.in_scope(|| time_lock(public_key_bytes, round_number, message))?;
dst.write_all(&ct.u.to_compressed()?)?;
dst.write_all(&ct.v)?;
dst.write_all(&ct.w)?;
Ok(())
}
pub fn decrypt<W: io::Write, R: io::Read>(
mut dst: W,
mut src: R,
signature: &[u8],
) -> anyhow::Result<(), TLockError> {
let c = {
let u = if signature.len() == ibe::G1_SIZE {
let mut u = [0u8; ibe::G2_SIZE];
src.read_exact(&mut u).map_err(TLockError::IOError)?;
u.to_vec()
} else {
let mut u = [0u8; ibe::G1_SIZE];
src.read_exact(&mut u).map_err(TLockError::IOError)?;
u.to_vec()
};
let mut v = [0u8; 16];
src.read_exact(&mut v).map_err(TLockError::IOError)?;
let v = [[0u8; 16], v].concat().to_vec();
let mut w = [0u8; 16];
src.read_exact(&mut w).map_err(TLockError::IOError)?;
let w = [[0u8; 16], w].concat().to_vec();
Ciphertext {
u: u.as_slice().try_into()?,
v,
w,
}
};
let mut pt = time_unlock(signature, &c)?;
if let Some(i) = pt.iter().rposition(|x| *x != 0) {
pt.truncate(i + 1);
}
dst.write_all(&pt).map_err(TLockError::IOError)
}
fn time_lock<M: AsRef<[u8]>>(
public_key_bytes: &[u8],
round_number: u64,
message: M,
) -> Result<ibe::Ciphertext, anyhow::Error> {
let public_key = GAffine::try_from(public_key_bytes)?;
let id = {
let mut hash = sha2::Sha256::new();
hash.update(round_number.to_be_bytes());
&hash.finalize().to_vec()[0..32]
};
ibe::encrypt(public_key, id, message)
}
fn time_unlock(signature: &[u8], c: &Ciphertext) -> Result<Vec<u8>, TLockError> {
ibe::decrypt(signature.try_into()?, c).map_err(TLockError::IBE)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pk_g1_sig_g2() {
let pk_bytes = hex::decode("8200fc249deb0148eb918d6e213980c5d01acd7fc251900d9260136da3b54836ce125172399ddc69c4e3e11429b62c11").unwrap();
let msg = vec![8; 16];
let ct = time_lock(&pk_bytes, 1000, msg.clone()).unwrap();
let signature = hex::decode("a4721e6c3eafcd823f138cd29c6c82e8c5149101d0bb4bafddbac1c2d1fe3738895e4e21dd4b8b41bf007046440220910bb1cdb91f50a84a0d7f33ff2e8577aa62ac64b35a291a728a9db5ac91e06d1312b48a376138d77b4d6ad27c24221afe").unwrap();
let pt = time_unlock(&signature, &ct).unwrap();
assert_eq!(pt, msg)
}
#[cfg(not(feature = "rfc9380"))]
#[test]
fn test_pk_g2_sig_g1() {
let pk_bytes = hex::decode("a0b862a7527fee3a731bcb59280ab6abd62d5c0b6ea03dc4ddf6612fdfc9d01f01c31542541771903475eb1ec6615f8d0df0b8b6dce385811d6dcf8cbefb8759e5e616a3dfd054c928940766d9a5b9db91e3b697e5d70a975181e007f87fca5e").unwrap();
let msg = vec![8; 16];
let ct = time_lock(&pk_bytes, 1000, msg.clone()).unwrap();
let signature = hex::decode("b09eacd45767c4d58306b98901ad0d6086e2663766f3a4ec71d00cf26f0f49eaf248abc7151c60cf419c4e8b37e80412").unwrap();
let pt = time_unlock(&signature, &ct).unwrap();
assert_eq!(pt, msg)
}
#[cfg(feature = "rfc9380")]
#[test]
fn test_pk_g2_sig_g1() {
let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a").unwrap();
let msg = vec![8; 16];
let ct = time_lock(&pk_bytes, 1000, msg.clone()).unwrap();
let signature = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39").unwrap();
let pt = time_unlock(&signature, &ct).unwrap();
assert_eq!(pt, msg)
}
}