Skip to content

Conversation

AndHell
Copy link

@AndHell AndHell commented Jun 2, 2025

In this PR, we provide a simplified implementation of the CSIDH protocol using the CSIDH-512 parameter set.

It is simplified in the sense that it is not fully optimized, but focuses on the readability of the different steps in the protocol.

Further todos:

  • switch to velusqrt
  • implement verification

Copy link
Owner

@GiacomoPope GiacomoPope left a comment

Choose a reason for hiding this comment

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

Some small comments, but otherwise thank you so much!

let Y = V * XZ;
let ok = Y.is_square();

(P, ok)
Copy link
Owner

Choose a reason for hiding this comment

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

This function should simply be a call to rand_point to get a valid point, then we should use to_pointx. Something like:

pub fn rand_pointx<R: CryptoRng + RngCore>(&self, rng: &mut R) -> PointX {
    let P = Self::rand_point(rng);
    P.to_pointx()
}

Copy link
Owner

Choose a reason for hiding this comment

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

I see that this method uses failure for the direction in CSIDH reading some more, but I think this then really needs to be a method for CSIDH rather than on the Curve type, as wanting a point on the twist instead of the curve seems a little esoteric?

Copy link
Owner

Choose a reason for hiding this comment

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

Maybe we want something like rand_point on the Csidh type and then we return `P, twist) to make it obvious what we're doing for this case?


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

Choose a reason for hiding this comment

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

is num_primes not just the same as COUNT?


}

// // For extra safty, assert the kernel is indeed of the correct degree
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
// // For extra safty, assert the kernel is indeed of the correct degree
// // For extra safety, assert the kernel is indeed of the correct degree


// 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.

];

pub const CSIDH_PARAMS: CsidhParameters<NUM_PRIMES> = CsidhParameters {
num_primes: NUM_PRIMES,
Copy link
Owner

Choose a reason for hiding this comment

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

yeah this can be cleaned up, we dont need num_primesas we can just useNUM_PRIMES`

let (mut P, direction) = curve.rand_pointX(rng);
let direction = direction == 0;

// clear cofactor
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
// clear cofactor
// clear 2^e cofactor

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?

@GiacomoPope
Copy link
Owner

I'm back from leave now, did you have time to work on the comments, or would you like me to merge as is and do edits myself?

}

/// 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?

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?




fp2::define_fp2_from_modulus!(
Copy link
Owner

Choose a reason for hiding this comment

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

We dont need the extension, right? We can use define_fp_core https://docs.rs/fp2/latest/fp2/macro.define_fp_core.html

max_exponent: MAX_EXPONENT,
two_cofactor: COFACTOR,
primes: PRIMES,
four_sqrt_p: FOUR_SQRT_P,
Copy link
Owner

Choose a reason for hiding this comment

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

Cant we have this value as a Fp and then avoid the need for , 5? Similar to how we have constants in the sidh impl

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, this part is quite ugly.

The issue is that we need 4sqrt(p) as a bigint (bn), not as a Fp, in the verification, to check against the order of the random point.
The cleanest way would probably be to have a struct for bn.

I'll try to come up with something.

Copy link
Owner

Choose a reason for hiding this comment

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

We do we need it to be a big number? For scalar multiplication? One option would be to use a Fp element and then encode this to bytes for scalar multiplication? Another, yes, would be to make a BN type

Copy link
Author

Choose a reason for hiding this comment

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

We only use this constant to check if the order of a point is > 4sqrt(p).

Copy link
Owner

Choose a reason for hiding this comment

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

Oh I see, this is alg 2 of (https://eprint.iacr.org/2022/880.pdf)...

Copy link
Owner

Choose a reason for hiding this comment

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

I think the "best" method at the moment is have $4 \sqrt{p}$ as a Fp type in the structure and then in verify we can do something like

    bound = bytes_to_bn_vartime(&self.four_sqrt_p.encode())

where &self.four_sqrt_p.encode() will have type &[u8] and bytes_to_bn_vartime can convert from base 2^8 to base 2^64?

Copy link
Author

Choose a reason for hiding this comment

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

I ended up adding a small Bn struct as a wrapper.
Let me know what you think about this approach.
If you think it's suitable I can also extend the struct.


// if RHS i a sqrt, it is on the curve
let RHS = (Cxx+four_Ax-two_Cx+(*C24))*x;
let (_, is_sqrt) = RHS.sqrt();
Copy link
Owner

Choose a reason for hiding this comment

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

let four_Ax = tmp+tmp;
// projective curve: x^3C+4Ax^2−2x^2C+XC​
let tmp = *A24*x;
let four_Ax = tmp+tmp+tmp+tmp;
Copy link
Owner

Choose a reason for hiding this comment

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

we have a mul4() method available in fp2_rs

let four_Ax = tmp+tmp+tmp+tmp;
let tmp = *C24*x;
let Cxx = tmp*x;
let two_Cx = tmp+tmp;
Copy link
Owner

Choose a reason for hiding this comment

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

Same here, we have a mul2()

let A24 = public_key.A + Fp::TWO;
let C24 = Fp::FOUR;

let fsqrtp = &self.four_sqrt_p.limbs[..self.four_sqrt_p.len];
Copy link
Owner

Choose a reason for hiding this comment

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

This looks like we might want to make a method out of it? maybe a Bn to_slice() ?

0x856f1399d91d6592,
0x0000000000000002,
];
0x0000000000000000,
Copy link
Owner

Choose a reason for hiding this comment

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

why the extra 0x00?



// This is a small wrapper to use a bn as constant
const BN_MAXLIMBS : usize = 8;
Copy link
Owner

Choose a reason for hiding this comment

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

oooh this is why... Maybe we should use a vector and dynamically allocate? This could be refactored later I suppose though.

Copy link
Author

Choose a reason for hiding this comment

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

Vectors and constants don't like each other much.

What about putting Bn behind a macro, similar to Fp?
It's quite a bit of overhead for a single constant, but it should be the cleanest solution and might be helpful for other protocols.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants