diff --git a/Cargo.toml b/Cargo.toml index 4dbd5c3..36c8e59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ name = "plexcryptool" path = "src/main.rs" [dependencies] +bitvec = "1.0.1" clap = { version = "4.2.7", features = ["derive"]} clap-num = "1.0.2" num = "0.4.0" diff --git a/src/cplex/cli.rs b/src/cplex/cli.rs index 5086474..0a81c9c 100644 --- a/src/cplex/cli.rs +++ b/src/cplex/cli.rs @@ -71,11 +71,12 @@ pub enum MathActions { /// p minus 1 prime test Pm1(PM1Args), /// calculate in a gallois field + /// includes Eliptic curves Gallois(GalloisAction), /// Euklidian Algorithm Gcd(GcdArgs), /// factorize a natural number - Factorize(FactorizeArgs) + Factorize(FactorizeArgs), } #[derive(Args, Clone, Debug, PartialEq, Eq)] @@ -137,7 +138,9 @@ pub enum GalloisActions { /// reduce n to the range of the field Reduce(GalloisReduceArgs), /// calculate the (multiplicative) inverse of n - Invert(GalloisInvertArgs), + Inverse(GalloisInverseArgs), + /// eliptic curves + ECC(ECCAction) } #[derive(Args, Clone, Debug, PartialEq, Eq)] @@ -152,11 +155,62 @@ pub struct GalloisReduceArgs { } #[derive(Args, Clone, Debug, PartialEq, Eq)] -pub struct GalloisInvertArgs { +pub struct GalloisInverseArgs { #[clap(value_parser=maybe_hex::)] pub n: u128, } +#[derive(Args, Clone, Debug, PartialEq, Eq)] +pub struct ECCAction { + #[clap(allow_hyphen_values=true)] // allow negative inputs like -19 + pub a: i128, + #[clap(allow_hyphen_values=true)] // allow negative inputs like -19 + pub b: i128, + #[command(subcommand)] + pub action: ECCActions +} + +#[derive(Subcommand, Clone, Debug, PartialEq, Eq)] +pub enum ECCActions { + /// negate a point + Neg(ECCNegArgs), + /// add a twp poimts + Add(ECCAddArgs), + /// multiply a point with an integer + /// uses double and add + Mul(ECCMulArgs), +} + +#[derive(Args, Clone, Debug, PartialEq, Eq)] +pub struct ECCNegArgs { + #[clap(value_parser=maybe_hex::)] + pub r: u128, + #[clap(value_parser=maybe_hex::)] + pub s: u128, +} + +#[derive(Args, Clone, Debug, PartialEq, Eq)] +pub struct ECCMulArgs { + #[clap(value_parser=maybe_hex::)] + pub r: u128, + #[clap(value_parser=maybe_hex::)] + pub s: u128, + #[clap(value_parser=maybe_hex::)] + pub n: u128, +} + +#[derive(Args, Clone, Debug, PartialEq, Eq)] +pub struct ECCAddArgs { + #[clap(value_parser=maybe_hex::)] + pub r1: u128, + #[clap(value_parser=maybe_hex::)] + pub s1: u128, + #[clap(value_parser=maybe_hex::)] + pub r2: u128, + #[clap(value_parser=maybe_hex::)] + pub s2: u128, +} + #[derive(Subcommand, Clone, Debug, PartialEq, Eq)] pub enum BinaryActions { /// bit rotation/circular shifting (only 32bit) @@ -166,7 +220,6 @@ pub enum BinaryActions { Xor(XorArgs), /// use a pbox Pbox(PboxArgs), - } #[derive(Args, Clone, Debug, PartialEq, Eq)] diff --git a/src/cplex/printing.rs b/src/cplex/printing.rs index 00dbb4a..376201e 100644 --- a/src/cplex/printing.rs +++ b/src/cplex/printing.rs @@ -88,6 +88,18 @@ pub fn proc_num(num: T, args: Cli) } } +pub fn proc_display(item: T, args: Cli) + where + T: Display, +{ + if args.machine { + println!("{}", item); + } + else { + println!("result is {}", item); + } +} + /// process some int tuple pub fn proc_result_tup_num(result: Result<(T, T), K>, args: Cli) where diff --git a/src/main.rs b/src/main.rs index 67c0a18..1ba9085 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,13 +68,62 @@ pub fn main() { cplex::printing::proc_result_tup_num(result, args); } GalloisActions::Reduce(gal_red_args) => { - let result = field.reduce(gal_red_args.n); + let result = field.reduce::<_, u128>(gal_red_args.n); cplex::printing::proc_num(result, args); } - GalloisActions::Invert(gal_inv_args) => { + GalloisActions::Inverse(gal_inv_args) => { let result = field.inverse(gal_inv_args.n); cplex::printing::proc_result_num(result, args); } + GalloisActions::ECC(ecc_args) => { + let ec = math::ecc::ElipticCurve::new(field, ecc_args.a, ecc_args.b, args.verbose).expect("Could not create eliptic curve"); + match ecc_args.action { + ECCActions::Neg(ecc_neg_args) => { + let p = ec.new_point(ecc_neg_args.r, ecc_neg_args.s); + match p { + Ok(p) => { + let item = ec.neg(p); + cplex::printing::proc_display(item, args) + } + Err(e) => { + cplex::printing::proc_err(e, args); + } + } + } + ECCActions::Mul(ecc_mul_args) => { + let p = ec.new_point(ecc_mul_args.r, ecc_mul_args.s); + if p.is_err() { + cplex::printing::proc_err(p, args); + } + else { + let item = ec.mul(p.unwrap(), ecc_mul_args.n); + if item.is_err() { + cplex::printing::proc_err(item.unwrap_err(), args) + } + else { + cplex::printing::proc_display(item.unwrap(), args); + } + } + } + ECCActions::Add(ecc_add_args) => { + let p1 = ec.new_point(ecc_add_args.r1, ecc_add_args.s1); + let p2 = ec.new_point(ecc_add_args.r2, ecc_add_args.s2); + if p1.is_err() || p2.is_err() { + cplex::printing::proc_err(p1, args.clone()); + cplex::printing::proc_err(p2, args); + } + else { + let item = ec.add(p1.unwrap(), p2.unwrap()); + if item.is_err() { + cplex::printing::proc_err(item.unwrap_err(), args) + } + else { + cplex::printing::proc_display(item.unwrap(), args); + } + } + } + } + } } } MathActions::Factorize(fac_args) => { diff --git a/src/math/ecc.rs b/src/math/ecc.rs index d0dda6c..46dcdd9 100644 --- a/src/math/ecc.rs +++ b/src/math/ecc.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use std::{ops::{Mul, Neg}, fmt::Debug, f32::consts::PI}; +use crate::cplex::printing::seperator; /// eliptic curve cryptograp.s /// @@ -12,7 +12,12 @@ use std::{ops::{Mul, Neg}, fmt::Debug, f32::consts::PI}; use super::gallois::GalloisField; -use num::Integer; +use std::{ops::{Mul, Neg}, fmt::{Debug, Display}, f32::consts::PI}; + +use num::{Integer, Unsigned, NumCast}; + +use bitvec::prelude::*; + use pyo3::prelude::*; #[derive(Debug, Clone, Eq, PartialEq)] @@ -31,30 +36,43 @@ pub struct ElipticCurve { } impl ElipticCurve { - pub fn new(field: GalloisField, a: i128, b: i128, verbose: bool) -> Result { + pub fn new(field: GalloisField, a: T, b: T, verbose: bool) -> Result + where + T: Integer, + T: Debug, + T: num::cast::AsPrimitive, + { + // convert from generics to i128 + let a: i128 = num::cast::AsPrimitive::as_(a); + let b: i128 = num::cast::AsPrimitive::as_(b); - // convert numbers to u128 in the fields - let a = field.reduce(a); - let b = field.reduce(b); + // reduce a and b if possible + let a = field.reduce::<_, u128>(a); + let b = field.reduce::<_, u128>(b); + + if verbose { + println!("On eliptic curve:\n\ + F(X, Y) = Y² - X³ - {a}X - {b}") + } // check diskriminante let d = 4*a.pow(3) + 27*b.pow(2); - if field.reduce(d) == 0 { + if field.reduce::<_, u128>(d) == 0 { if verbose { println!("4*{a}³ + 27*{b}² = {d} = {} != 0\n\ - Check for Diskriminante not passed", field.reduce(d)); + Check for Diskriminante not passed", field.reduce::<_, u128>(d)); } return Err(String::from("Diskriminante not 0")); } else if verbose { println!("4*{a}³ + 27*{b}² = {d} = {} != 0\n\ - Check for Diskriminante passed", field.reduce(d)); + Check for Diskriminante passed", field.reduce::<_, u128>(d)); } let mut infty = ElipticCurvePoint::new(0, 0, field, false); infty.is_infinity_point = true; let infty = infty; - let mut e = ElipticCurve { + let e = ElipticCurve { field, a, b, @@ -65,6 +83,22 @@ impl ElipticCurve { return Ok(e); } + /// build a new point in the EC + pub fn new_point(&self, r: u128, s: u128) -> Result { + let p = ElipticCurvePoint::new(r, s, self.field, self.verbose); + if self.verbose { + println!("{p}") + } + match self.check_point(p, self.verbose) { + true => { + return Ok(p); + } + false => { + return Err(String::from("the point you want to create is not on the EC")); + } + } + } + /// calculate a value for coordinates pub fn poly(&self, r: T, s: T) -> i128 where @@ -79,7 +113,7 @@ impl ElipticCurve { let r: u128 = num::cast::AsPrimitive::as_(r); let s: u128 = num::cast::AsPrimitive::as_(s); let res = (s.pow(2) as u128) - (r.pow(3) as u128) - (self.a * r) - self.b; - let res1 = self.field.reduce(res); + let res1 = self.field.reduce::<_, u128>(res); if self.verbose { println!("F({}, {}) = {}² - {}³ - {} * {} - {} = {res} = {res1}", r, s, s, r, self.a, r, self.b @@ -88,13 +122,13 @@ impl ElipticCurve { return res1 as i128; } - pub fn check_point(self, p: ElipticCurvePoint) -> bool { + pub fn check_point(&self, p: ElipticCurvePoint, verbose: bool) -> bool { let mut valid = true; // insert into poly - let left = self.field.reduce(p.s.pow(2)); - let right = self.field.reduce((p.r.pow(3) as u128) + self.a*p.r + self.b); - if self.verbose { + let left = self.field.reduce::<_, u128>(p.s.pow(2)); + let right = self.field.reduce::<_, u128>((p.r.pow(3) as u128) + self.a*p.r + self.b); + if self.verbose && verbose { let unred_left = p.s.pow(2); let unred_right = p.r.pow(3) + self.a*p.r + self.b; println!("All Points need to fullfill this equation:\n\ @@ -117,68 +151,148 @@ impl ElipticCurve { /// add two points - pub fn add(&self, p1: ElipticCurvePoint, p2: ElipticCurvePoint) -> Result { + pub fn add(&self, p1: ElipticCurvePoint, p2: ElipticCurvePoint) -> + Result { + if self.verbose { + seperator(); + println!("adding {p1} + {p2}"); + seperator(); + } if p1.field != p2.field { return Err(String::from("Points are not on the same field")); } + if !self.check_point(p1, self.verbose) { + return Err(String::from("{p1} is not a valid point")); + } + if !self.check_point(p2, self.verbose) { + return Err(String::from("{p2} is not a valid point")); + } if p1.field.prime_base { + // verbisity stuff + if self.verbose { + println!("{} = {}; {} = -{} = {} <=> {}", + p1.r, p2.r, p1.s, p2.s, self.neg(p2).s, + p1.r == p2.r && p1.s == self.neg(p2).s, + ); + } // case 1: both infty if p1.is_infinity_point && p2.is_infinity_point { + if self.verbose { + println!("case 1"); + } return Ok(self.INFINITY_POINT); } // case 2: one is infty - else if p1.is_infinity_point && !p2.is_infinity_point { + else if p1.is_infinity_point && !p2.is_infinity_point || + !p1.is_infinity_point && p2.is_infinity_point + { + if self.verbose { + println!("case 2"); + } return Ok(self.INFINITY_POINT); } - else if !p1.is_infinity_point && p2.is_infinity_point { - return Ok(p1); + // case 4: r_1 = r_2 && s_1 = -s_2 + else if p1.r == p2.r && p1.s == self.neg(p2).s { + if self.verbose { + println!("case 4"); + } + return Ok(self.INFINITY_POINT); } // case 3: r_1 != r_2 else if p1.r != p2.r { + if self.verbose { + println!("case 3"); + } if self.field.prime_base { - let m = self.field.reduce(p2.s - p1.s) * + let m: u128 = self.field.reduce::(p2.s as i128 - p1.s as i128) * self.field.inverse( - self.field.reduce(p2.r - p1.r) + self.field.reduce::(p2.r as i128 - p1.r as i128) ).expect("could not find inverse"); - if self.verbose || p2.verbose { - println!("m = [s_2 - s_1]/[r_2 - r_1] = [{} - {}]/[{} - {}] = {} = {}", - p2.s, p1.s, p2.r, p1.r, m, p1.field.reduce(m)) - } - let m = self.field.reduce(m); - - let r3 = self.field.reduce(m.pow(3)) - p1.r - p2.r; + let m: i128 = m as i128; if self.verbose { - println!("r_3 = m³ - r_1 - r_2 = {} - {} - {} = {} = {}", - m.pow(3), p1.r, p2.r, r3, p1.field.reduce(r3)); + println!("m = [s_2 - s_1]/[r_2 - r_1] = [{} - {}]/[{} - {}] = {} = {}", + p2.s, p1.s, p2.r, p1.r, m, p1.field.reduce::<_, u128>(m)) } - let r3 = self.field.reduce(r3); + let m: i128 = self.field.reduce(m); - let s3 = m.pow(3) - 2*m*p1.r - m*p2.r + p1.s; - if self.verbose || p2.verbose { - println!("s_3 = m³ − 2*m*r_1 − m*r_2 + s1 = {} - 2*{m}*{} - {m}*{} + {} = {} = {}", - m.pow(3), p1.r, p2.r, p1.s, s3, self.field.reduce(s3)); + let r3 = m.pow(2) - p1.r as i128 - p2.r as i128; + if self.verbose { + println!("r_3 = m² - r_1 - r_2 = {} - {} - {} = {} = {}", + m.pow(2), p1.r, p2.r, r3, p1.field.reduce::<_, u128>(r3)); } - let s3 = self.field.reduce(s3) as i128; - let p = ElipticCurvePoint::new(r3, self.field.reduce(-s3), self.field, self.verbose); + let r3 = self.field.reduce::<_, u128>(r3); - panic!("TODO"); + let s3 = m.pow(3) - 2*m*p1.r as i128 - m*p2.r as i128 + p1.s as i128; + if self.verbose { + println!("s_3 = m³ − 2*m*r_1 − m*r_2 + s1 =\ + {} - 2*{m}*{} - {m}*{} + {} = {} = {}", + m.pow(3), p1.r, p2.r, p1.s, s3, + self.field.reduce::<_, u128>(s3)); + } + let s3 = self.field.reduce::<_, u128>(s3) as i128; + if self.verbose { + println!("-s_3 = - {s3} = {}", self.field.reduce::<_, u128>(-s3)); + } + let p3 = ElipticCurvePoint::new(r3, self.field.reduce::<_, u128>(-s3), + self.field, self.verbose); + + if self.verbose { + seperator(); + println!("result: ({}, {})", p3.r, p3.s); + seperator(); + } + return Ok(p3); } else { panic!("TODO"); } } - // case 4: r_1 = r_2 && s_1 = -s_2 - else if p1.r == p2.r && p1.s == self.neg(p2).s { - return Ok(self.INFINITY_POINT); - } // case 5: P + P where P = (r, 0) else if p1 == p2 && p1.s == 0 { + if self.verbose { + println!("case 5"); + } return Ok(self.INFINITY_POINT); } // case 6: P + P where s != 0 else if p1 == p2 && p1.s != 0 { + if self.verbose { + println!("case 6"); + } if self.field.prime_base { - panic!("TODO"); + let m: i128 = (self.field.reduce::<_, u128>(3 * p1.r.pow(2) + self.a) * + self.field.inverse( + self.field.reduce::(2 * p1.r) + ).expect("could not find inverse")) as i128; + if self.verbose { + println!("m = [3*r²]/[2s] = [3*{}²]/[2*{}] = {} = {}", + p1.r, p1.s, m, self.field.reduce::<_, u128>(m)); + } + let m: i128 = self.field.reduce(m); + + let r3: i128 = self.field.reduce::<_, i128>(m.pow(2)) - p1.r as i128 - p2.r as i128; + if self.verbose { + println!("r_3 = m² - r_1 - r_2 = {} - {} - {} = {} = {}", + m.pow(2), p1.r, p2.r, r3, p1.field.reduce::<_, u128>(r3)); + } + let r3: i128 = self.field.reduce(r3); + + let s3: i128 = m.pow(3) - 2*m*p1.r as i128 - m*p2.r as i128 + p1.s as i128; + if self.verbose || p2.verbose { + println!("s_3 = m³ − 2*m*r_1 − m*r_2 + s1 = {} - 2*{m}*{} - {m}*{} + {} = \ + {} = {}", + m.pow(3), p1.r, p2.r, p1.s, s3, self.field.reduce::<_, u128>(s3)); + } + let s3: i128 = self.field.reduce(s3); + let p3 = ElipticCurvePoint::new(r3 as u128, self.field.reduce::<_, u128>(-s3), + self.field, self.verbose); + + if self.verbose { + seperator(); + println!("result: ({}, {})", p3.r, p3.s); + seperator(); + } + return Ok(p3); } else { panic!("TODO"); @@ -188,7 +302,7 @@ impl ElipticCurve { // how do we get here? // this should never occur else { - panic!("No rules for adding these two points, should not be possible mathmatically.") + panic!("No rules for adding these two points, mathmatically impossible.") } } else { @@ -198,18 +312,70 @@ impl ElipticCurve { /// get negative of a point pub fn neg(&self, p: ElipticCurvePoint) -> ElipticCurvePoint { - return ElipticCurvePoint::new( - p.r, - p.field.reduce(-(p.s as i128)), - p.field, - p.verbose - ); + self.new_point(p.r, self.field.reduce::<_, u128>(-(p.s as i128))).expect("negation of \ + point is not on field, math error") } /// multip.s a point by an integer - pub fn mul(self, p: ElipticCurvePoint, n: u128) -> ElipticCurvePoint { - // TODO - panic!("TODO"); + pub fn mul(&self, g: ElipticCurvePoint, t: T) -> Result + where + T: Integer, + T: NumCast, + T: Debug, + T: Unsigned, + { + if !self.check_point(g, self.verbose) { + return Err(String::from("invalid point")); + } + let t: usize = num::cast(t).unwrap(); + if t < 1 { + return Err(String::from("point multiplication works only if t > 0")); + } + if self.verbose { + println!("h = t * g = {t} * {g}\n\ + t = [{:b}]2", t) + } + let mut t_bits = BitVec::<_, Msb0>::from_element(t); + t_bits.reverse(); + while t_bits[t_bits.len() - 1] == false { + t_bits.pop(); + } + t_bits.reverse(); + let l = t_bits.len() - 1; + let mut lh: ElipticCurvePoint = g; + let mut h: ElipticCurvePoint = g; + let mut index: usize = l; + if l == 0 { + return Ok(h); + } + for bit in t_bits { + if index == l { + if self.verbose { + println!("h_{index} = {h}") + } + index -= 1; + } + h = self.add(lh, lh).expect("error while performing point multiplication"); + if bit == false { + h = self.add(h, g).expect("error while performing point multiplication"); + } + // else h = h + assert!(self.check_point(h, false)); + lh = h; + if self.verbose { + println!("h_{index} = {h}") + } + index -= 1; + } + // now we should have reached h_0 + + return Ok(h); + } +} + +impl std::fmt::Display for ElipticCurve{ + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "F(X, Y) = Y² - X³ -{}X - {}", self.a, self.b) } } @@ -238,6 +404,17 @@ impl ElipticCurvePoint { } } +impl std::fmt::Display for ElipticCurvePoint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if self.is_infinity_point { + write!(f, "(∞ INFINITY)") + } + else { + write!(f, "({}, {})", self.r, self.s) + } + } +} + #[cfg(test)] pub mod test { @@ -278,10 +455,71 @@ pub mod test { ElipticCurvePoint::new(1, 4, f, false), ]; for i in p { - assert!(ec.clone().check_point(i)); + assert!(ec.clone().check_point(i, true)); } for i in np { - assert!(!ec.clone().check_point(i)); + assert!(!ec.clone().check_point(i, true)); } } + + #[test] + fn test_add_points() { + let f = GalloisField::new(11, true, None); + let ec = ElipticCurve::new(f, 1, 1, true).expect("ec cant be created"); + let p1 = ec.new_point(3, 3).expect("point is on ec but an error occurs"); + let p2 = ec.new_point(6, 5).expect("point is on ec but an error occurs"); + let p3 = ec.new_point(0, 10).expect("point is on ec but an error occurs"); + assert_eq!(ec.add(p1, p2).expect("error for possible addition"), p3); + + let f = GalloisField::new(13, true, None); + let ec = ElipticCurve::new(f, -3, 3, true).expect("ec cant be created"); + let p1 = ec.new_point(1, 1).expect("point is on ec but an error occurs"); + let p2 = ec.new_point(5, 3).expect("point is on ec but an error occurs"); + let p3 = ec.new_point(4, 4).expect("point is on ec but an error occurs"); + let p4 = ec.new_point(8, 6).expect("point is on ec but an error occurs"); + let p5 = ec.new_point(11, 12).expect("point is on ec but an error occurs"); + assert_eq!(ec.add(p1, p2).expect("error for possible addition"), p3); + assert_eq!(ec.add(p2, p4).expect("error for possible addition"), p1); + assert_eq!(ec.add(p1, p1).expect("error for possible addition"), p5); + + let f = GalloisField::new(19, true, None); + let ec = ElipticCurve::new(f, 7, 13, true).expect("ec cant be created"); + let p1 = ec.new_point(2, 15).expect("point is on ec but an error occurs"); + let p2 = ec.new_point(6, 10).expect("point is on ec but an error occurs"); + let p3 = ec.new_point(9, 8).expect("point is on ec but an error occurs"); + assert_eq!(ec.add(p1, p2).expect("error for possible addition"), p3); + + let f = GalloisField::new(13, true, None); + let ec = ElipticCurve::new(f, 7, 11, true).expect("ec cant be created"); + let p1 = ec.new_point(4, 5).expect("point is on ec but an error occurs"); + let p2 = ec.new_point(6, 10).expect("point is on ec but an error occurs"); + assert_eq!(ec.add(p1, p1).expect("error for possible addition"), p2); + } + + #[test] + fn test_mul_points() { + // from ecc lectures + let f = GalloisField::new(13, true, None); + let ec = ElipticCurve::new(f, 7, 11, true).expect("ec cant be created"); + let p1 = ec.new_point(4, 5).expect("point is on ec but an error occurs"); + let p2 = ec.new_point(6, 10).expect("point is on ec but an error occurs"); + let p3 = ec.new_point(4, 8).expect("point is on ec but an error occurs"); + let p4 = ec.new_point(6, 3).expect("point is on ec but an error occurs"); + assert_eq!(ec.mul(p1, 2u32).expect("error for possible addition"), p2); + assert_eq!(ec.mul(p1, 4u32).expect("error for possible addition"), p3); + assert_eq!(ec.mul(p3, 2u32).expect("error for possible addition"), p4); + assert_eq!(ec.mul(p2, 4u32).expect("error for possible addition"), p4); + + //let f = GalloisField::new(13, true, None); + //let ec = ElipticCurve::new(f, -3, 3, true).expect("ec cant be created"); + //let p1 = ec.new_point(1, 1).expect("point is on ec but an error occurs"); + //let p2 = ec.new_point(11, 12).expect("point is on ec but an error occurs"); + //assert_eq!(ec.mul(p1, 2u64).expect("error for possible addition"), p2); + + //let f = GalloisField::new(17, true, None); + //let ec = ElipticCurve::new(f, 11, 3, true).expect("ec cant be created"); + //let p1 = ec.new_point(5, 8).expect("point is on ec but an error occurs"); + //let p2 = ec.new_point(6, 8).expect("point is on ec but an error occurs"); + //assert_eq!(ec.mul(p1, 10u128).expect("error for possible addition"), p2); + } } diff --git a/src/math/gallois.rs b/src/math/gallois.rs index 2542f27..f71600d 100644 --- a/src/math/gallois.rs +++ b/src/math/gallois.rs @@ -35,11 +35,13 @@ pub const F_256_DEFAULT_RELATION: u128 = 0x11b; /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] /// used when trying to find a root for a number which does not have a root. -pub struct NoInverseError; +pub struct NoInverseError { + pub n: u128 +} impl fmt::Display for NoInverseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "inverse for 0 does not exist") + write!(f, "inverse for {} does not exist", self.n) } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -136,32 +138,30 @@ impl GalloisField { T: Integer, T: NumCast, T: Debug, - K: Unsigned, K: Integer, K: NumCast, K: Debug, - { - dbg!(&n); - let mut n: i128 = num::cast(n).unwrap(); - if self.prime_base { - if n < 0 { - while n < 0 { - n += self.base as i128; + { + let mut n: i128 = num::cast(n).unwrap(); + if self.prime_base { + if n < 0 { + while n < 0 { + n += self.base as i128; + } } + n %= self.base as i128; + let n: K = num::cast(n).unwrap(); + return n; } - n %= self.base as i128; - let n: K = num::cast(n).unwrap(); - return n; - } - else { - if n < 0 { - panic!("reduction for negative numbers not implemented."); + else { + if n < 0 { + panic!("reduction for negative numbers not implemented."); + } + let n = modred(n as u128, self.relation.unwrap(), false).expect("modular reduction didn't work"); + let n: K = num::cast(n).unwrap(); + return n; } - let n = modred(n as u128, self.relation.unwrap(), false).expect("modular reduction didn't work"); - let n: K = num::cast(n).unwrap(); - return n; } - } /// calculate the exponent of a base in the field pub fn pow(self, base: u128, exp: u128) -> u128 { @@ -175,9 +175,8 @@ impl GalloisField { /// find the multiplicative inverse of a number pub fn inverse(self, n: u128) -> Result { - dbg!(&n); if n == 0 { - return Err(NoInverseError); + return Err(NoInverseError{n}); } let egcd = (n as i128).extended_gcd(&(self.base as i128)); let egcd = self.reduce(egcd.x); @@ -307,7 +306,7 @@ impl GalloisField { println!("{index}.\ta^(2^[l-(i+1)]*t) * b^(n_{index}) = {a}^(2^[{l}-({index}+1)]*{t}) * {b}^({}) = {tmp} (mod {})", n[index as usize], self.base - ); + ); } c.push(tmp); if self.verbose { @@ -324,7 +323,7 @@ impl GalloisField { index + 1, n[index as usize], n[index as usize] - ); + ); } } else { @@ -340,7 +339,7 @@ impl GalloisField { index + 1, n[index as usize], n.last().unwrap() - ); + ); } } } @@ -357,10 +356,10 @@ impl GalloisField { w1 = self.reduce(w1); if self.verbose { println!("w_1 = [a^(t+1)]/[2] * b^(n_l) = [{a}^([{t}+1])]/[2] * {b}^{} = {} (mod {})", - n[l as usize], - w1, - self.base - ); + n[l as usize], + w1, + self.base + ); } let w2 = self.a_inverse(w1); if self.verbose { @@ -388,7 +387,7 @@ impl GalloisField { println!("Therefore, char(F_{}) = {i}", self.base); seperator(); } - + self.cha = i; return i; } @@ -407,7 +406,7 @@ impl GalloisField { pub fn py_pow(&self, base: u128, exp: u128) -> u128 { return self.pow(base, exp); } - + #[pyo3(name="reduce")] /// reduce any int pub fn py_reduce(&self, n: i128) -> u128 { @@ -462,55 +461,62 @@ impl GalloisField { } /////////////////////////////////////////////////////////////////////////////////////////////////// -#[test] -fn test_gallois_sqrt() { - let field = GalloisField::new(977, true, None); - assert_eq!(field.sqrt(269).expect("function says there is no root but there is"), (313, 664)); - assert_eq!(field.sqrt(524).expect("function says there is no root but there is"), (115, 862)); - assert_eq!(field.sqrt(275).expect("function says there is no root but there is"), (585, 392)); -} -#[test] -fn test_gallois_reduce() { - let field = GalloisField::new(977, true, None); - for i in 0..976u128 { - assert_eq!(field.reduce::<_, u128>(i as u128), i); +#[cfg(test)] +pub mod test { + + use super::*; + + #[test] + fn test_gallois_sqrt() { + let field = GalloisField::new(977, true, None); + assert_eq!(field.sqrt(269).expect("function says there is no root but there is"), (313, 664)); + assert_eq!(field.sqrt(524).expect("function says there is no root but there is"), (115, 862)); + assert_eq!(field.sqrt(275).expect("function says there is no root but there is"), (585, 392)); } - let field = GalloisField::new(16, true, None); -} - -#[test] -fn test_gallois_inverse() { - let field = GalloisField::new(31, true, None); - assert_eq!(field.inverse(12).unwrap(), 13); - assert_eq!(field.inverse(28).unwrap(), 10); - assert!(field.inverse(0).is_err()); - - let field = GalloisField::new(83, true, None); - assert_eq!(field.inverse(6).unwrap(), 14); - assert_eq!(field.inverse(54).unwrap(), 20); - assert!(field.inverse(0).is_err()); - - let field = GalloisField::new(23, true, None); - assert_eq!(field.inverse(17).unwrap(), 19); - assert_eq!(field.inverse(7).unwrap(), 10); - assert!(field.inverse(0).is_err()); - - // TODO add a test for a field that has a non prime base - let field = GalloisField::new(16, true, None); - assert_eq!(field.inverse(0x130).unwrap(), 0); - assert!(field.inverse(0).is_err()); -} - -#[test] -fn test_calc_char() { - assert_eq!(GalloisField::new(83, true, None).calc_char(), 83); - assert_eq!(GalloisField::new(1151, true, None).calc_char(), 1151); - assert_eq!(GalloisField::new(2, true, None).calc_char(), 2); - - //// experimental - //assert_eq!(GalloisField::new(8, true, None).calc_char(), 2); - //assert_eq!(GalloisField::new(64, true, None).calc_char(), 2); - ////assert_eq!(GalloisField::new(2u128.pow(64u32), true, None).calc_char(), 2); + #[test] + fn test_gallois_reduce() { + let field = GalloisField::new(977, true, None); + for i in 0..976u128 { + assert_eq!(field.reduce::<_, u128>(i as u128), i); + } + + let field = GalloisField::new(16, true, None); + } + + #[test] + fn test_gallois_inverse() { + let field = GalloisField::new(31, true, None); + assert_eq!(field.inverse(12).unwrap(), 13); + assert_eq!(field.inverse(28).unwrap(), 10); + assert!(field.inverse(0).is_err()); + + let field = GalloisField::new(83, true, None); + assert_eq!(field.inverse(6).unwrap(), 14); + assert_eq!(field.inverse(54).unwrap(), 20); + assert!(field.inverse(0).is_err()); + + let field = GalloisField::new(23, true, None); + assert_eq!(field.inverse(17).unwrap(), 19); + assert_eq!(field.inverse(7).unwrap(), 10); + assert!(field.inverse(0).is_err()); + + // TODO add a test for a field that has a non prime base + let field = GalloisField::new(16, true, None); + assert_eq!(field.inverse(0x130).unwrap(), 0); + assert!(field.inverse(0).is_err()); + } + + #[test] + fn test_calc_char() { + assert_eq!(GalloisField::new(83, true, None).calc_char(), 83); + assert_eq!(GalloisField::new(1151, true, None).calc_char(), 1151); + assert_eq!(GalloisField::new(2, true, None).calc_char(), 2); + + //// experimental + //assert_eq!(GalloisField::new(8, true, None).calc_char(), 2); + //assert_eq!(GalloisField::new(64, true, None).calc_char(), 2); + ////assert_eq!(GalloisField::new(2u128.pow(64u32), true, None).calc_char(), 2); + } }