Skip to content

Commit 2fd763e

Browse files
authored
Merge pull request #28 from anoma/xuyang/verifiable_encryption
verifiable encryption
2 parents 7cbe9a0 + d58d302 commit 2fd763e

File tree

10 files changed

+501
-35
lines changed

10 files changed

+501
-35
lines changed

lib/cairo.ex

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ defmodule Cairo do
6262
list(byte()) | {:error, term()}
6363
defdelegate get_public_key(priv_key),
6464
to: Cairo.CairoProver,
65-
as: :cairo_get_binding_sig_public_key
65+
as: :get_public_key
6666

6767
@spec poseidon_single(list(byte())) ::
6868
list(byte()) | {:error, term()}
@@ -114,4 +114,16 @@ defmodule Cairo do
114114
),
115115
to: Cairo.CairoProver,
116116
as: :cairo_generate_compliance_input_json
117+
118+
@spec encrypt(list(list(byte())), list(byte()), list(byte()), list(byte())) ::
119+
list(byte()) | {:error, term()}
120+
defdelegate encrypt(messages, pk, sk, nonce),
121+
to: Cairo.CairoProver,
122+
as: :encrypt
123+
124+
@spec decrypt(list(list(byte())), list(byte())) ::
125+
list(byte()) | {:error, term()}
126+
defdelegate decrypt(cihper, sk),
127+
to: Cairo.CairoProver,
128+
as: :decrypt
117129
end

lib/cairo/cairo.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ defmodule Cairo.CairoProver do
3535
@spec cairo_random_felt() :: nif_result(list(byte()))
3636
def cairo_random_felt(), do: error()
3737

38-
@spec cairo_get_binding_sig_public_key(list(byte())) ::
38+
@spec get_public_key(list(byte())) ::
3939
nif_result(list(byte()))
40-
def cairo_get_binding_sig_public_key(_priv_key), do: error()
40+
def get_public_key(_priv_key), do: error()
4141

4242
@spec poseidon_single(list(byte())) :: nif_result(list(byte()))
4343
def poseidon_single(_input), do: error()
@@ -64,6 +64,13 @@ defmodule Cairo.CairoProver do
6464
),
6565
do: error()
6666

67+
@spec encrypt(list(list(byte())), list(byte()), list(byte()), list(byte())) ::
68+
nif_result(list(byte()))
69+
def encrypt(_messages, _pk, _sk, _nonce), do: error()
70+
71+
@spec decrypt(list(list(byte())), list(byte())) :: nif_result(list(byte()))
72+
def decrypt(_cihper, _sk), do: error()
73+
6774
defp error, do: :erlang.nif_error(:nif_not_loaded)
6875
end
6976

