Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
11 changes: 9 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ rayon = "1.10.0"
[features]
kat-tests = []

# [[bench]]
# name = "bench_velu"
# path = "benches/bench_velu.rs"
# harness = false


[[bench]]
name = "bench_velu"
path = "benches/bench_velu.rs"
name = "bench_csidh"
path = "benches/bench_csidh.rs"
harness = false


# [[bench]]
# name = "bench_sidh"
# path = "benches/bench_sidh.rs"
Expand Down
28 changes: 28 additions & 0 deletions benches/bench_csidh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
mod benchmark_csidh {
use isogeny::protocols::csidh_parameters::CSIDH_512;
use isogeny::utilities::test_utils::drng::DRNG;

use criterion::{Criterion, criterion_group};
use std::time::Duration;

fn benchmark_action(c: &mut Criterion) {
let mut rng = DRNG::from_seed("csidh_action".as_bytes());
let bench_id = format!("Benchmarking Action for CSIDH-512 Parameters",);

let (alice_sk, alice_pk) = CSIDH_512.keygen(&mut rng);

// for simplicity, we just use alice pk and sk
c.bench_function(&bench_id, |b| b.iter(|| CSIDH_512.derive_shared_key(&alice_pk, &alice_sk, &mut rng)));
}


criterion_group! {
name = csidh_benchmarks;
config = Criterion::default().measurement_time(Duration::from_secs(15));
targets = benchmark_action,
}
}

fn main() {
benchmark_csidh::csidh_benchmarks();
}
7 changes: 7 additions & 0 deletions src/elliptic/point.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use fp2::traits::Fp as FpTrait;
use rand_core::{CryptoRng, RngCore};

/// Special x-only representation of a point (or a pair of points,
/// since two Y coordinates may match a given X).
Expand Down Expand Up @@ -64,6 +65,12 @@ impl<Fq: FpTrait> PointX<Fq> {
P.Z = Fq::ONE;
}
}

/// Return a new random X only point.
pub fn rand_point<R: CryptoRng + RngCore>(rng: &mut R) -> PointX<Fq> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesnt always return a valid point on the curve... I think we should just delete this for now as it might be confusing?

let x = Fq::rand(rng);
PointX::from_x_coord(&x)
}
}

