Skip to content

Commit

Permalink
Merge pull request #33 from anoma/xuyang/refactor_architecture
Browse files Browse the repository at this point in the history
architecture refactor
  • Loading branch information
XuyangSong authored Nov 25, 2024
2 parents 0e5dd1a + ccaff17 commit 187280f
Show file tree
Hide file tree
Showing 29 changed files with 566 additions and 540 deletions.
4 changes: 2 additions & 2 deletions bench/compliance_circuit_bench.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{:ok, program} = File.read("./native/cairo_vm/compliance.json")
{:ok, input} = File.read("./native/cairo_vm/compliance_input.json")
{:ok, program} = File.read("./juvix/compliance.json")
{:ok, input} = File.read("./juvix/compliance_input.json")

{_output, trace, memory, public_inputs} =
Cairo.cairo_vm_runner(
Expand Down
4 changes: 2 additions & 2 deletions bench/logic_circuit_bench.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{:ok, program} = File.read("./native/cairo_vm/trivial_resource_logic.json")
{:ok, program} = File.read("./juvix/trivial_resource_logic.json")

{:ok, input} =
File.read("./native/cairo_vm/trivial_resource_logic_input.json")
File.read("./juvix/trivial_resource_logic_input.json")

{_output, trace, memory, public_inputs} =
Cairo.cairo_vm_runner(
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
117 changes: 117 additions & 0 deletions native/cairo_prover/src/binding_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use crate::{
error::CairoError,
utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec},
};
use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::Zero;
use rand::{thread_rng, RngCore};
use rustler::NifResult;
use starknet_crypto::{poseidon_hash_many, sign, verify};
use starknet_curve::curve_params::{EC_ORDER, GENERATOR};
use starknet_types_core::{curve::ProjectivePoint, felt::Felt};
use std::ops::Add;

// The private_key_segments are random values used in delta commitments.
// The messages are nullifiers and resource commitments in the transaction.
#[rustler::nif]
fn cairo_binding_sig_sign(
private_key_segments: Vec<u8>,
messages: Vec<Vec<u8>>,
) -> NifResult<Vec<u8>> {
if private_key_segments.is_empty() || private_key_segments.len() % 32 != 0 {
return Err(CairoError::InvalidInputs.into());
}
// Compute private key
let private_key = {
let result = private_key_segments
.chunks(32)
.fold(BigInt::zero(), |acc, key_segment| {
let key = BigInt::from_bytes_be(num_bigint::Sign::Plus, key_segment);
acc.add(key)
})
.mod_floor(&EC_ORDER.to_bigint());

let (_, buffer) = result.to_bytes_be();
let mut result = [0u8; 32];
result[(32 - buffer.len())..].copy_from_slice(&buffer[..]);

Felt::from_bytes_be(&result)
};

// Message digest
let sig_hash = message_digest(messages)?;

// ECDSA sign
let mut rng = thread_rng();
let k = {
let mut felt: [u8; 32] = Default::default();
rng.fill_bytes(&mut felt);
Felt::from_bytes_be(&felt)
};
let signature = sign(&private_key, &sig_hash, &k).map_err(CairoError::from)?;

// Serialize signature
let mut ret = Vec::new();
ret.extend(signature.r.to_bytes_be());
ret.extend(signature.s.to_bytes_be());
// We don't need the v to recover pubkey
// ret.extend(signature.v.to_bytes_be());
Ok(ret)
}

// The pub_key_segments are delta commitments in compliance input inputs.
#[rustler::nif]
fn cairo_binding_sig_verify(
pub_key_segments: Vec<Vec<u8>>,
messages: Vec<Vec<u8>>,
signature: Vec<u8>,
) -> NifResult<bool> {
// Generate the public key
let mut pub_key = ProjectivePoint::identity();
for pk_seg_bytes in pub_key_segments.into_iter() {
let pk_seg = bytes_to_affine(pk_seg_bytes)?;
pub_key += pk_seg;
}
let pub_key_x = pub_key
.to_affine()
.map_err(|_| CairoError::InvalidAffinePoint)?
.x();

// Message digest
let msg = message_digest(messages)?;

// Decode the signature
if signature.len() != 64 {
return Err(CairoError::InvalidSignatureFormat.into());
}

let (r_bytes, s_bytes) = signature.split_at(32);
let r = bytes_to_felt(r_bytes.to_vec())?;
let s = bytes_to_felt(s_bytes.to_vec())?;

// Verify the signature
verify(&pub_key_x, &msg, &r, &s).map_err(|_| CairoError::SigVerifyError.into())
}

#[rustler::nif]
fn get_public_key(priv_key: Vec<u8>) -> NifResult<Vec<u8>> {
let priv_key_felt = bytes_to_felt(priv_key)?;

let generator = ProjectivePoint::from_affine(GENERATOR.x(), GENERATOR.y())
.map_err(|_| CairoError::InvalidAffinePoint)?;

let pub_key = (&generator * priv_key_felt)
.to_affine()
.map_err(|_| CairoError::InvalidAffinePoint)?;

let mut ret = pub_key.x().to_bytes_be().to_vec();
let mut y = pub_key.y().to_bytes_be().to_vec();
ret.append(&mut y);
Ok(ret)
}

fn message_digest(msg: Vec<Vec<u8>>) -> NifResult<Felt> {
let felt_msg_vec: Vec<Felt> = bytes_to_felt_vec(msg)?;
Ok(poseidon_hash_many(&felt_msg_vec))
}
34 changes: 34 additions & 0 deletions native/cairo_prover/src/compliance_input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
use crate::{error::CairoError, utils::felt_to_string};
use rustler::NifResult;
use serde::{Deserialize, Serialize};

#[rustler::nif]
fn cairo_generate_compliance_input_json(
input_resource: Vec<u8>,
output_resource: Vec<u8>,
path: Vec<Vec<u8>>,
pos: u64,
input_nf_key: Vec<u8>,
eph_root: Vec<u8>,
rcv: Vec<u8>,
) -> NifResult<String> {
Ok(ComplianceInputJson::to_json_string(
input_resource,
output_resource,
path,
pos,
input_nf_key,
eph_root,
rcv,
)?)
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ComplianceInputJson {
input: ResourceJson,
Expand Down Expand Up @@ -108,3 +130,15 @@ fn test_compliance_input_json() {

println!("compliance_input_json: {}", json);
}

#[test]
fn generate_compliance_input_test_params() {
use starknet_crypto::poseidon_hash;
use starknet_types_core::felt::Felt;

println!("Felf one hex: {:?}", Felt::ONE.to_hex_string());
let input_nf_key = Felt::ONE;
let input_npk = poseidon_hash(input_nf_key, Felt::ZERO);
println!("input_npk: {:?}", input_npk.to_bytes_be());
println!("input_npk: {:?}", input_npk.to_hex_string());
}
43 changes: 43 additions & 0 deletions native/cairo_prover/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use lazy_static::lazy_static;

// The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics
// Make it 10
pub const PLAINTEXT_NUM: usize = 10;
pub const CIPHERTEXT_MAC: usize = PLAINTEXT_NUM;
pub const CIPHERTEXT_PK_X: usize = PLAINTEXT_NUM + 1;
pub const CIPHERTEXT_PK_Y: usize = PLAINTEXT_NUM + 2;
pub const CIPHERTEXT_NONCE: usize = PLAINTEXT_NUM + 3;
pub const CIPHERTEXT_NUM: usize = PLAINTEXT_NUM + 4;

lazy_static! {
// Bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 97, 105, 114, 111, 95, 69, 120, 112, 97, 110, 100, 83, 101, 101, 100]
// Hexstring: "0x436169726f5f457870616e6453656564"
// Decimal string(used in juvix): "89564067232354163924078705540990330212"
pub static ref PRF_EXPAND_PERSONALIZATION_FELT: Vec<u8> = {
let personalization: Vec<u8> = b"Cairo_ExpandSeed".to_vec();
let mut result = [0u8; 32];
result[(32 - personalization.len())..].copy_from_slice(&personalization[..]);

result.to_vec()
};
}

#[test]
fn test_prf_expand_personalization() {
use starknet_types_core::felt::Felt;
println!(
"PRF_EXPAND_PERSONALIZATION_FELT bytes: {:?}",
*PRF_EXPAND_PERSONALIZATION_FELT
);

println!(
"hex: {:?}",
Felt::from_bytes_be(
&PRF_EXPAND_PERSONALIZATION_FELT
.as_slice()
.try_into()
.unwrap()
)
.to_hex_string()
);
}
68 changes: 54 additions & 14 deletions native/cairo_prover/src/encryption.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,63 @@
use crate::{error::CairoError, utils::bytes_to_felt_vec};
use crate::{
constants::{
CIPHERTEXT_MAC, CIPHERTEXT_NONCE, CIPHERTEXT_NUM, CIPHERTEXT_PK_X, CIPHERTEXT_PK_Y,
PLAINTEXT_NUM,
},
error::CairoError,
utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec},
};
use rustler::NifResult;
use starknet_crypto::{poseidon_hash, poseidon_hash_many};
use starknet_curve::curve_params::GENERATOR;
use starknet_types_core::{
curve::{AffinePoint, ProjectivePoint},
felt::Felt,
};

// The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics
// Make it 10
pub const PLAINTEXT_NUM: usize = 10;
pub const CIPHERTEXT_MAC: usize = PLAINTEXT_NUM;
pub const CIPHERTEXT_PK_X: usize = PLAINTEXT_NUM + 1;
pub const CIPHERTEXT_PK_Y: usize = PLAINTEXT_NUM + 2;
pub const CIPHERTEXT_NONCE: usize = PLAINTEXT_NUM + 3;
pub const CIPHERTEXT_NUM: usize = PLAINTEXT_NUM + 4;
#[rustler::nif]
fn encrypt(
messages: Vec<Vec<u8>>,
pk: Vec<u8>,
sk: Vec<u8>,
nonce: Vec<u8>,
) -> NifResult<Vec<Vec<u8>>> {
// Decode messages
let msgs_felt = bytes_to_felt_vec(messages)?;

// Decode pk
let pk_affine = bytes_to_affine(pk)?;

// Decode sk
let sk_felt = bytes_to_felt(sk)?;

// Decode nonce
let nonce_felt = bytes_to_felt(nonce)?;

// Encrypt
let cipher = Ciphertext::encrypt(&msgs_felt, &pk_affine, &sk_felt, &nonce_felt)?;
let cipher_bytes = cipher
.inner()
.iter()
.map(|x| x.to_bytes_be().to_vec())
.collect();

Ok(cipher_bytes)
}

#[rustler::nif]
fn decrypt(cihper: Vec<Vec<u8>>, sk: Vec<u8>) -> NifResult<Vec<Vec<u8>>> {
// Decode messages
let cipher = Ciphertext::from_bytes(cihper)?;

// Decode sk
let sk_felt = bytes_to_felt(sk)?;

// Encrypt
let plaintext = cipher.decrypt(&sk_felt)?;
let plaintext_bytes = plaintext.iter().map(|x| x.to_bytes_be().to_vec()).collect();

Ok(plaintext_bytes)
}

#[derive(Debug, Clone)]
pub struct Ciphertext([Felt; CIPHERTEXT_NUM]);
Expand Down Expand Up @@ -132,10 +176,6 @@ impl Plaintext {
&self.0
}

pub fn to_vec(&self) -> Vec<Felt> {
self.0.to_vec()
}

pub fn padding(msg: &[Felt]) -> Self {
let mut plaintext = msg.to_owned();
let padding = std::iter::repeat(Felt::ZERO).take(PLAINTEXT_NUM - msg.len());
Expand Down Expand Up @@ -186,5 +226,5 @@ fn test_encryption() {
let decryption = cipher.decrypt(&Felt::ONE).unwrap();

let padded_plaintext = Plaintext::padding(&messages);
assert_eq!(padded_plaintext.to_vec(), decryption);
assert_eq!(padded_plaintext.inner().to_vec(), decryption);
}
Loading

0 comments on commit 187280f

Please sign in to comment.