Skip to main content

fastcrypto_tbls/
ecies_v1.rs

1// Copyright (c) 2022, Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::ecies::{PrivateKey, PublicKey, RecoveryPackage};
5use crate::nizk::DdhTupleNizk;
6use crate::random_oracle::RandomOracle;
7use fastcrypto::aes::{Aes256Ctr, AesKey, Cipher, InitializationVector};
8use fastcrypto::error::{FastCryptoError, FastCryptoResult};
9use fastcrypto::groups::{FiatShamirChallenge, GroupElement, HashToGroupElement, Scalar};
10use fastcrypto::traits::{AllowedRng, ToFromBytes};
11use serde::{Deserialize, Serialize};
12use typenum::consts::{U16, U32};
13use typenum::Unsigned;
14
15/// Simple ECIES encryption using a generic group and AES-256-counter.
16///
17/// Random oracles are extended from two oracles provided by the caller, one for
18/// encryptions/decryptions and one for recovery packages. We assume that the
19/// caller extended the random oracles with the relevant tags and omit them here.
20///
21/// The encryption uses AES Counter mode and is not CCA secure as is.
22///
23
24// TODO: move PrivateKey and PublicKey here and remove old APIs
25
26/// Multi-recipient encryption with a proof-of-possession of the ephemeral key.
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct MultiRecipientEncryption<G: GroupElement> {
29    c: G,
30    c_hat: G,
31    encs: Vec<Vec<u8>>,
32    pub proof: DdhTupleNizk<G>,
33}
34
35impl<G: GroupElement + Serialize> MultiRecipientEncryption<G>
36where
37    <G as GroupElement>::ScalarType: FiatShamirChallenge,
38    G: HashToGroupElement,
39{
40    pub fn encrypt<R: AllowedRng>(
41        pk_and_msgs: &[(PublicKey<G>, Vec<u8>)],
42        encryption_random_oracle: &RandomOracle,
43        rng: &mut R,
44    ) -> MultiRecipientEncryption<G> {
45        let r = G::ScalarType::rand(rng);
46        let c = G::generator() * r;
47        let g_hat = G::hash_to_group_element(
48            &Self::g_hat_random_oracle(encryption_random_oracle).evaluate(&c),
49        );
50        let c_hat = g_hat * r;
51        let proof = DdhTupleNizk::<G>::create(
52            &r,
53            &g_hat,
54            &c,
55            &c_hat,
56            &Self::zk_random_oracle(encryption_random_oracle),
57            rng,
58        );
59
60        let encs_ro = Self::encs_random_oracle(encryption_random_oracle);
61        let encs = pk_and_msgs
62            .iter()
63            .enumerate()
64            .map(|(receiver_index, (receiver_pk, msg))| {
65                let pk_r = receiver_pk.0 * r;
66                let k = encs_ro.evaluate(&(receiver_index, pk_r));
67                let cipher = sym_cipher(&k);
68                // Since k is fresh per encryption, we can safely use a fixed nonce.
69                cipher.encrypt(&fixed_zero_nonce(), msg)
70            })
71            .collect::<Vec<_>>();
72
73        MultiRecipientEncryption {
74            c,
75            c_hat,
76            encs,
77            proof,
78        }
79    }
80
81    pub fn verify(&self, encryption_random_oracle: &RandomOracle) -> FastCryptoResult<()> {
82        let g_hat = G::hash_to_group_element(
83            &Self::g_hat_random_oracle(encryption_random_oracle).evaluate(&self.c),
84        );
85        self.proof.verify(
86            &g_hat,
87            &self.c,
88            &self.c_hat,
89            &Self::zk_random_oracle(encryption_random_oracle),
90        )?;
91        // Encryptions should not be empty.
92        self.encs
93            .iter()
94            .all(|e| !e.is_empty())
95            .then_some(())
96            .ok_or(FastCryptoError::InvalidInput)
97    }
98
99    /// We assume that verify is called before decrypt and do not call it again here to
100    /// avoid redundant checks.
101    pub fn decrypt(
102        &self,
103        sk: &PrivateKey<G>,
104        encryption_random_oracle: &RandomOracle,
105        receiver_index: usize,
106    ) -> Vec<u8> {
107        let enc_ro = Self::encs_random_oracle(encryption_random_oracle);
108        let ephemeral_key = self.c * sk.0;
109        let k = enc_ro.evaluate(&(receiver_index, ephemeral_key));
110        let cipher = sym_cipher(&k);
111        cipher
112            .decrypt(&fixed_zero_nonce(), &self.encs[receiver_index])
113            .expect("Decrypt should never fail for CTR mode")
114    }
115
116    /// We assume that verify is called before decrypt and do not call it again here to
117    /// avoid redundant checks.
118    pub fn create_recovery_package<R: AllowedRng>(
119        &self,
120        sk: &PrivateKey<G>,
121        recovery_random_oracle: &RandomOracle,
122        rng: &mut R,
123    ) -> RecoveryPackage<G> {
124        let pk = G::generator() * sk.0;
125        let ephemeral_key = self.c * sk.0;
126
127        let proof = DdhTupleNizk::<G>::create(
128            &sk.0,
129            &self.c,
130            &pk,
131            &ephemeral_key,
132            recovery_random_oracle,
133            rng,
134        );
135
136        RecoveryPackage {
137            ephemeral_key,
138            proof,
139        }
140    }
141
142    pub fn decrypt_with_recovery_package(
143        &self,
144        pkg: &RecoveryPackage<G>,
145        recovery_random_oracle: &RandomOracle,
146        encryption_random_oracle: &RandomOracle,
147        receiver_pk: &PublicKey<G>,
148        receiver_index: usize,
149    ) -> FastCryptoResult<Vec<u8>> {
150        assert!(receiver_index < self.encs.len());
151        pkg.proof.verify(
152            &self.c,
153            &receiver_pk.0,
154            &pkg.ephemeral_key,
155            recovery_random_oracle,
156        )?;
157        let encs_ro = Self::encs_random_oracle(encryption_random_oracle);
158        let k = encs_ro.evaluate(&(receiver_index, pkg.ephemeral_key));
159        let cipher = sym_cipher(&k);
160        Ok(cipher
161            .decrypt(&fixed_zero_nonce(), &self.encs[receiver_index])
162            .expect("Decrypt should never fail for CTR mode"))
163    }
164
165    pub fn len(&self) -> usize {
166        self.encs.len()
167    }
168    pub fn is_empty(&self) -> bool {
169        self.encs.is_empty()
170    }
171
172    // Used for debugging
173    pub fn ephemeral_key(&self) -> &G {
174        &self.c
175    }
176
177    // Used for debugging
178    pub fn proof(&self) -> &DdhTupleNizk<G> {
179        &self.proof
180    }
181
182    fn encs_random_oracle(encryption_random_oracle: &RandomOracle) -> RandomOracle {
183        encryption_random_oracle.extend("encs")
184    }
185
186    fn zk_random_oracle(encryption_random_oracle: &RandomOracle) -> RandomOracle {
187        encryption_random_oracle.extend("zk")
188    }
189
190    fn g_hat_random_oracle(encryption_random_oracle: &RandomOracle) -> RandomOracle {
191        encryption_random_oracle.extend("g_hat")
192    }
193
194    #[cfg(test)]
195    pub fn modify_c_hat_for_testing(&mut self, c_hat: G) {
196        self.c_hat = c_hat;
197    }
198
199    #[cfg(test)]
200    pub fn copy_for_testing(&mut self, src: usize, dst: usize) {
201        self.encs[dst] = self.encs[src].clone();
202    }
203}
204
205fn fixed_zero_nonce() -> InitializationVector<U16> {
206    InitializationVector::<U16>::from_bytes(&[0u8; 16])
207        .expect("U16 could always be set from a 16 bytes array of zeros")
208}
209
210fn sym_cipher(k: &[u8; 64]) -> Aes256Ctr {
211    Aes256Ctr::new(
212        AesKey::<U32>::from_bytes(&k[0..U32::USIZE])
213            .expect("New shouldn't fail as use fixed size key is used"),
214    )
215}