Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8e2d1d4
move vote protocol under the vote_protocol mod
Mr-Leshiy Oct 9, 2024
6af5db8
add jormungandr tx struct
Mr-Leshiy Oct 9, 2024
72351a1
add CipherText serde
Mr-Leshiy Oct 9, 2024
0ee0f95
add EncryptedVote decoding functionality
Mr-Leshiy Oct 9, 2024
fad9fd7
add new deserializers
Mr-Leshiy Oct 9, 2024
c3e3213
refactor
Mr-Leshiy Oct 9, 2024
f37999a
wip
Mr-Leshiy Oct 9, 2024
5b35061
wip
Mr-Leshiy Oct 10, 2024
8ad0075
replace thiserror with anyhow
Mr-Leshiy Oct 10, 2024
8f652bc
move decoding functionalities to separate module
Mr-Leshiy Oct 10, 2024
43b506e
wip
Mr-Leshiy Oct 10, 2024
f40f1a8
add test
Mr-Leshiy Oct 10, 2024
adfccba
fix
Mr-Leshiy Oct 10, 2024
1d7afdf
refactor
Mr-Leshiy Oct 10, 2024
7c670a0
fix tests
Mr-Leshiy Oct 10, 2024
7ab36ba
wip
Mr-Leshiy Oct 10, 2024
3f6160d
Merge branch 'main' into feat/jorm-tx
Mr-Leshiy Oct 10, 2024
bc6d4a9
wip
Mr-Leshiy Oct 11, 2024
ca5ca2a
fix spelling
Mr-Leshiy Oct 11, 2024
ceb11d8
add v1::Tx generation functions
Mr-Leshiy Oct 11, 2024
e5190fd
add test
Mr-Leshiy Oct 11, 2024
38f20f3
refactor, add ElectionPublicKey, ElectionSecretKey
Mr-Leshiy Oct 14, 2024
040730d
fix
Mr-Leshiy Oct 14, 2024
f096bad
fix with must_use
Mr-Leshiy Oct 14, 2024
d063c41
fix docs
Mr-Leshiy Oct 14, 2024
7b17441
refactor digest crate imports
Mr-Leshiy Oct 14, 2024
8873b9b
add ed25519 impl
Mr-Leshiy Oct 14, 2024
f2c95cc
add ed25519 decoding functionality
Mr-Leshiy Oct 14, 2024
b0b94aa
update v1::Tx
Mr-Leshiy Oct 14, 2024
a81277c
add Tx signing
Mr-Leshiy Oct 14, 2024
96f1114
wip
Mr-Leshiy Oct 14, 2024
7f5527b
Merge branch 'main' into feat/jorm-tx-build
Mr-Leshiy Oct 14, 2024
f2e657a
fix
Mr-Leshiy Oct 14, 2024
0b2ac1e
wip
Mr-Leshiy Oct 14, 2024
513965c
add Blake2b-256 hash impl, update v1::Tx sign
Mr-Leshiy Oct 14, 2024
d1820ef
add txs::v1 doc test
Mr-Leshiy Oct 14, 2024
43fe42a
update rust docs
Mr-Leshiy Oct 15, 2024
0993cb0
make rng optional
Mr-Leshiy Oct 15, 2024
6529b79
wip
Mr-Leshiy Oct 15, 2024
d0e870e
update v1::Tx decoding
Mr-Leshiy Oct 15, 2024
fd87a2e
add signature and proof verification
Mr-Leshiy Oct 15, 2024
e59adee
update verification
Mr-Leshiy Oct 15, 2024
2b8588d
update decoding test
Mr-Leshiy Oct 15, 2024
3026d7f
add decrypt_vote function
Mr-Leshiy Oct 16, 2024
ec8eb9d
add private_choice, public_choice methods
Mr-Leshiy Oct 16, 2024
e27d8d1
Merge branch 'main' into feat/jorm-tx-build
Mr-Leshiy Oct 16, 2024
474aa4e
fix spelling
Mr-Leshiy Oct 16, 2024
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
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ CBOR
cbork
cdylib
CEST
chacha
CHAINCODE
chainsync
childs
Expand Down
4 changes: 3 additions & 1 deletion rust/catalyst-voting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ workspace = true
[dependencies]
anyhow = "1.0.89"
rand_core = "0.6.4"
curve25519-dalek = { version = "4.1.3", features = ["digest"] }
rand_chacha = "0.3.1"
curve25519-dalek = { version = "4.1.3", features = ["digest", "rand_core"] }
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
blake2b_simd = "1.0.2"