impl<Fq: FpTrait> ::std::fmt::Display for PointX<Fq> {
Expand Down
2 changes: 1 addition & 1 deletion src/elliptic/velu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl<Fq: FqTrait> Curve<Fq> {

/// P3 <- n*P, x-only variant using (A24 : C24).
/// Integer n is represented as a u64 and is assumed to be public.
fn set_xmul_proj_u64_vartime(A24: &Fq, C24: &Fq, P3: &mut PointX<Fq>, P: &PointX<Fq>, n: u64) {
pub fn set_xmul_proj_u64_vartime(A24: &Fq, C24: &Fq, P3: &mut PointX<Fq>, P: &PointX<Fq>, n: u64) {
// Handle small cases.
match n {
0 => {
Expand Down
20 changes: 20 additions & 0 deletions src/fields/csidh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// CSIDH - 512
const CSIDH_512_MODULUS: [u64; 8] = [
0x1B81B90533C6C87B,
0xC2721BF457ACA835,
0x516730CC1F0B4F25,
0xA7AAC6C567F35507,
0x5AFBFCC69322C9CD,
0xB42D083AEDC88C42,
0xFC8AB0D15E3E4C4A,
0x65B48E8F740F89BF,
];

fp2::define_fp_core!(typename = Csidh512, modulus = CSIDH_512_MODULUS,);

#[cfg(test)]
mod test_csidh_512_arithmetic {
use super::Csidh512;

fp2::define_fp_tests!(Csidh512);
}
2 changes: 2 additions & 0 deletions src/fields/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub mod csidh;
pub mod sike;
pub mod sqisign;

192 changes: 192 additions & 0 deletions src/protocols/csidh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@

use fp2::traits::Fp as FqTrait;

use crate::elliptic::{curve::Curve, point::PointX};

use rand_core::{CryptoRng, RngCore};

#[derive(Clone, Copy, Debug)]
pub struct CsidhParameters<const COUNT: usize> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename to PRIMES or PRIME_COUNT?

pub max_exponent: usize,
pub two_cofactor: usize,
pub primes: [u64; COUNT],
}

pub struct Csidh<Fp: FqTrait, const COUNT: usize> {
max_exponent: usize,
two_cofactor: usize,
base: Fp,
primes: [u64; COUNT],
}

pub struct CsidhPrivateKey<const COUNT: usize> {
e: [u64; COUNT], // secret degree
d: [bool; COUNT], // secret direction
}

#[derive(Clone, Copy, Debug)]
pub struct CsidhPublicKey<Fp: FqTrait> {
pub A: Fp,
}

impl<Fp: FqTrait> PartialEq for CsidhPublicKey<Fp> {
fn eq(&self, other: &Self) -> bool {
self.A.equals(&other.A) == u32::MAX
}
}

impl<Fp: FqTrait, const COUNT: usize> Csidh<Fp, COUNT> {

pub const fn new(params: &CsidhParameters<COUNT>) -> Self {
Self {
max_exponent: params.max_exponent,
two_cofactor: params.two_cofactor,
base: Fp::ZERO,
primes: params.primes,
}
}

// Returns the starting curve
pub const fn starting_curve(&self) -> CsidhPublicKey<Fp> {
CsidhPublicKey { A: self.base }
}

/// get a random point that either on the curve or on its twist
fn rand_point<R: CryptoRng + RngCore>(
&self,
A24: &Fp,
C24: &Fp,
rng: &mut R,
) -> (PointX<Fp>, bool) {
let curve = Curve::<Fp>::curve_from_A24_proj(&A24, &C24);
let P = PointX::rand_point(rng);

// to check if the point is on the curve,
// or on the twist
let (_, twist) = curve.lift_pointx(&P);

(P, twist == 0)
}

fn sample_secret_key<R: CryptoRng + RngCore>(&self, rng: &mut R) -> CsidhPrivateKey<COUNT> {
let mut e = [0u64; COUNT];
let mut d = [false; COUNT];

for i in 0..COUNT {
// sample exponent between 0 and max_exponent
e[i] = rng.next_u64();
e[i] >>= 59; // shift to increase probabily to hit the range
while e[i] > self.max_exponent as u64 {
e[i] = rng.next_u64();
e[i] >>= 59;
}

// sample direction
d[i] = (rng.next_u32() % 2) == 0;
}

CsidhPrivateKey { e, d }
}

fn action<R: CryptoRng + RngCore>(
&self,
public_key: &CsidhPublicKey<Fp>,
private_key: &CsidhPrivateKey<COUNT>,
rng: &mut R,
) -> CsidhPublicKey<Fp> {
let mut A24 = public_key.A + Fp::TWO;
let mut C24 = Fp::FOUR;

let mut sk_e = private_key.e;
let sk_d = private_key.d;

let mut done: u64 = sk_e.iter().sum();
while done != 0 {
let (mut P, direction) = self.rand_point(&A24, &C24, rng);

// clear 2^e cofactor
Curve::<Fp>::xdbl_proj_iter(&A24, &C24, &mut P, self.two_cofactor);

for i in (0..COUNT).rev() {
let secret_e = sk_e[i];
let secret_d = sk_d[i];
let degree = self.primes[i];

// check if the current degree is part of the secrete key
if secret_e == 0 {
continue;
}

// check if the sampled point is for
// the correct direction
if secret_d != direction {
continue;
}

// get kernel from point
// we do this by removing every ell, but the current degree
// (this can be optimized way more)
let mut K = P;
for ell in self.primes.iter() {
if *ell == degree {
continue;
}
K = Curve::<Fp>::xmul_proj_u64_vartime(&A24, &C24, &K, *ell);
}

// check if the kernel is of the correct degree
// if not we skip it and try again on an new round
if K.is_zero() == u32::MAX {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this continue should somehow be linked in to cofactor clearing, otherwise you might have P = 0 after an evaluation and yet you spend all that time cofactor clearing only to exit afterwards.

continue;
}

// Finally compute the isogeny and push P
let mut image_points = [P];
Curve::<Fp>::velu_odd_isogeny_proj(
&mut A24,
&mut C24,
&K,
degree as usize,
&mut image_points,
);
P = image_points[0];

// mark step as done
sk_e[i] -= 1;

// did we "exhaust" the point?
if P.is_zero() == u32::MAX {
break;
}
}

done = sk_e.iter().sum();
}

CsidhPublicKey {
A: Curve::<Fp>::curve_from_A24_proj(&A24, &C24).A,
}
}

pub fn keygen<R: CryptoRng + RngCore>(
self,
rng: &mut R,
) -> (CsidhPrivateKey<COUNT>, CsidhPublicKey<Fp>) {
let sk = self.sample_secret_key(rng);

let pk = self.action(&self.starting_curve(), &sk, rng);

(sk, pk)
}

pub fn derive_shared_key<R: CryptoRng + RngCore>(
self,
public_key: &CsidhPublicKey<Fp>,
private_key: &CsidhPrivateKey<COUNT>,
rng: &mut R,
) -> CsidhPublicKey<Fp> {
// todo: verify
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we ust multiply by (p+1) a bunch of times to check supersingularity?


self.action(public_key, private_key, rng)
}
}
25 changes: 25 additions & 0 deletions src/protocols/csidh_parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use super::csidh::Csidh;
use crate::fields::csidh::Csidh512;

mod csidh_512 {
use crate::protocols::csidh::CsidhParameters;

pub const NUM_PRIMES: usize = 74;
const MAX_EXPONENT: usize = 5;
const COFACTOR: usize = 2;
const PRIMES: [u64; NUM_PRIMES] = [
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 587,
];

pub const CSIDH_PARAMS: CsidhParameters<NUM_PRIMES> = CsidhParameters {
max_exponent: MAX_EXPONENT,
two_cofactor: COFACTOR,
primes: PRIMES,
};
}

pub const CSIDH_512: Csidh<Csidh512, { csidh_512::NUM_PRIMES }> =
Csidh::new(&csidh_512::CSIDH_PARAMS);
2 changes: 2 additions & 0 deletions src/protocols/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod csidh;
pub mod csidh_parameters;
pub mod sidh;
pub mod sidh_parameters;
pub mod sqisign;
Expand Down
20 changes: 20 additions & 0 deletions tests/test_csidh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![allow(non_snake_case)]

#[cfg(test)]
mod test_csidh {
use isogeny::protocols::csidh_parameters::CSIDH_512;
use isogeny::utilities::test_utils::drng::DRNG;

#[test]
fn test_csidh_key_exchange() {
let mut rng = DRNG::from_seed("csidh_key_exchange".as_bytes());

let (alice_sk, alice_pk) = CSIDH_512.keygen(&mut rng);
let (bob_sk, bob_pk) = CSIDH_512.keygen(&mut rng);

let alice_shared = CSIDH_512.derive_shared_key(&bob_pk, &alice_sk, &mut rng);
let bob_shared = CSIDH_512.derive_shared_key(&alice_pk, &bob_sk, &mut rng);

assert_eq!(alice_shared, bob_shared);
}
}