Skip to content

Commit

Permalink
fix: eddsa + sha512 pad fixes (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
ratankaliani authored Nov 10, 2023
1 parent 55bb561 commit 51b089b
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 22 deletions.
125 changes: 108 additions & 17 deletions plonky2x/core/src/frontend/ecc/curve25519/ed25519/eddsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ impl<L: PlonkParameters<D>, const D: usize> CircuitBuilder<L, D> {
assert!(signatures.len() == NUM_SIGS);
assert!(pubkeys.len() == NUM_SIGS);

let max_msg_byte_length = self.constant::<U32Variable>(MAX_MSG_LENGTH_BYTES as u32);

let (dummy_pub_key, dummy_sig, dummy_msg, dummy_msg_byte_length) =
self.get_dummy_variables::<MAX_MSG_LENGTH_BYTES>();

Expand All @@ -103,28 +105,27 @@ impl<L: PlonkParameters<D>, const D: usize> CircuitBuilder<L, D> {
msg_vec.push(self.select(is_active[i], messages[i], dummy_msg));
if let Some(ref msg_lens) = message_byte_lengths {
msg_len_vec.push(self.select(is_active[i], msg_lens[i], dummy_msg_byte_length));
} else {
msg_len_vec.push(self.select(
is_active[i],
max_msg_byte_length,
dummy_msg_byte_length,
))
}
sig_vec.push(self.select(is_active[i], signatures[i].clone(), dummy_sig.clone()));
pub_key_vec.push(self.select(is_active[i], pubkeys[i].clone(), dummy_pub_key.clone()));
}

let msg_len_vec = ArrayVariable::<U32Variable, NUM_SIGS>::from(
msg_len_vec.into_iter().collect::<Vec<_>>(),
);
let msg_array =
ArrayVariable::<BytesVariable<MAX_MSG_LENGTH_BYTES>, NUM_SIGS>::from(msg_vec);
let msg_len_array = ArrayVariable::<U32Variable, NUM_SIGS>::from(msg_len_vec);
let sig_array = ArrayVariable::<EDDSASignatureVariable, NUM_SIGS>::from(sig_vec);
let pub_key_array =
ArrayVariable::<CompressedEdwardsYVariable, NUM_SIGS>::from(pub_key_vec);

self.curta_eddsa_verify_sigs(
msg_array,
if message_byte_lengths.is_none() {
None
} else {
Some(msg_len_array)
},
sig_array,
pub_key_array,
);
self.curta_eddsa_verify_sigs(msg_array, Some(msg_len_vec), sig_array, pub_key_array);
}

/// This function will verify a set of eddsa signatures. If message_byte_lengths is None, then
Expand Down Expand Up @@ -210,12 +211,49 @@ mod tests {
use crate::frontend::ecc::curve25519::ed25519::eddsa::{
EDDSASignatureVariable, EDDSASignatureVariableValue,
};
use crate::prelude::{ArrayVariable, BytesVariable, DefaultBuilder, U32Variable};
use crate::prelude::{ArrayVariable, BoolVariable, BytesVariable, DefaultBuilder, U32Variable};
use crate::utils;

const MAX_MSG_LEN_BYTES: usize = 192;
const MAX_MSG_LEN_BYTES: usize = 174;
const NUM_SIGS: usize = 3;

fn test_curta_eddsa_verify_sigs(
test_pub_keys: Vec<CompressedEdwardsY>,
test_signatures: Vec<EDDSASignatureVariableValue<GoldilocksField>>,
test_messages: Vec<[u8; MAX_MSG_LEN_BYTES]>,
test_message_lens: Vec<u32>,
variable_msg_len: bool,
) {
utils::setup_logger();

let mut builder = DefaultBuilder::new();

let pkeys = builder.read::<ArrayVariable<CompressedEdwardsYVariable, NUM_SIGS>>();
let signatures = builder.read::<ArrayVariable<EDDSASignatureVariable, NUM_SIGS>>();
let messages = builder.read::<ArrayVariable<BytesVariable<MAX_MSG_LEN_BYTES>, NUM_SIGS>>();
if variable_msg_len {
let message_lens = builder.read::<ArrayVariable<U32Variable, NUM_SIGS>>();
builder.curta_eddsa_verify_sigs(messages, Some(message_lens), signatures, pkeys);
} else {
builder.curta_eddsa_verify_sigs(messages, None, signatures, pkeys);
}

let circuit = builder.build();

let mut input = circuit.input();
input.write::<ArrayVariable<CompressedEdwardsYVariable, NUM_SIGS>>(test_pub_keys);
input.write::<ArrayVariable<EDDSASignatureVariable, NUM_SIGS>>(test_signatures);
input.write::<ArrayVariable<BytesVariable<MAX_MSG_LEN_BYTES>, NUM_SIGS>>(
test_messages.to_vec(),
);
if variable_msg_len {
input.write::<ArrayVariable<U32Variable, NUM_SIGS>>(test_message_lens);
}

let (proof, output) = circuit.prove(&input);
circuit.verify(&proof, &input, &output);
}

#[test]
#[cfg_attr(feature = "ci", ignore)]
fn test_curta_eddsa_verify_sigs_constant_msg_len() {
Expand Down Expand Up @@ -341,30 +379,40 @@ mod tests {
);
}

fn test_curta_eddsa_verify_sigs(
fn test_curta_eddsa_verify_sigs_conditional(
test_is_active: Vec<bool>,
test_pub_keys: Vec<CompressedEdwardsY>,
test_signatures: Vec<EDDSASignatureVariableValue<GoldilocksField>>,
test_messages: Vec<[u8; 192]>,
test_messages: Vec<[u8; MAX_MSG_LEN_BYTES]>,
test_message_lens: Vec<u32>,
variable_msg_len: bool,
) {
utils::setup_logger();

let mut builder = DefaultBuilder::new();

let is_active = builder.read::<ArrayVariable<BoolVariable, NUM_SIGS>>();
let pkeys = builder.read::<ArrayVariable<CompressedEdwardsYVariable, NUM_SIGS>>();
let signatures = builder.read::<ArrayVariable<EDDSASignatureVariable, NUM_SIGS>>();
let messages = builder.read::<ArrayVariable<BytesVariable<MAX_MSG_LEN_BYTES>, NUM_SIGS>>();
if variable_msg_len {
let message_lens = builder.read::<ArrayVariable<U32Variable, NUM_SIGS>>();
builder.curta_eddsa_verify_sigs(messages, Some(message_lens), signatures, pkeys);
builder.curta_eddsa_verify_sigs_conditional(
is_active,
Some(message_lens),
messages,
signatures,
pkeys,
);
} else {
builder.curta_eddsa_verify_sigs(messages, None, signatures, pkeys);
builder
.curta_eddsa_verify_sigs_conditional(is_active, None, messages, signatures, pkeys);
}

let circuit = builder.build();

let mut input = circuit.input();
input.write::<ArrayVariable<BoolVariable, NUM_SIGS>>(test_is_active);
input.write::<ArrayVariable<CompressedEdwardsYVariable, NUM_SIGS>>(test_pub_keys);
input.write::<ArrayVariable<EDDSASignatureVariable, NUM_SIGS>>(test_signatures);
input.write::<ArrayVariable<BytesVariable<MAX_MSG_LEN_BYTES>, NUM_SIGS>>(
Expand All @@ -377,4 +425,47 @@ mod tests {
let (proof, output) = circuit.prove(&input);
circuit.verify(&proof, &input, &output);
}

#[test]
#[cfg_attr(feature = "ci", ignore)]
fn test_curta_eddsa_verify_sigs_constant_msg_len_conditional() {
// Generate random messages and private keys
let mut test_messages: Vec<[u8; MAX_MSG_LEN_BYTES]> = Vec::new();
let test_message_lens = Vec::new();
let mut test_is_active = Vec::new();
let mut test_pub_keys = Vec::new();
let mut test_signatures = Vec::new();

let mut csprng = OsRng;
for _i in 0..NUM_SIGS {
// Generate random length
let msg_len = MAX_MSG_LEN_BYTES as u32;
let mut test_message = Vec::new();
for _ in 0..msg_len {
test_message.push(rand::thread_rng().gen_range(0..255));
}

let test_signing_key = SigningKey::generate(&mut csprng);
let test_pub_key = test_signing_key.verifying_key();
let test_signature = test_signing_key.sign(&test_message);

test_message.resize(MAX_MSG_LEN_BYTES, 0);
test_messages.push(test_message.try_into().unwrap());
test_is_active.push(true);
test_pub_keys.push(CompressedEdwardsY(test_pub_key.to_bytes()));
test_signatures.push(EDDSASignatureVariableValue {
r: CompressedEdwardsY(*test_signature.r_bytes()),
s: U256::from_little_endian(test_signature.s_bytes()),
});
}

test_curta_eddsa_verify_sigs_conditional(
test_is_active,
test_pub_keys,
test_signatures,
test_messages,
test_message_lens,
false,
);
}
}
40 changes: 36 additions & 4 deletions plonky2x/core/src/frontend/hash/sha/sha512/curta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,21 +306,53 @@ mod tests {
setup_logger();
let mut builder = DefaultBuilder::new();

let max_number_of_chunks = 20;
let max_number_of_chunks = 2;
let total_message_length = 128 * max_number_of_chunks;
let max_len = (total_message_length - 18) / 128;
let max_len = total_message_length - 8;

let mut rng = thread_rng();
let total_message = (0..total_message_length)
.map(|_| rng.gen::<u8>())
.collect::<Vec<_>>();
let message = total_message
.iter()
.map(|b| builder.constant::<ByteVariable>(*b))
.collect::<Vec<_>>();
for i in 0..max_len {
let message = &total_message[..i];
let expected_digest = sha512(message);
let expected_digest = sha512(&total_message[..i]);

let length = builder.constant::<U32Variable>(i as u32);

let digest = builder.curta_sha512_variable(&message, length);
let expected_digest = builder.constant::<BytesVariable<64>>(expected_digest);
builder.assert_is_equal(digest, expected_digest);
}

let circuit = builder.build();
let input = circuit.input();
let (proof, output) = circuit.prove(&input);
circuit.verify(&proof, &input, &output);
}

#[test]
#[cfg_attr(feature = "ci", ignore)]
fn test_sha512_variable_length_max_size() {
// This test checks that sha512_variable_pad works as intended, especially when the max
// input length is (length % 128 > 128 - 17).
setup_logger();
let mut builder = DefaultBuilder::new();

let max_number_of_chunks = 1;
let total_message_length = 128 * max_number_of_chunks;

for i in 127 - 20..total_message_length + 1 {
let mut rng = thread_rng();
let total_message = (0..i).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
let message = total_message
.iter()
.map(|b| builder.constant::<ByteVariable>(*b))
.collect::<Vec<_>>();
let expected_digest = sha512(&total_message);

let length = builder.constant::<U32Variable>(i as u32);

Expand Down
5 changes: 4 additions & 1 deletion plonky2x/core/src/frontend/hash/sha/sha512/pad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ impl<L: PlonkParameters<D>, const D: usize> CircuitBuilder<L, D> {
) -> Vec<ByteVariable> {
let last_chunk = self.compute_sha512_last_chunk(input_byte_length);

// Calculate the number of chunks needed to store the input. 17 is the number of bytes added
// by the padding and LE length representation.
let max_num_chunks = ceil_div_usize(input.len() + 17, SHA512_CHUNK_SIZE_BYTES_128);

// Extend input to size max_num_chunks * 128 before padding.
let max_num_chunks = ceil_div_usize(input.len(), SHA512_CHUNK_SIZE_BYTES_128);
let mut padded_input = input.to_vec();
padded_input.resize(max_num_chunks * SHA512_CHUNK_SIZE_BYTES_128, self.zero());

Expand Down

0 comments on commit 51b089b

Please sign in to comment.