Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion rust/catalyst-voting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ curve25519-dalek = { version = "4.1.3", features = ["digest", "rand_core"] }
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
blake2b_simd = "1.0.2"
rayon = "1.10.0"
minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] }

cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" }

[dev-dependencies]
criterion = "0.5.1"
proptest = { version = "1.5.0" }
proptest = { version = "1.6.0", features = ["attr-macro"] }
# Potentially it could be replaced with using `proptest::property_test` attribute macro,
# after this PR will be merged https://github.com/proptest-rs/proptest/pull/523
test-strategy = "0.4.0"
41 changes: 41 additions & 0 deletions rust/catalyst-voting/src/crypto/elgamal/decoding.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Elgamal objects decoding implementation

use anyhow::anyhow;
use cbork_utils::decode_helper::decode_array_len;
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};

use super::{Ciphertext, GroupElement};

Expand Down Expand Up @@ -34,6 +36,35 @@ impl Ciphertext {
}
}

impl Encode<()> for Ciphertext {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(2)?;
self.0.encode(e, ctx)?;
self.1.encode(e, ctx)
}
}

impl Decode<'_, ()> for Ciphertext {
fn decode(
d: &mut Decoder<'_>,
ctx: &mut (),
) -> Result<Self, minicbor::decode::Error> {
let len = decode_array_len(d, "Ciphertext")?;
if len != 2 {
return Err(minicbor::decode::Error::message(format!(
"Unexpected Ciphertext array length: {len}, expected 2"
)));
}
let c1 = GroupElement::decode(d, ctx)?;
let c2 = GroupElement::decode(d, ctx)?;
Ok(Self(c1, c2))
}
}

#[cfg(test)]
mod tests {
use test_strategy::proptest;
Expand All @@ -46,4 +77,14 @@ mod tests {
let c2 = Ciphertext::from_bytes(&bytes).unwrap();
assert_eq!(c1, c2);
}

#[proptest]
fn cbor_roundtrip(original: Ciphertext) {
let mut buffer = Vec::new();
original
.encode(&mut Encoder::new(&mut buffer), &mut ())
.unwrap();
let decoded = Ciphertext::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
assert_eq!(original, decoded);
}
}
18 changes: 17 additions & 1 deletion rust/catalyst-voting/src/crypto/elgamal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ use std::ops::{Add, Mul};
use crate::crypto::group::{GroupElement, Scalar};

/// `ElGamal` ciphertext, encrypted message with the public key.
#[derive(Debug, Clone, PartialEq, Eq)]
///
/// The CBOR CDDL schema:
/// ```cddl
/// elgamal-ristretto255-encrypted-choice = [
/// c1: elgamal-ristretto255-group-element
/// c2: elgamal-ristretto255-group-element
/// ]
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[must_use]
pub struct Ciphertext(GroupElement, GroupElement);

Expand All @@ -19,6 +27,14 @@ impl Ciphertext {
Ciphertext(GroupElement::zero(), GroupElement::zero())
}

/// Creates a `Ciphertext` instance from the given elements.
pub fn from_elements(
first: GroupElement,
second: GroupElement,
) -> Self {
Self(first, second)
}

/// Get the first element of the `Ciphertext`.
pub fn first(&self) -> &GroupElement {
&self.0
Expand Down
61 changes: 61 additions & 0 deletions rust/catalyst-voting/src/crypto/group/ristretto255/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use anyhow::anyhow;
use curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar as IScalar};
use minicbor::{Decode, Decoder, Encode, Encoder, encode::Write};

use super::{GroupElement, Scalar};

Expand Down Expand Up @@ -48,6 +49,46 @@ impl GroupElement {
}
}

impl Encode<()> for Scalar {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
self.to_bytes().encode(e, ctx)
}
}

impl Decode<'_, ()> for Scalar {
fn decode(
d: &mut Decoder<'_>,
ctx: &mut (),
) -> Result<Self, minicbor::decode::Error> {
let bytes = <[u8; Scalar::BYTES_SIZE]>::decode(d, ctx)?;
Self::from_bytes(bytes).map_err(minicbor::decode::Error::message)
}
}

impl Encode<()> for GroupElement {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
self.to_bytes().encode(e, ctx)
}
}

impl Decode<'_, ()> for GroupElement {
fn decode(
d: &mut Decoder<'_>,
ctx: &mut (),
) -> Result<Self, minicbor::decode::Error> {
let compressed = <[u8; GroupElement::BYTES_SIZE]>::decode(d, ctx)?;
Self::from_bytes(&compressed).map_err(minicbor::decode::Error::message)
}
}

#[cfg(test)]
mod tests {
use test_strategy::proptest;
Expand All @@ -67,4 +108,24 @@ mod tests {
let ge2 = GroupElement::from_bytes(&bytes).unwrap();
assert_eq!(ge1, ge2);
}

#[proptest]
fn scalar_cbor_roundtrip(original: Scalar) {
let mut buffer = Vec::new();
original
.encode(&mut Encoder::new(&mut buffer), &mut ())
.unwrap();
let decoded = Scalar::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
assert_eq!(original, decoded);
}

#[proptest]
fn group_element_cbor_roundtrip(original: GroupElement) {
let mut buffer = Vec::new();
original
.encode(&mut Encoder::new(&mut buffer), &mut ())
.unwrap();
let decoded = GroupElement::decode(&mut Decoder::new(&buffer), &mut ()).unwrap();
assert_eq!(original, decoded);
}
}
12 changes: 11 additions & 1 deletion rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,21 @@ use crate::crypto::{
};

/// Ristretto group scalar.
#[derive(Debug, Clone, PartialEq, Eq)]
///
/// The CBOR CDDL schema:
/// ```cddl
/// zkproof-ed25519-scalar = bytes .size 32
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[must_use]
pub struct Scalar(IScalar);

/// Ristretto group element.
///
/// The CBOR CDDL schema:
/// ```cddl
/// elgamal-ristretto255-group-element = bytes .size 32
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct GroupElement(RistrettoPoint);
Expand Down
Loading