Skip to main content

ironoxide/crypto/
transform.rs

1use crate::internal::{
2    IronOxideErr, PublicKey, WithKey,
3    document_api::{DocAccessEditErr, UserOrGroup},
4};
5use itertools::{Either, Itertools};
6use recrypt::{
7    api::{DerivedSymmetricKey, EncryptedValue, Plaintext, PrivateKey, RecryptErr},
8    prelude::*,
9};
10
11/// Generate a DEK and its associated symmetric key for a new document
12pub fn generate_new_doc_key<CR: rand::CryptoRng + rand::RngCore>(
13    recrypt: &Recrypt<Sha256, Ed25519, RandomBytes<CR>>,
14) -> (Plaintext, DerivedSymmetricKey) {
15    let dek = recrypt.gen_plaintext();
16    let symmetric_key = recrypt.derive_symmetric_key(&dek);
17    (dek, symmetric_key)
18}
19
20/// Generate a plaintext and a key pair necessary to create a new group
21pub fn gen_group_keys<R: CryptoOps + KeyGenOps>(
22    recrypt: &R,
23) -> Result<(Plaintext, PrivateKey, PublicKey), IronOxideErr> {
24    let plaintext = recrypt.gen_plaintext();
25    let priv_key = recrypt.derive_private_key(&plaintext);
26    let pub_key = recrypt.compute_public_key(&priv_key)?;
27    Ok((plaintext, priv_key, pub_key.into()))
28}
29
30/// Decrypt the provided encrypted plaintext and return the symmetric key that is derived from it.
31pub fn decrypt_as_symmetric_key<CR: rand::CryptoRng + rand::RngCore>(
32    recrypt: &Recrypt<Sha256, Ed25519, RandomBytes<CR>>,
33    encrypted_plaintext: EncryptedValue,
34    user_device_private_key: &PrivateKey,
35) -> Result<DerivedSymmetricKey, IronOxideErr> {
36    let plaintext = recrypt.decrypt(encrypted_plaintext, user_device_private_key)?;
37    let symmetric_key = recrypt.derive_symmetric_key(&plaintext);
38    Ok(symmetric_key)
39}
40
41/// Decrypt the provided encrypted plaintext and return both the plaintext and the private key that
42/// is derived from it.
43pub fn decrypt_as_private_key<CR: rand::CryptoRng + rand::RngCore>(
44    recrypt: &Recrypt<Sha256, Ed25519, RandomBytes<CR>>,
45    encrypted_plaintext: EncryptedValue,
46    user_device_private_key: &PrivateKey,
47) -> Result<(Plaintext, PrivateKey), IronOxideErr> {
48    let plaintext = recrypt.decrypt(encrypted_plaintext, user_device_private_key)?;
49    let private_key = recrypt.derive_private_key(&plaintext);
50    Ok((plaintext, private_key))
51}
52
53/// Encrypt the plaintext to all the public keys in the `with_keys` list. If the encryption succeeds, return the values in the right
54/// list. If encryption fails, return them in the left list.
55pub fn encrypt_to_with_key<T, CR: rand::CryptoRng + rand::RngCore>(
56    recrypt: &Recrypt<Sha256, Ed25519, RandomBytes<CR>>,
57    plaintext: &recrypt::api::Plaintext,
58    signing_keys: &recrypt::api::SigningKeypair,
59    with_keys: Vec<WithKey<T>>,
60) -> (
61    Vec<(WithKey<T>, recrypt::api::RecryptErr)>,
62    Vec<(WithKey<T>, recrypt::api::EncryptedValue)>,
63) {
64    //Generate encrypted results for all the users we can. If they error, we'll put them in the acc_fails list.
65    let enc_results_iter = with_keys.into_iter().map(move |key_entry| {
66        let enc_result = recrypt.encrypt(
67            plaintext,
68            &key_entry.public_key.clone().into(),
69            signing_keys,
70        );
71        match enc_result {
72            Ok(recrypt_transform_key) => Either::Right((key_entry, recrypt_transform_key)),
73            Err(e) => Either::Left((key_entry, e)),
74        }
75    });
76    //Now split the failures from the successes, this is done as a separate step
77    //because we can't mutate recrypt in a partition_map call.
78    enc_results_iter.partition_map(std::convert::identity)
79}
80impl From<(WithKey<UserOrGroup>, RecryptErr)> for DocAccessEditErr {
81    fn from((user_or_group, err): (WithKey<UserOrGroup>, RecryptErr)) -> Self {
82        let WithKey { id, .. } = user_or_group;
83        DocAccessEditErr::new(id, format!("Access grant failed with error {err}"))
84    }
85}