[dev-dependencies]
Expand Down
42 changes: 42 additions & 0 deletions rust/catalyst-voting/src/crypto/ed25519/decoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! `Ed25519` objects decoding implementation

use ed25519_dalek::{
Signature as Ed25519Signature, VerifyingKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
};

use super::{PublicKey, Signature};

impl PublicKey {
/// `PublicKey` bytes size
pub const BYTES_SIZE: usize = PUBLIC_KEY_LENGTH;

/// Convert this `PublicKey` to its underlying sequence of bytes.
#[must_use]
pub fn to_bytes(&self) -> [u8; Self::BYTES_SIZE] {
self.0.to_bytes()
}

/// Attempt to construct a `PublicKey` from a byte representation.
///
/// # Errors
/// - Cannot decode public key.
pub fn from_bytes(bytes: &[u8; Self::BYTES_SIZE]) -> anyhow::Result<Self> {
Ok(Self(VerifyingKey::from_bytes(bytes)?))
}
}

impl Signature {
/// `Signature` bytes size
pub const BYTES_SIZE: usize = SIGNATURE_LENGTH;

/// Convert this `Signature` to its underlying sequence of bytes.
#[must_use]
pub fn to_bytes(&self) -> [u8; Self::BYTES_SIZE] {
self.0.to_bytes()
}

/// Attempt to construct a `Signature` from a byte representation.
pub fn from_bytes(bytes: &[u8; Self::BYTES_SIZE]) -> Self {
Self(Ed25519Signature::from_bytes(bytes))
}
}
72 changes: 72 additions & 0 deletions rust/catalyst-voting/src/crypto/ed25519/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! `EdDSA` digital signature scheme over Curve25519.

mod decoding;

use ed25519_dalek::{
ed25519::signature::Signer, Signature as Ed25519Signature, SigningKey, VerifyingKey,
};
use rand_core::CryptoRngCore;

/// `Ed25519` private key struct.
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrivateKey(SigningKey);

impl PrivateKey {
/// Randomly generate the `Ed25519` private key.
pub fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
Self(SigningKey::generate(rng))
}

/// Get associated `Ed25519` public key.
pub fn public_key(&self) -> PublicKey {
PublicKey(self.0.verifying_key())
}
}

/// `Ed25519` public key struct.
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey(VerifyingKey);

/// `Ed25519` signature struct.
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature(Ed25519Signature);

/// Sign a message using the `Ed25519` private key.
pub fn sign(sk: &PrivateKey, msg: &[u8]) -> Signature {
Signature(sk.0.sign(msg))
}

/// Verify a `Ed25519` signature using the `Ed25519` public key.
#[must_use]
pub fn verify_signature(pk: &PublicKey, msg: &[u8], sig: &Signature) -> bool {
pk.0.verify_strict(msg, &sig.0).is_ok()
}

#[cfg(test)]
mod tests {
use proptest::prelude::{any, Arbitrary, BoxedStrategy, Strategy};
use test_strategy::proptest;

use super::*;

impl Arbitrary for PrivateKey {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
any::<[u8; 32]>()
.prop_map(|b| PrivateKey(SigningKey::from_bytes(&b)))
.boxed()
}
}

#[proptest]
fn sign_test(private_key: PrivateKey, msg: Vec<u8>) {
let public_key = private_key.public_key();
let signature = sign(&private_key, &msg);
assert!(verify_signature(&public_key, &msg, &signature));
}
}
55 changes: 4 additions & 51 deletions rust/catalyst-voting/src/crypto/elgamal/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,14 @@

use anyhow::anyhow;

use super::{Ciphertext, GroupElement, PublicKey, Scalar, SecretKey};

impl PublicKey {
/// `PublicKey` bytes size
pub const BYTES_SIZE: usize = GroupElement::BYTES_SIZE;

/// Convert this `PublicKey` to its underlying sequence of bytes.
#[must_use]
pub fn to_bytes(&self) -> [u8; Self::BYTES_SIZE] {
self.0.to_bytes()
}

/// Attempt to construct a `PublicKey` from a byte representation.
///
/// # Errors
/// - Cannot decode group element field.
pub fn from_bytes(bytes: &[u8; Self::BYTES_SIZE]) -> anyhow::Result<Self> {
GroupElement::from_bytes(bytes).map(Self)
}
}

impl SecretKey {
/// `SecretKey` bytes size
pub const BYTES_SIZE: usize = Scalar::BYTES_SIZE;

/// Convert this `SecretKey` to its underlying sequence of bytes.
#[must_use]
pub fn to_bytes(&self) -> [u8; Self::BYTES_SIZE] {
self.0.to_bytes()
}

/// Attempt to construct a `SecretKey` from a byte representation.
///
/// # Errors
/// - Cannot decode scalar field.
pub fn from_bytes(bytes: [u8; Self::BYTES_SIZE]) -> anyhow::Result<Self> {
Scalar::from_bytes(bytes).map(Self)
}
}
use super::{Ciphertext, GroupElement};