native/cairo_prover/src/encryption.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use starknet_crypto::{poseidon_hash, poseidon_hash_many};
2+
use starknet_curve::curve_params::GENERATOR;
3+
use starknet_types_core::{
4+
curve::{AffinePoint, ProjectivePoint},
5+
felt::Felt,
6+
};
7+
8+
// The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics
9+
// Make it 10
10+
pub const PLAINTEXT_NUM: usize = 10;
11+
pub const CIPHERTEXT_MAC: usize = PLAINTEXT_NUM;
12+
pub const CIPHERTEXT_PK_X: usize = PLAINTEXT_NUM + 1;
13+
pub const CIPHERTEXT_PK_Y: usize = PLAINTEXT_NUM + 2;
14+
pub const CIPHERTEXT_NONCE: usize = PLAINTEXT_NUM + 3;
15+
pub const CIPHERTEXT_NUM: usize = PLAINTEXT_NUM + 4;
16+
17+
#[derive(Debug, Clone)]
18+
pub struct Ciphertext([Felt; CIPHERTEXT_NUM]);
19+
20+
#[derive(Debug, Clone)]
21+
pub struct Plaintext([Felt; PLAINTEXT_NUM]);
22+
23+
// Symmetric encryption key
24+
#[derive(Debug, Clone)]
25+
pub struct SecretKey(AffinePoint);
26+
27+
impl Ciphertext {
28+
pub fn inner(&self) -> &[Felt; CIPHERTEXT_NUM] {
29+
&self.0
30+
}
31+
32+
pub fn encrypt(messages: &[Felt], pk: &AffinePoint, sk: &Felt, encrypt_nonce: &Felt) -> Self {
33+
// Generate the secret key
34+
let (secret_key_x, secret_key_y) = SecretKey::from_dh_exchange(pk, sk).get_coordinates();
35+
36+
// Pad the messages
37+
let plaintext = Plaintext::padding(messages);
38+
39+
// Init poseidon state
40+
let mut poseidon_state = poseidon_hash_many(&vec![
41+
secret_key_x,
42+
secret_key_y,
43+
*encrypt_nonce,
44+
Felt::from(PLAINTEXT_NUM),
45+
]);
46+
47+
// Encrypt
48+
let mut cipher = vec![];
49+
plaintext.inner().iter().for_each(|f| {
50+
poseidon_state += f;
51+
cipher.push(poseidon_state);
52+
poseidon_state = poseidon_hash(poseidon_state, secret_key_x);
53+
});
54+
55+
// Add MAC
56+
cipher.push(poseidon_state);
57+
58+
// Add sender's public key
59+
let generator = ProjectivePoint::from_affine(GENERATOR.x(), GENERATOR.y()).unwrap();
60+
let sender_pk = (&generator * *sk).to_affine().unwrap();
61+
cipher.push(sender_pk.x());
62+
cipher.push(sender_pk.y());
63+
64+
// Add encrypt_nonce
65+
cipher.push(*encrypt_nonce);
66+
67+
cipher.into()
68+
}
69+
70+
pub fn decrypt(&self, sk: &Felt) -> Option<Vec<Felt>> {
71+
let cipher_text = self.inner();
72+
let cipher_len = cipher_text.len();
73+
if cipher_len != CIPHERTEXT_NUM {
74+
return None;
75+
}
76+
77+
let mac = cipher_text[CIPHERTEXT_MAC];
78+
let pk_x = cipher_text[CIPHERTEXT_PK_X];
79+
let pk_y = cipher_text[CIPHERTEXT_PK_Y];
80+
let encrypt_nonce = cipher_text[CIPHERTEXT_NONCE];
81+
82+
if let Ok(pk) = AffinePoint::new(pk_x, pk_y) {
83+
// Generate the secret key
84+
let (secret_key_x, secret_key_y) =
85+
SecretKey::from_dh_exchange(&pk, sk).get_coordinates();
86+
87+
// Init poseidon sponge state
88+
let mut poseidon_state = poseidon_hash_many(&vec![
89+
secret_key_x,
90+
secret_key_y,
91+
encrypt_nonce,
92+
Felt::from(PLAINTEXT_NUM),
93+
]);
94+
95+
// Decrypt
96+
let mut msg = vec![];
97+
for cipher_element in &cipher_text[0..PLAINTEXT_NUM] {
98+
let msg_element = *cipher_element - poseidon_state;
99+
msg.push(msg_element);
100+
poseidon_state = poseidon_hash(*cipher_element, secret_key_x);
101+
}
102+
103+
if mac != poseidon_state {
104+
return None;
105+
}
106+
107+
Some(msg)
108+
} else {
109+
return None;
110+
}
111+
}
112+
}
113+
114+
impl From<Vec<Felt>> for Ciphertext {
115+
fn from(input_vec: Vec<Felt>) -> Self {
116+
Ciphertext(
117+
input_vec
118+
.try_into()
119+
.expect("public input with incorrect length"),
120+
)
121+
}
122+
}
123+
124+
impl Plaintext {
125+
pub fn inner(&self) -> &[Felt; PLAINTEXT_NUM] {
126+
&self.0
127+
}
128+
129+
pub fn to_vec(&self) -> Vec<Felt> {
130+
self.0.to_vec()
131+
}
132+
133+
pub fn padding(msg: &[Felt]) -> Self {
134+
let mut plaintext = msg.to_owned();
135+
let padding = std::iter::repeat(Felt::ZERO).take(PLAINTEXT_NUM - msg.len());
136+
plaintext.extend(padding);
137+
plaintext.into()
138+
}
139+
}
140+
141+
impl From<Vec<Felt>> for Plaintext {
142+
fn from(input_vec: Vec<Felt>) -> Self {
143+
Plaintext(
144+
input_vec
145+
.try_into()
146+
.expect("public input with incorrect length"),
147+
)
148+
}
149+
}
150+
151+
impl SecretKey {
152+
pub fn from_dh_exchange(pk: &AffinePoint, sk: &Felt) -> Self {
153+
Self(
154+
(&ProjectivePoint::try_from(pk.clone()).unwrap() * *sk)
155+
.to_affine()
156+
.unwrap(),
157+
)
158+
}
159+
160+
pub fn get_coordinates(&self) -> (Felt, Felt) {
161+
(self.0.x(), self.0.y())
162+
}
163+
}
164+
165+
#[test]
166+
fn test_encryption() {
167+
// Key generation
168+
let sender_sk = Felt::ONE;
169+
let pk = GENERATOR;
170+
171+
// let key = SecretKey::from_dh_exchange(pk, random_sk);
172+
let messages = [Felt::ONE, Felt::ZERO, Felt::ONE];
173+
let encrypt_nonce = Felt::ONE;
174+
175+
// Encryption
176+
let cipher = Ciphertext::encrypt(&messages, &pk, &sender_sk, &encrypt_nonce);
177+
178+
// Decryption
179+
let decryption = cipher.decrypt(&Felt::ONE).unwrap();
180+
181+
let padded_plaintext = Plaintext::padding(&messages);
182+
assert_eq!(padded_plaintext.to_vec(), decryption);
183+
}

native/cairo_prover/src/errors.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,22 @@ impl Encoder for CairoBindingSigError {
143143
self.to_string().encode(env)
144144
}
145145
}
146+
147+
#[derive(Debug)]
148+
pub enum TypeError {
149+
DecodingError(String),
150+
}
151+
152+
impl std::fmt::Display for TypeError {
153+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
154+
match self {
155+
TypeError::DecodingError(msg) => write!(f, "Type error: {}", msg),
156+
}
157+
}
158+
}
159+
160+
impl Encoder for TypeError {
161+
fn encode<'a>(&self, env: Env<'a>) -> Term<'a> {
162+
self.to_string().encode(env)
163+
}
164+
}

0 commit comments

Comments
 (0)