-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Outline of a PRSS framework #40
Changes from all commits
e0e9134
9e0f4f7
f393e86
b846a69
3b1cd82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use std::{ | ||
fmt::Debug, | ||
ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, | ||
}; | ||
|
||
pub trait Field: | ||
Add<Output = Self> | ||
+ AddAssign | ||
+ Neg<Output = Self> | ||
+ Sub<Output = Self> | ||
+ SubAssign | ||
+ Mul<Output = Self> | ||
+ MulAssign | ||
+ From<u128> | ||
+ Clone | ||
+ Copy | ||
+ PartialEq | ||
+ Debug | ||
+ Sized | ||
{ | ||
type Integer; | ||
const PRIME: Self::Integer; | ||
} | ||
|
||
// TODO(mt) - this code defining fields can be turned into a macro if we ever | ||
// need lots of fields with different primes. | ||
|
||
#[derive(Clone, Copy, PartialEq)] | ||
pub struct Fp31(<Self as Field>::Integer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow i haven't seen this syntax before, at least not as a field of a struct. im not even sure how this works There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Magic. I was a little surprised that this sort of recursiveness was tolerated by the compiler too. The syntax isn't new though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what this means =). In human language what is going on here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically Honestly, this was even more DRY than I would have thought possible, but given that rust accepted it, I left it there for us all to wonder at. |
||
|
||
impl Field for Fp31 { | ||
type Integer = u8; | ||
const PRIME: Self::Integer = 31; | ||
} | ||
|
||
impl Add for Fp31 { | ||
type Output = Self; | ||
|
||
fn add(self, rhs: Self) -> Self::Output { | ||
// TODO(mt) - constant time? | ||
Self((self.0 + rhs.0) % Self::PRIME) | ||
} | ||
} | ||
|
||
impl AddAssign for Fp31 { | ||
#[allow(clippy::assign_op_pattern)] | ||
fn add_assign(&mut self, rhs: Self) { | ||
*self = *self + rhs; | ||
} | ||
} | ||
Comment on lines
+45
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code seems like it would be the same for all prime fields. Is there some way to put this into "Field" itself, and not Fp31? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is step 1. As you can see, it's likely to be very repetitive for new primes. The next step is to define a macro that includes all these implementations. There are a few choices we need to make (prime, basic type, upconversion type for addition and multiplication), but it should be a fairly easy thing to do. |
||
|
||
impl Neg for Fp31 { | ||
type Output = Self; | ||
|
||
fn neg(self) -> Self::Output { | ||
Self((Self::PRIME - self.0) % Self::PRIME) | ||
} | ||
} | ||
|
||
impl Sub for Fp31 { | ||
type Output = Self; | ||
|
||
fn sub(self, rhs: Self) -> Self::Output { | ||
// TODO(mt) - constant time? | ||
// Note: no upcast needed here because `2*p < u8::MAX`. | ||
Self((Self::PRIME + self.0 - rhs.0) % Self::PRIME) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be more complex for other primes =). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the generic implementation will need to upconvert, which - for types that don't need that upconversion, that is most of them - will need some nice warning suppression annotations. |
||
} | ||
} | ||
|
||
impl SubAssign for Fp31 { | ||
#[allow(clippy::assign_op_pattern)] | ||
fn sub_assign(&mut self, rhs: Self) { | ||
*self = *self - rhs; | ||
} | ||
} | ||
Comment on lines
+70
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as the one on AddAssign. I'd love to only have this defined once - and not repeat it on Fp2147483647 and friends =). |
||
|
||
impl Mul for Fp31 { | ||
type Output = Self; | ||
|
||
fn mul(self, rhs: Self) -> Self::Output { | ||
// TODO(mt) - constant time? | ||
let c = u16::from; | ||
#[allow(clippy::cast_possible_truncation)] | ||
Self(((c(self.0) * c(rhs.0)) % c(Self::PRIME)) as <Self as Field>::Integer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as <Self as Field>::Integer That part kind of blows my mind =). Is there a way to simplify that? Why is the cast necessary? Can we not just do: as Self::Integer I'm sure there's a good reason why not - I'd just love to learn what it is =). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem here is that multiplying two values from [0..31) produces a value that is > 8 bits. So I need to upconvert to I could use |
||
} | ||
} | ||
|
||
impl MulAssign for Fp31 { | ||
#[allow(clippy::assign_op_pattern)] | ||
fn mul_assign(&mut self, rhs: Self) { | ||
*self = *self * rhs; | ||
} | ||
} | ||
|
||
/// An infallible conversion from `u128` to this type. This can be used to draw | ||
/// a random value in the field. This introduces bias into the final value | ||
/// but for our purposes that bias is small provided that `2^128 >> PRIME`, which | ||
/// is true provided that `PRIME` is kept to at most 64 bits in value. | ||
/// | ||
/// This method is simpler than rejection sampling for these small prime fields. | ||
impl<T: Into<u128>> From<T> for Fp31 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also magic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In practice, when you define something as generic, what it really does is create what rust calls "monomorphisms" of the type for each of the concrete types that use the code. So as |
||
fn from(v: T) -> Self { | ||
#[allow(clippy::cast_possible_truncation)] | ||
Self((v.into() % u128::from(Self::PRIME)) as <Self as Field>::Integer) | ||
} | ||
} | ||
|
||
impl Debug for Fp31 { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}_mod{}", self.0, Self::PRIME) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use crate::field::Field; | ||
|
||
use super::Fp31; | ||
|
||
#[test] | ||
fn fp31() { | ||
let x = Fp31(24); | ||
let y = Fp31(23); | ||
|
||
assert_eq!(Fp31(16), x + y); | ||
assert_eq!(Fp31(25), x * y); | ||
assert_eq!(Fp31(1), x - y); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest adding a few more test cases like 0 + 0 and 0 - 0 and 1 + 0, 0 - 1, etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the zero tests are fun. Easy to get that wrong. |
||
|
||
let mut x = Fp31(1); | ||
x += Fp31(2); | ||
assert_eq!(Fp31(3), x); | ||
} | ||
|
||
#[test] | ||
fn zero() { | ||
assert_eq!( | ||
Fp31(0), | ||
Fp31::from(<Fp31 as Field>::PRIME), | ||
"from takes a modulus" | ||
); | ||
assert_eq!(Fp31(0), Fp31(0) + Fp31(0)); | ||
assert_eq!(Fp31(0), Fp31(0) + Fp31(0)); | ||
assert_eq!(Fp31(<Fp31 as Field>::PRIME - 1), Fp31(0) - Fp31(1)); | ||
assert_eq!(Fp31(0), Fp31(0) * Fp31(1)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we use https://crates.io/crates/x25519-dalek/2.0.0-pre.1 that uses 0.6?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be easier than what I did, yeah.