umbral_pre/
pre.rs

1//! The high-level functional reencryption API.
2
3use core::fmt;
4
5use rand_core::{CryptoRng, RngCore};
6
7#[cfg(feature = "default-rng")]
8use rand_core::OsRng;
9
10use crate::capsule::{Capsule, OpenReencryptedError};
11use crate::capsule_frag::VerifiedCapsuleFrag;
12use crate::dem::{DecryptionError, EncryptionError, DEM};
13use crate::key_frag::{KeyFragBase, VerifiedKeyFrag};
14use crate::keys::{PublicKey, SecretKey, Signer};
15
16use alloc::boxed::Box;
17use alloc::vec::Vec;
18
19/// Errors that can happen when decrypting a reencrypted ciphertext.
20#[derive(Debug, PartialEq, Eq)]
21pub enum ReencryptionError {
22    /// An error when opening a capsule. See [`OpenReencryptedError`] for the options.
23    OnOpen(OpenReencryptedError),
24    /// An error when decrypting the ciphertext. See [`DecryptionError`] for the options.
25    OnDecryption(DecryptionError),
26}
27
28impl fmt::Display for ReencryptionError {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::OnOpen(err) => write!(f, "Re-encryption error on open: {err}"),
32            Self::OnDecryption(err) => write!(f, "Re-encryption error on decryption: {err}"),
33        }
34    }
35}
36
37/// Encrypts the given plaintext message using a DEM scheme,
38/// and encapsulates the key for later reencryption.
39/// Returns the KEM [`Capsule`] and the ciphertext.
40pub fn encrypt_with_rng(
41    rng: &mut (impl CryptoRng + RngCore),
42    delegating_pk: &PublicKey,
43    plaintext: &[u8],
44) -> Result<(Capsule, Box<[u8]>), EncryptionError> {
45    let (capsule, key_seed) = Capsule::from_public_key(rng, delegating_pk);
46    let dem = DEM::new(key_seed.as_secret());
47    dem.encrypt(rng, plaintext, &capsule.to_bytes_simple())
48        .map(|ciphertext| (capsule, ciphertext))
49}
50
51/// A synonym for [`encrypt`] with the default RNG.
52#[cfg(feature = "default-rng")]
53pub fn encrypt(
54    delegating_pk: &PublicKey,
55    plaintext: &[u8],
56) -> Result<(Capsule, Box<[u8]>), EncryptionError> {
57    encrypt_with_rng(&mut OsRng, delegating_pk, plaintext)
58}
59
60/// Attempts to decrypt the ciphertext using the receiver's secret key.
61pub fn decrypt_original(
62    delegating_sk: &SecretKey,
63    capsule: &Capsule,
64    ciphertext: impl AsRef<[u8]>,
65) -> Result<Box<[u8]>, DecryptionError> {
66    let key_seed = capsule.open_original(delegating_sk);
67    let dem = DEM::new(key_seed.as_secret());
68    dem.decrypt(ciphertext, &capsule.to_bytes_simple())
69}
70
71/// Creates `shares` fragments of `delegating_sk`,
72/// which will be possible to reencrypt to allow the creator of `receiving_pk`
73/// decrypt the ciphertext encrypted with `delegating_sk`.
74///
75/// `threshold` sets the number of fragments necessary for decryption
76/// (that is, fragments created with `threshold > num_frags` will be useless).
77///
78/// `signer` is used to sign the resulting [`KeyFrag`](`crate::KeyFrag`) objects,
79/// which can be later verified by the associated public key.
80///
81/// If `sign_delegating_key` or `sign_receiving_key` are `true`,
82/// the reencrypting party will be able to verify that a [`KeyFrag`](`crate::KeyFrag`)
83/// corresponds to given delegating or receiving public keys
84/// by supplying them to [`KeyFrag::verify()`](`crate::KeyFrag::verify`).
85///
86/// Returns a boxed slice of `shares` KeyFrags
87#[allow(clippy::too_many_arguments)]
88pub fn generate_kfrags_with_rng(
89    rng: &mut (impl CryptoRng + RngCore),
90    delegating_sk: &SecretKey,
91    receiving_pk: &PublicKey,
92    signer: &Signer,
93    threshold: usize,
94    shares: usize,
95    sign_delegating_key: bool,
96    sign_receiving_key: bool,
97) -> Box<[VerifiedKeyFrag]> {
98    let base = KeyFragBase::new(rng, delegating_sk, receiving_pk, signer, threshold);
99
100    let mut result = Vec::<VerifiedKeyFrag>::new();
101    for _ in 0..shares {
102        result.push(VerifiedKeyFrag::from_base(
103            rng,
104            &base,
105            sign_delegating_key,
106            sign_receiving_key,
107        ));
108    }
109
110    result.into_boxed_slice()
111}
112
113/// A synonym for [`generate_kfrags_with_rng`] with the default RNG.
114#[cfg(feature = "default-rng")]
115#[allow(clippy::too_many_arguments)]
116pub fn generate_kfrags(
117    delegating_sk: &SecretKey,
118    receiving_pk: &PublicKey,
119    signer: &Signer,
120    threshold: usize,
121    shares: usize,
122    sign_delegating_key: bool,
123    sign_receiving_key: bool,
124) -> Box<[VerifiedKeyFrag]> {
125    generate_kfrags_with_rng(
126        &mut OsRng,
127        delegating_sk,
128        receiving_pk,
129        signer,
130        threshold,
131        shares,
132        sign_delegating_key,
133        sign_receiving_key,
134    )
135}
136
137/// Reencrypts a [`Capsule`] object with a key fragment, creating a capsule fragment.
138///
139/// Having `threshold` (see [`generate_kfrags()`](`crate::generate_kfrags()`))
140/// distinct fragments (along with the original capsule and the corresponding secret key)
141/// allows one to decrypt the original plaintext.
142///
143/// One can call [`KeyFrag::verify()`](`crate::KeyFrag::verify`)
144/// before reencryption to check its integrity.
145pub fn reencrypt_with_rng(
146    rng: &mut (impl CryptoRng + RngCore),
147    capsule: &Capsule,
148    verified_kfrag: VerifiedKeyFrag,
149) -> VerifiedCapsuleFrag {
150    VerifiedCapsuleFrag::reencrypted(rng, capsule, verified_kfrag.unverify())
151}
152
153/// A synonym for [`reencrypt_with_rng`] with the default RNG.
154#[cfg(feature = "default-rng")]
155pub fn reencrypt(capsule: &Capsule, verified_kfrag: VerifiedKeyFrag) -> VerifiedCapsuleFrag {
156    reencrypt_with_rng(&mut OsRng, capsule, verified_kfrag)
157}
158
159/// Decrypts the ciphertext using previously reencrypted capsule fragments.
160///
161/// `decrypting_sk` is the secret key whose associated public key was used in
162/// [`generate_kfrags()`](`crate::generate_kfrags()`).
163///
164/// `delegating_pk` is the public key of the encrypting party.
165/// Used to check the validity of decryption.
166///
167/// One can call [`CapsuleFrag::verify()`](`crate::CapsuleFrag::verify`)
168/// before reencryption to check its integrity.
169pub fn decrypt_reencrypted(
170    receiving_sk: &SecretKey,
171    delegating_pk: &PublicKey,
172    capsule: &Capsule,
173    verified_cfrags: impl IntoIterator<Item = VerifiedCapsuleFrag>,
174    ciphertext: impl AsRef<[u8]>,
175) -> Result<Box<[u8]>, ReencryptionError> {
176    let cfrags: Vec<_> = verified_cfrags
177        .into_iter()
178        .map(|vcfrag| vcfrag.unverify())
179        .collect();
180    let key_seed = capsule
181        .open_reencrypted(receiving_sk, delegating_pk, &cfrags)
182        .map_err(ReencryptionError::OnOpen)?;
183    let dem = DEM::new(key_seed.as_secret());
184    dem.decrypt(&ciphertext, &capsule.to_bytes_simple())
185        .map_err(ReencryptionError::OnDecryption)
186}
187
188#[cfg(test)]
189mod tests {
190
191    use alloc::vec::Vec;
192
193    use crate::{SecretKey, Signer, VerifiedCapsuleFrag};
194
195    use super::{decrypt_original, decrypt_reencrypted, encrypt, generate_kfrags, reencrypt};
196
197    #[test]
198    fn test_simple_api() {
199        /*
200        This test models the main interactions between NuCypher actors (i.e., Alice,
201        Bob, Data Source, and Ursulas) and artifacts (i.e., public and private keys,
202        ciphertexts, capsules, KeyFrags, CapsuleFrags, etc).
203
204        The test covers all the main stages of data sharing with NuCypher:
205        key generation, delegation, encryption, decryption by
206        Alice, re-encryption by Ursula, and decryption by Bob.
207        */
208
209        let threshold: usize = 2;
210        let num_frags: usize = threshold + 1;
211
212        // Key Generation (Alice)
213        let delegating_sk = SecretKey::random();
214        let delegating_pk = delegating_sk.public_key();
215
216        let signer = Signer::new(SecretKey::random());
217        let verifying_pk = signer.verifying_key();
218
219        // Key Generation (Bob)
220        let receiving_sk = SecretKey::random();
221        let receiving_pk = receiving_sk.public_key();
222
223        // Encryption by an unnamed data source
224        let plaintext = b"peace at dawn";
225        let (capsule, ciphertext) = encrypt(&delegating_pk, plaintext).unwrap();
226
227        // Decryption by Alice
228        let plaintext_alice = decrypt_original(&delegating_sk, &capsule, &ciphertext).unwrap();
229        assert_eq!(&plaintext_alice as &[u8], plaintext);
230
231        // Split Re-Encryption Key Generation (aka Delegation)
232        let verified_kfrags = generate_kfrags(
233            &delegating_sk,
234            &receiving_pk,
235            &signer,
236            threshold,
237            num_frags,
238            true,
239            true,
240        );
241
242        // Bob requests re-encryption to some set of `threshold` ursulas
243
244        // Simulate network transfer
245        let kfrags = verified_kfrags
246            .iter()
247            .cloned()
248            .map(|vkfrag| vkfrag.unverify());
249
250        // If Ursula received kfrags from the network, she must check that they are valid
251        let verified_kfrags: Vec<_> = kfrags
252            .into_iter()
253            .map(|kfrag| {
254                kfrag
255                    .verify(&verifying_pk, Some(&delegating_pk), Some(&receiving_pk))
256                    .unwrap()
257            })
258            .collect();
259
260        let verified_cfrags: Vec<VerifiedCapsuleFrag> = verified_kfrags[0..threshold]
261            .iter()
262            .map(|vkfrag| reencrypt(&capsule, vkfrag.clone()))
263            .collect();
264
265        // Simulate network transfer
266        let cfrags = verified_cfrags
267            .iter()
268            .cloned()
269            .map(|vcfrag| vcfrag.unverify());
270
271        // If Bob received cfrags from the network, he must check that they are valid
272        let verified_cfrags: Vec<_> = cfrags
273            .into_iter()
274            .map(|cfrag| {
275                cfrag
276                    .verify(&capsule, &verifying_pk, &delegating_pk, &receiving_pk)
277                    .unwrap()
278            })
279            .collect();
280
281        // Decryption by Bob
282        let plaintext_bob = decrypt_reencrypted(
283            &receiving_sk,
284            &delegating_pk,
285            &capsule,
286            verified_cfrags,
287            &ciphertext,
288        )
289        .unwrap();
290        assert_eq!(&plaintext_bob as &[u8], plaintext);
291    }
292}