commonware_cryptography/bls12381/
tle.rs

1//! Timelock Encryption (TLE) over BLS12-381.
2//!
3//! This crate implements Timelock Encryption (TLE) over BLS12-381 using
4//! Identity-Based Encryption (IBE) with the Boneh-Franklin scheme. TLE enables
5//! encrypting messages that can only be decrypted when a valid signature over
6//! a specific target (e.g., timestamp or round number) becomes available.
7//!
8//! # Security
9//!
10//! To achieve CCA-security (resistance against chosen-ciphertext attacks), this
11//! implementation employs the Fujisaki-Okamoto transform, which converts the
12//! underlying CPA-secure IBE scheme into a CCA-secure scheme through:
13//!
14//! * Deriving encryption randomness deterministically from the message and a
15//!   random value (sigma)
16//! * Including integrity checks to detect ciphertext tampering
17//!
18//! # Architecture
19//!
20//! The encryption process involves (for [crate::bls12381::primitives::variant::MinPk]):
21//! 1. Generating a random sigma value
22//! 2. Deriving encryption randomness r = H3(sigma || message)
23//! 3. Computing the ciphertext components:
24//!    - U = r * G (commitment in G1)
25//!    - V = sigma ⊕ H2(e(r * P_pub, Q_id)) (masked random value)
26//!    - W = M ⊕ H4(sigma) (masked message)
27//!
28//! Where Q_id = H1(target) maps the target to a point in G2.
29//!
30//! # Example
31//!
32//! _It is recommended to use a threshold signature scheme to generate decrypting
33//! signatures in production (where no single party owns the private key)._
34//!
35//! ```rust
36//! use commonware_cryptography::bls12381::{
37//!     tle::{encrypt, decrypt, Block},
38//!     primitives::{
39//!         ops::{keypair, sign_message},
40//!         variant::MinPk,
41//!     },
42//! };
43//! use rand::rngs::OsRng;
44//!
45//! // Generate keypair
46//! let (master_secret, master_public) = keypair::<_, MinPk>(&mut OsRng);
47//!
48//! // Define a target (e.g., a timestamp or round number)
49//! let target = 12345u64.to_be_bytes();
50//!
51//! // Create a 32-byte message
52//! let message_bytes = b"This is a secret message 32bytes";
53//! let message = Block::new(*message_bytes);
54//!
55//! // Encrypt the message for the target
56//! let ciphertext = encrypt::<_, MinPk>(
57//!     &mut OsRng,
58//!     master_public,
59//!     (None, &target),
60//!     &message,
61//! );
62//!
63//! // Later, when someone has a signature over the target...
64//! let signature = sign_message::<MinPk>(&master_secret, None, &target);
65//!
66//! // They can decrypt the message
67//! let decrypted = decrypt::<MinPk>(&signature, &ciphertext)
68//!     .expect("Decryption should succeed with valid signature");
69//!
70//! assert_eq!(message.as_ref(), decrypted.as_ref());
71//! ```
72//!
73//! # Acknowledgements
74//!
75//! The following resources were used as references when implementing this crate:
76//!
77//! * <https://crypto.stanford.edu/~dabo/papers/bfibe.pdf>: Identity-Based Encryption from the Weil Pairing
78//! * <https://eprint.iacr.org/2023/189>: tlock: Practical Timelock Encryption from Threshold BLS
79//! * <https://github.com/thibmeu/tlock-rs>: tlock-rs: Practical Timelock Encryption/Decryption in Rust
80//! * <https://github.com/drand/tlock> tlock: Timelock Encryption/Decryption Made Practical
81
82use crate::{
83    bls12381::primitives::{
84        group::{Element, Scalar, DST, GT},
85        ops::{hash_message, hash_message_namespace},
86        variant::Variant,
87    },
88    sha256::Digest,
89};
90use bytes::{Buf, BufMut};
91use commonware_codec::{EncodeSize, FixedSize, Read, ReadExt, Write};
92use commonware_utils::sequence::FixedBytes;
93use rand::{CryptoRng, Rng};
94
95/// Domain separation tag for hashing the `h3` message to a scalar.
96const DST: DST = b"TLE_BLS12381_XMD:SHA-256_SSWU_RO_H3_";
97
98/// Block size for encryption operations.
99const BLOCK_SIZE: usize = Digest::SIZE;
100
101/// Block type for IBE.
102pub type Block = FixedBytes<BLOCK_SIZE>;
103
104impl From<Digest> for Block {
105    fn from(digest: Digest) -> Self {
106        Block::new(digest.0)
107    }
108}
109
110/// Encrypted message.
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct Ciphertext<V: Variant> {
113    /// First group element U = r * Public::one().
114    pub u: V::Public,
115    /// Encrypted random value V = sigma XOR H2(e(r * P_pub, Q_id)).
116    pub v: Block,
117    /// Encrypted message W = M XOR H4(sigma).
118    pub w: Block,
119}
120
121impl<V: Variant> Write for Ciphertext<V> {
122    fn write(&self, buf: &mut impl BufMut) {
123        self.u.write(buf);
124        buf.put_slice(self.v.as_ref());
125        buf.put_slice(self.w.as_ref());
126    }
127}
128
129impl<V: Variant> Read for Ciphertext<V> {
130    type Cfg = ();
131
132    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, commonware_codec::Error> {
133        let u = V::Public::read(buf)?;
134        let v = Block::read(buf)?;
135        let w = Block::read(buf)?;
136        Ok(Self { u, v, w })
137    }
138}
139
140impl<V: Variant> EncodeSize for Ciphertext<V> {
141    fn encode_size(&self) -> usize {
142        self.u.encode_size() + self.v.encode_size() + self.w.encode_size()
143    }
144}
145
146/// Hash functions for IBE.
147mod hash {
148    use super::*;
149    use crate::{Hasher, Sha256};
150
151    /// H2: GT -> Block
152    ///
153    /// Used to mask the random sigma value.
154    pub fn h2(gt: &GT) -> Block {
155        let mut hasher = Sha256::new();
156        hasher.update(b"h2");
157        hasher.update(&gt.as_slice());
158        hasher.finalize().into()
159    }
160
161    /// H3: (sigma, M) -> Scalar
162    ///
163    /// Used to derive the random scalar r using RFC9380 hash-to-field.
164    pub fn h3(sigma: &Block, message: &[u8]) -> Scalar {
165        // Combine sigma and message
166        let mut combined = Vec::with_capacity(sigma.len() + message.len());
167        combined.extend_from_slice(sigma.as_ref());
168        combined.extend_from_slice(message);
169
170        // Map the combined bytes to a scalar via RFC9380 hash-to-field
171        Scalar::map(DST, &combined)
172    }
173
174    /// H4: sigma -> Block
175    ///
176    /// Used to mask the message.
177    pub fn h4(sigma: &Block) -> Block {
178        let mut hasher = Sha256::new();
179        hasher.update(b"h4");
180        hasher.update(sigma.as_ref());
181        hasher.finalize().into()
182    }
183}
184
185/// XOR two [Block]s together.
186///
187/// This function takes advantage of the fixed-size nature of blocks
188/// to enable better compiler optimizations. Since we know blocks are
189/// exactly 32 bytes, we can unroll the operation completely.
190#[inline]
191fn xor(a: &Block, b: &Block) -> Block {
192    let a_bytes = a.as_ref();
193    let b_bytes = b.as_ref();
194
195    // Since Block is exactly 32 bytes, we can use array initialization
196    // with const generics to let the compiler fully optimize this
197    Block::new([
198        a_bytes[0] ^ b_bytes[0],
199        a_bytes[1] ^ b_bytes[1],
200        a_bytes[2] ^ b_bytes[2],
201        a_bytes[3] ^ b_bytes[3],
202        a_bytes[4] ^ b_bytes[4],
203        a_bytes[5] ^ b_bytes[5],
204        a_bytes[6] ^ b_bytes[6],
205        a_bytes[7] ^ b_bytes[7],
206        a_bytes[8] ^ b_bytes[8],
207        a_bytes[9] ^ b_bytes[9],
208        a_bytes[10] ^ b_bytes[10],
209        a_bytes[11] ^ b_bytes[11],
210        a_bytes[12] ^ b_bytes[12],
211        a_bytes[13] ^ b_bytes[13],
212        a_bytes[14] ^ b_bytes[14],
213        a_bytes[15] ^ b_bytes[15],
214        a_bytes[16] ^ b_bytes[16],
215        a_bytes[17] ^ b_bytes[17],
216        a_bytes[18] ^ b_bytes[18],
217        a_bytes[19] ^ b_bytes[19],
218        a_bytes[20] ^ b_bytes[20],
219        a_bytes[21] ^ b_bytes[21],
220        a_bytes[22] ^ b_bytes[22],
221        a_bytes[23] ^ b_bytes[23],
222        a_bytes[24] ^ b_bytes[24],
223        a_bytes[25] ^ b_bytes[25],
224        a_bytes[26] ^ b_bytes[26],
225        a_bytes[27] ^ b_bytes[27],
226        a_bytes[28] ^ b_bytes[28],
227        a_bytes[29] ^ b_bytes[29],
228        a_bytes[30] ^ b_bytes[30],
229        a_bytes[31] ^ b_bytes[31],
230    ])
231}
232
233/// Encrypt a message for a given target.
234///
235/// # Steps
236/// 1. Generate random sigma
237/// 2. Derive encryption randomness r = H3(sigma || message)
238/// 3. Create commitment U = r * G
239/// 4. Mask sigma with the pairing result
240/// 5. Mask the message with H4(sigma)
241///
242/// # Arguments
243/// * `rng` - Random number generator
244/// * `public` - Master public key
245/// * `target` - Payload over which a signature will decrypt the message
246/// * `message` - Message to encrypt
247///
248/// # Returns
249/// * `Ciphertext<V>` - The encrypted ciphertext
250pub fn encrypt<R: Rng + CryptoRng, V: Variant>(
251    rng: &mut R,
252    public: V::Public,
253    target: (Option<&[u8]>, &[u8]),
254    message: &Block,
255) -> Ciphertext<V> {
256    // Hash target to get Q_id in signature group using the variant's message DST
257    let q_id = match target {
258        (None, target) => hash_message::<V>(V::MESSAGE, target),
259        (Some(namespace), target) => hash_message_namespace::<V>(V::MESSAGE, namespace, target),
260    };
261
262    // Generate random sigma
263    let mut sigma_array = [0u8; BLOCK_SIZE];
264    rng.fill_bytes(&mut sigma_array);
265    let sigma = Block::new(sigma_array);
266
267    // Derive scalar r from sigma and message
268    let r = hash::h3(&sigma, message.as_ref());
269
270    // Compute U = r * Public::one()
271    let mut u = V::Public::one();
272    u.mul(&r);
273
274    // Compute e(r * P_pub, Q_id)
275    let mut r_pub = public;
276    r_pub.mul(&r);
277    let gt = V::pairing(&r_pub, &q_id);
278
279    // Compute V = sigma XOR H2(e(r * P_pub, Q_id))
280    let h2_value = hash::h2(&gt);
281    let v = xor(&sigma, &h2_value);
282
283    // Compute W = M XOR H4(sigma)
284    let h4_value = hash::h4(&sigma);
285    let w = xor(message, &h4_value);
286
287    Ciphertext { u, v, w }
288}
289
290/// Decrypt a ciphertext with a signature over the target specified
291/// during [encrypt].
292///
293/// # Steps
294/// 1. Recover sigma from the pairing
295/// 2. Recover the message
296/// 3. Recompute r = H3(sigma || message)
297/// 4. Verify that U = r * G matches the ciphertext
298///
299/// # Arguments
300/// * `signature` - Signature over the target payload
301/// * `ciphertext` - Ciphertext to decrypt
302///
303/// # Returns
304/// * `Option<Block>` - The decrypted message
305pub fn decrypt<V: Variant>(signature: &V::Signature, ciphertext: &Ciphertext<V>) -> Option<Block> {
306    // Compute e(U, signature)
307    let gt = V::pairing(&ciphertext.u, signature);
308
309    // Recover sigma = V XOR H2(e(U, signature))
310    let h2_value = hash::h2(&gt);
311    let sigma = xor(&ciphertext.v, &h2_value);
312
313    // Recover M = W XOR H4(sigma)
314    let h4_value = hash::h4(&sigma);
315    let message = xor(&ciphertext.w, &h4_value);
316
317    // Recompute r and verify U = r * Public::one()
318    let r = hash::h3(&sigma, &message);
319    let mut expected_u = V::Public::one();
320    expected_u.mul(&r);
321    if ciphertext.u != expected_u {
322        return None;
323    }
324
325    Some(message)
326}
327
328#[cfg(test)]
329mod tests {
330    use super::*;
331    use crate::bls12381::primitives::{
332        ops::{keypair, sign_message},
333        variant::{MinPk, MinSig},
334    };
335    use rand::thread_rng;
336
337    #[test]
338    fn test_encrypt_decrypt_minpk() {
339        let mut rng = thread_rng();
340
341        // Generate master keypair
342        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
343
344        // Target and message
345        let target = 10u64.to_be_bytes();
346        let message = b"Hello, IBE! This is exactly 32b!"; // 32 bytes
347
348        // Generate signature over the target
349        let signature = sign_message::<MinPk>(&master_secret, None, &target);
350
351        // Encrypt
352        let ciphertext = encrypt::<_, MinPk>(
353            &mut rng,
354            master_public,
355            (None, &target),
356            &Block::new(*message),
357        );
358
359        // Decrypt
360        let decrypted =
361            decrypt::<MinPk>(&signature, &ciphertext).expect("Decryption should succeed");
362
363        assert_eq!(message.as_ref(), decrypted.as_ref());
364    }
365
366    #[test]
367    fn test_encrypt_decrypt_minsig() {
368        let mut rng = thread_rng();
369
370        // Generate master keypair
371        let (master_secret, master_public) = keypair::<_, MinSig>(&mut rng);
372
373        // Target and message
374        let target = 20u64.to_be_bytes();
375        let message = b"Testing MinSig variant - 32 byte";
376
377        // Generate signature over the target
378        let signature = sign_message::<MinSig>(&master_secret, None, &target);
379
380        // Encrypt
381        let ciphertext = encrypt::<_, MinSig>(
382            &mut rng,
383            master_public,
384            (None, &target),
385            &Block::new(*message),
386        );
387
388        // Decrypt
389        let decrypted =
390            decrypt::<MinSig>(&signature, &ciphertext).expect("Decryption should succeed");
391
392        assert_eq!(message.as_ref(), decrypted.as_ref());
393    }
394
395    #[test]
396    fn test_wrong_private_key() {
397        let mut rng = thread_rng();
398
399        // Generate two different master keypairs
400        let (_, master_public1) = keypair::<_, MinPk>(&mut rng);
401        let (master_secret2, _) = keypair::<_, MinPk>(&mut rng);
402
403        let target = 30u64.to_be_bytes();
404        let message = b"Secret message padded to 32bytes";
405
406        // Encrypt with first master public key
407        let ciphertext = encrypt::<_, MinPk>(
408            &mut rng,
409            master_public1,
410            (None, &target),
411            &Block::new(*message),
412        );
413
414        // Try to decrypt with signature from second master
415        let wrong_signature = sign_message::<MinPk>(&master_secret2, None, &target);
416        let result = decrypt::<MinPk>(&wrong_signature, &ciphertext);
417
418        assert!(result.is_none());
419    }
420
421    #[test]
422    fn test_tampered_ciphertext() {
423        let mut rng = thread_rng();
424
425        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
426        let target = 40u64.to_be_bytes();
427        let message = b"Tamper test padded to 32 bytes.."; // 32 bytes
428
429        // Generate signature over the target
430        let signature = sign_message::<MinPk>(&master_secret, None, &target);
431
432        // Encrypt
433        let ciphertext = encrypt::<_, MinPk>(
434            &mut rng,
435            master_public,
436            (None, &target),
437            &Block::new(*message),
438        );
439
440        // Tamper with ciphertext by creating a modified w
441        let mut w_bytes = [0u8; BLOCK_SIZE];
442        w_bytes.copy_from_slice(ciphertext.w.as_ref());
443        w_bytes[0] ^= 0xFF;
444        let tampered_ciphertext = Ciphertext {
445            u: ciphertext.u,
446            v: ciphertext.v,
447            w: Block::new(w_bytes),
448        };
449
450        // Try to decrypt
451        let result = decrypt::<MinPk>(&signature, &tampered_ciphertext);
452        assert!(result.is_none());
453    }
454
455    #[test]
456    fn test_encrypt_decrypt_with_namespace() {
457        let mut rng = thread_rng();
458
459        // Generate master keypair
460        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
461
462        // Target and namespace
463        let namespace = b"example.org";
464        let target = 80u64.to_be_bytes();
465        let message = b"Message with namespace - 32 byte"; // 32 bytes
466
467        // Generate signature over the namespaced target
468        let signature = sign_message::<MinPk>(&master_secret, Some(namespace), &target);
469
470        // Encrypt with namespace
471        let ciphertext = encrypt::<_, MinPk>(
472            &mut rng,
473            master_public,
474            (Some(namespace), &target),
475            &Block::new(*message),
476        );
477
478        // Decrypt
479        let decrypted =
480            decrypt::<MinPk>(&signature, &ciphertext).expect("Decryption should succeed");
481
482        assert_eq!(message.as_ref(), decrypted.as_ref());
483    }
484
485    #[test]
486    fn test_namespace_variance() {
487        let mut rng = thread_rng();
488
489        // Generate master keypair
490        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
491
492        let namespace = b"example.org";
493        let target = 100u64.to_be_bytes();
494        let message = b"Namespace vs no namespace - 32by"; // 32 bytes
495
496        // Generate signature without namespace
497        let signature_no_ns = sign_message::<MinPk>(&master_secret, None, &target);
498
499        // Generate signature with namespace
500        let signature_ns = sign_message::<MinPk>(&master_secret, Some(namespace), &target);
501
502        // Encrypt with namespace
503        let ciphertext_ns = encrypt::<_, MinPk>(
504            &mut rng,
505            master_public,
506            (Some(namespace), &target),
507            &Block::new(*message),
508        );
509
510        // Encrypt without namespace
511        let ciphertext_no_ns = encrypt::<_, MinPk>(
512            &mut rng,
513            master_public,
514            (None, &target),
515            &Block::new(*message),
516        );
517
518        // Try to decrypt namespaced ciphertext with non-namespaced signature - should fail
519        let result1 = decrypt::<MinPk>(&signature_no_ns, &ciphertext_ns);
520        assert!(result1.is_none());
521
522        // Try to decrypt non-namespaced ciphertext with namespaced signature - should fail
523        let result2 = decrypt::<MinPk>(&signature_ns, &ciphertext_no_ns);
524        assert!(result2.is_none());
525
526        // Correct decryptions should succeed
527        let decrypted_ns = decrypt::<MinPk>(&signature_ns, &ciphertext_ns)
528            .expect("Decryption with matching namespace should succeed");
529        let decrypted_no_ns = decrypt::<MinPk>(&signature_no_ns, &ciphertext_no_ns)
530            .expect("Decryption without namespace should succeed");
531
532        assert_eq!(message.as_ref(), decrypted_ns.as_ref());
533        assert_eq!(message.as_ref(), decrypted_no_ns.as_ref());
534    }
535
536    #[test]
537    fn test_cca_modified_v() {
538        let mut rng = thread_rng();
539
540        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
541        let target = 110u64.to_be_bytes();
542        let message = b"Another CCA test message 32bytes"; // 32 bytes
543
544        // Generate signature over the target
545        let signature = sign_message::<MinPk>(&master_secret, None, &target);
546
547        // Encrypt
548        let ciphertext = encrypt::<_, MinPk>(
549            &mut rng,
550            master_public,
551            (None, &target),
552            &Block::new(*message),
553        );
554
555        // Modify V component (encrypted sigma)
556        let mut v_bytes = [0u8; BLOCK_SIZE];
557        v_bytes.copy_from_slice(ciphertext.v.as_ref());
558        v_bytes[0] ^= 0x01;
559        let tampered_ciphertext = Ciphertext {
560            u: ciphertext.u,
561            v: Block::new(v_bytes),
562            w: ciphertext.w,
563        };
564
565        // Try to decrypt - should fail due to verification
566        let result = decrypt::<MinPk>(&signature, &tampered_ciphertext);
567        assert!(result.is_none());
568    }
569
570    #[test]
571    fn test_cca_modified_u() {
572        let mut rng = thread_rng();
573
574        let (master_secret, master_public) = keypair::<_, MinPk>(&mut rng);
575        let target = 70u64.to_be_bytes();
576        let message = b"CCA security test message 32 byt"; // 32 bytes
577
578        // Generate signature over the target
579        let signature = sign_message::<MinPk>(&master_secret, None, &target);
580
581        // Encrypt
582        let mut ciphertext = encrypt::<_, MinPk>(
583            &mut rng,
584            master_public,
585            (None, &target),
586            &Block::new(*message),
587        );
588
589        // Modify U component (this should make decryption fail due to FO transform)
590        let mut modified_u = ciphertext.u;
591        modified_u.mul(&Scalar::from_rand(&mut rng));
592        ciphertext.u = modified_u;
593
594        // Try to decrypt - should fail
595        let result = decrypt::<MinPk>(&signature, &ciphertext);
596        assert!(result.is_none());
597    }
598}