impl Ciphertext {
/// `Ciphertext` bytes size
pub const BYTES_SIZE: usize = GroupElement::BYTES_SIZE * 2;

/// Convert this `Ciphertext` to its underlying sequence of bytes.
#[must_use]
pub fn to_bytes(&self) -> [u8; Self::BYTES_SIZE] {
let mut res = [0; Self::BYTES_SIZE];
res[0..32].copy_from_slice(&self.0.to_bytes());
Expand All @@ -58,6 +21,8 @@ impl Ciphertext {
///
/// # Errors
/// - Cannot decode group element field.
///
/// # Panics
#[allow(clippy::unwrap_used)]
pub fn from_bytes(bytes: &[u8; Self::BYTES_SIZE]) -> anyhow::Result<Self> {
Ok(Self(
Expand All @@ -75,18 +40,6 @@ mod tests {

use super::*;

#[proptest]
fn keys_to_bytes_from_bytes_test(s1: SecretKey) {
let bytes = s1.to_bytes();
let s2 = SecretKey::from_bytes(bytes).unwrap();
assert_eq!(s1, s2);

let p1 = s1.public_key();
let bytes = p1.to_bytes();
let p2 = PublicKey::from_bytes(&bytes).unwrap();
assert_eq!(p1, p2);
}

#[proptest]
fn ciphertext_to_bytes_from_bytes_test(c1: Ciphertext) {
let bytes = c1.to_bytes();
Expand Down
78 changes: 17 additions & 61 deletions rust/catalyst-voting/src/crypto/elgamal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,17 @@
//! Implementation of the lifted ``ElGamal`` crypto system, and combine with `ChaCha`
//! Implementation of the lifted `ElGamal` crypto system, and combine with `ChaCha`
//! stream cipher to produce a hybrid encryption scheme.

mod decoding;

use std::ops::{Add, Deref, Mul};

use rand_core::CryptoRngCore;
use std::ops::{Add, Mul};

use crate::crypto::group::{GroupElement, Scalar};

/// ``ElGamal`` secret key.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecretKey(Scalar);

/// ``ElGamal`` public key.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PublicKey(GroupElement);

/// ``ElGamal`` ciphertext, encrypted message with the public key.
/// `ElGamal` ciphertext, encrypted message with the public key.
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct Ciphertext(GroupElement, GroupElement);

impl Deref for SecretKey {
type Target = Scalar;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Deref for PublicKey {
type Target = GroupElement;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl SecretKey {
/// Generate a random `SecretKey` value from the random number generator.
pub fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
Self(Scalar::random(rng))
}

/// Generate a corresponding `PublicKey`.
#[must_use]
pub fn public_key(&self) -> PublicKey {
PublicKey(GroupElement::GENERATOR.mul(&self.0))
}
}

impl Ciphertext {
/// Generate a zero `Ciphertext`.
/// The same as encrypt a `Scalar::zero()` message and `Scalar::zero()` randomness.
Expand All @@ -68,19 +30,24 @@ impl Ciphertext {
}
}

/// Generate `ElGamal` public key from the secret key value.
pub fn generate_public_key(secret_key: &Scalar) -> GroupElement {
GroupElement::GENERATOR.mul(secret_key)
}

/// Given a `message` represented as a `Scalar`, return a ciphertext using the
/// lifted ``ElGamal`` mechanism.
/// lifted `ElGamal` mechanism.
/// Returns a ciphertext of type `Ciphertext`.
pub fn encrypt(message: &Scalar, public_key: &PublicKey, randomness: &Scalar) -> Ciphertext {
pub fn encrypt(message: &Scalar, public_key: &GroupElement, randomness: &Scalar) -> Ciphertext {
let e1 = GroupElement::GENERATOR.mul(randomness);
let e2 = &GroupElement::GENERATOR.mul(message) + &public_key.0.mul(randomness);
let e2 = &GroupElement::GENERATOR.mul(message) + &public_key.mul(randomness);
Ciphertext(e1, e2)
}

/// Decrypt ``ElGamal`` `Ciphertext`, returns the original message represented as a
/// Decrypt `ElGamal` `Ciphertext`, returns the original message represented as a
/// `GroupElement`.
pub fn decrypt(cipher: &Ciphertext, secret_key: &SecretKey) -> GroupElement {
&(&cipher.0 * &secret_key.0.negate()) + &cipher.1
pub fn decrypt(cipher: &Ciphertext, secret_key: &Scalar) -> GroupElement {
&(&cipher.0 * &secret_key.negate()) + &cipher.1
}

impl Mul<&Scalar> for &Ciphertext {
Expand Down Expand Up @@ -109,15 +76,6 @@ mod tests {

use super::*;

impl Arbitrary for SecretKey {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
any::<Scalar>().prop_map(SecretKey).boxed()
}
}

impl Arbitrary for Ciphertext {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
Expand Down Expand Up @@ -152,10 +110,8 @@ mod tests {
}

#[proptest]
fn elgamal_encryption_decryption_test(
secret_key: SecretKey, message: Scalar, randomness: Scalar,
) {
let public_key = secret_key.public_key();
fn elgamal_encryption_decryption_test(secret_key: Scalar, message: Scalar, randomness: Scalar) {
let public_key = generate_public_key(&secret_key);

let cipher = encrypt(&message, &public_key, &randomness);
let decrypted = decrypt(&cipher, &secret_key);
Expand Down
21 changes: 14 additions & 7 deletions rust/catalyst-voting/src/crypto/group/ristretto255/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@ use std::{

use curve25519_dalek::{
constants::{RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE},
digest::{consts::U64, Digest},
ristretto::RistrettoPoint as Point,
scalar::Scalar as IScalar,
traits::Identity,
RistrettoPoint,
};
use rand_core::CryptoRngCore;

use crate::crypto::hash::digest::{consts::U64, Digest};

/// Ristretto group scalar.
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct Scalar(IScalar);

/// Ristretto group element.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GroupElement(Point);
#[must_use]
pub struct GroupElement(RistrettoPoint);

impl From<u64> for Scalar {
fn from(value: u64) -> Self {
Expand All @@ -41,9 +44,7 @@ impl Hash for GroupElement {
impl Scalar {
/// Generate a random scalar value from the random number generator.
pub fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar(IScalar::from_bytes_mod_order_wide(&scalar_bytes))
Scalar(IScalar::random(rng))
}

/// additive identity
Expand Down Expand Up @@ -84,7 +85,13 @@ impl GroupElement {

/// Generate a zero group element.
pub fn zero() -> Self {
GroupElement(Point::identity())
GroupElement(RistrettoPoint::identity())
}

/// Generate a `GroupElement` from a hash digest.
pub fn from_hash<D>(hash: D) -> GroupElement
where D: Digest<OutputSize = U64> + Default {
GroupElement(RistrettoPoint::from_hash(hash))
}
}

Expand Down
Loading