WIP: Replace OpenSSL with custom implementations

This commit is contained in:
Alex Page 2023-06-28 00:47:48 -04:00
parent 8dc063e4e5
commit c2995d9399
9 changed files with 653 additions and 172 deletions

View file

@ -6,7 +6,13 @@ edition = "2021"
[dependencies]
anyhow = "1.0.71"
bitreader = "0.3.7"
elliptic-curve = "0.13.5"
num-bigint = { version = "0.4.3", features = ["rand"] }
num-integer = "0.1.45"
num-traits = "0.2.15"
openssl = { git = "https://github.com/sfackler/rust-openssl" }
rand = "0.8.5"
sha1 = "0.10.5"
thiserror = "1.0.40"
[dev-dependencies]

View file

@ -1,20 +1,24 @@
use std::fmt::{Display, Formatter};
use std::{
cmp::Ordering,
fmt::{Display, Formatter},
};
use anyhow::{bail, Result};
use bitreader::BitReader;
use openssl::{
bn::{BigNum, BigNumContext, MsbOption},
ec::{EcGroup, EcPoint},
sha::sha1,
};
use num_bigint::{BigInt, BigUint, RandomBits};
use num_integer::Integer;
use num_traits::{FromPrimitive, ToPrimitive};
use rand::Rng;
use sha1::{Digest, Sha1};
use crate::{
crypto::{EllipticCurve, PrivateKey},
key::{base24_decode, base24_encode, strip_key},
math::bitmask,
weierstrass_curve::{Point, WeierstrassCurve},
};
const FIELD_BITS: i32 = 384;
const FIELD_BITS: u64 = 384;
const FIELD_BYTES: usize = 48;
const SHA_MSG_LENGTH: usize = 4 + 2 * FIELD_BYTES;
@ -44,10 +48,10 @@ impl ProductKey {
let sequence = match sequence {
Some(serial) => serial,
None => {
let mut bn_rand = BigNum::new()?;
bn_rand.rand(19, MsbOption::MAYBE_ZERO, false)?;
let o_raw = u32::from_be_bytes(bn_rand.to_vec_padded(4)?.try_into().unwrap());
o_raw % 999999
let mut rng = rand::thread_rng();
let random: BigInt = rng.sample(RandomBits::new(32));
let raw = u32::from_be_bytes(random.to_bytes_be().1[0..4].try_into().unwrap());
raw % 999999
}
};
@ -83,62 +87,75 @@ impl ProductKey {
}
fn generate(
e_curve: &EcGroup,
base_point: &EcPoint,
gen_order: &BigNum,
private_key: &BigNum,
e_curve: &WeierstrassCurve,
base_point: &Point,
gen_order: &BigInt,
private_key: &BigInt,
channel_id: u32,
sequence: u32,
upgrade: bool,
) -> Result<Self> {
let mut num_context = BigNumContext::new().unwrap();
let mut c = BigNum::new()?;
let mut s = BigNum::new()?;
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let mut ek: BigNum;
let serial = channel_id * 1_000_000 + sequence;
let data = serial << 1 | upgrade as u32;
let product_key = loop {
let mut r = EcPoint::new(e_curve)?;
let mut rng = rand::thread_rng();
let product_key = loop {
// Generate a random number c consisting of 384 bits without any constraints.
c.rand(FIELD_BITS, MsbOption::MAYBE_ZERO, false)?;
let c: BigUint = rng.sample(RandomBits::new(FIELD_BITS));
let c: BigInt = c.into();
// Pick a random derivative of the base point on the elliptic curve.
// R = cG;
r.mul(e_curve, base_point, &c, &num_context)?;
let r = e_curve.multiply_point(&c, base_point);
// Acquire its coordinates.
// x = R.x; y = R.y;
r.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?;
let (x, y) = match r {
Point::Point { x, y } => (x, y),
Point::Infinity => bail!("Point at infinity!"),
};
let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH];
let mut x_bin = x.to_vec_padded(FIELD_BYTES as i32)?;
x_bin.reverse();
let mut y_bin = y.to_vec_padded(FIELD_BYTES as i32)?;
y_bin.reverse();
let x_bin = x.to_signed_bytes_le();
let x_bin = match x_bin.len().cmp(&FIELD_BYTES) {
Ordering::Less => (0..FIELD_BYTES - x_bin.len() - 1)
.map(|_| 0)
.chain(x_bin.into_iter())
.collect(),
Ordering::Greater => continue,
Ordering::Equal => x_bin,
};
let y_bin = y.to_signed_bytes_le();
let y_bin = match y_bin.len().cmp(&FIELD_BYTES) {
Ordering::Less => (0..FIELD_BYTES - y_bin.len() - 1)
.map(|_| 0)
.chain(y_bin.into_iter())
.collect(),
Ordering::Greater => continue,
Ordering::Equal => y_bin,
};
msg_buffer[0..4].copy_from_slice(&data.to_le_bytes());
msg_buffer[4..4 + FIELD_BYTES].copy_from_slice(&x_bin);
msg_buffer[4 + FIELD_BYTES..4 + FIELD_BYTES * 2].copy_from_slice(&y_bin);
let msg_digest = sha1(&msg_buffer);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(msg_buffer);
hasher.finalize()
};
let hash: u32 =
u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32;
ek = (*private_key).to_owned()?;
ek.mul_word(hash)?;
let mut ek = private_key.clone();
ek *= hash;
s.mod_add(&ek, &c, gen_order, &mut num_context)?;
let s = (ek + c).mod_floor(gen_order);
let signature = u64::from_be_bytes(s.to_vec_padded(8)?.try_into().unwrap());
let signature = s.to_u64().unwrap_or(0);
if signature <= bitmask(55) {
break Self {
@ -156,36 +173,43 @@ impl ProductKey {
fn verify(
&self,
e_curve: &EcGroup,
base_point: &EcPoint,
public_key: &EcPoint,
e_curve: &WeierstrassCurve,
base_point: &Point,
public_key: &Point,
) -> Result<bool> {
let mut ctx = BigNumContext::new()?;
let e = BigInt::from_u32(self.hash).unwrap();
let s = BigInt::from_u64(self.signature).unwrap();
let e = BigNum::from_u32(self.hash)?;
let s = BigNum::from_slice(&self.signature.to_be_bytes())?;
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let t = e_curve.multiply_point(&s, base_point);
let mut p = e_curve.multiply_point(&e, public_key);
let mut t = EcPoint::new(e_curve)?;
let mut p = EcPoint::new(e_curve)?;
p = e_curve.add_points(&p, &t);
t.mul(e_curve, base_point, &s, &ctx)?;
p.mul(e_curve, public_key, &e, &ctx)?;
{
let p_copy = p.to_owned(e_curve)?;
p.add(e_curve, &t, &p_copy, &mut ctx)?;
}
p.affine_coordinates(e_curve, &mut x, &mut y, &mut ctx)?;
let (x, y) = match p {
Point::Point { x, y } => (x, y),
Point::Infinity => bail!("Point at infinity!"),
};
let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH];
let mut x_bin = x.to_vec_padded(FIELD_BYTES as i32)?;
x_bin.reverse();
let mut y_bin = y.to_vec_padded(FIELD_BYTES as i32)?;
y_bin.reverse();
let x_bin = x.to_signed_bytes_le();
let x_bin = if x_bin.len() < FIELD_BYTES {
(0..FIELD_BYTES - x_bin.len() - 1)
.map(|_| 0)
.chain(x_bin.into_iter())
.collect()
} else {
x_bin
};
let y_bin = y.to_signed_bytes_le();
let y_bin = if y_bin.len() < FIELD_BYTES {
(0..FIELD_BYTES - y_bin.len() - 1)
.map(|_| 0)
.chain(y_bin.into_iter())
.collect()
} else {
y_bin
};
let serial = self.channel_id * 1_000_000 + self.sequence;
let data = serial << 1 | self.upgrade as u32;
@ -194,7 +218,11 @@ impl ProductKey {
msg_buffer[4..4 + FIELD_BYTES].copy_from_slice(&x_bin);
msg_buffer[4 + FIELD_BYTES..4 + FIELD_BYTES * 2].copy_from_slice(&y_bin);
let msg_digest = sha1(&msg_buffer);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(msg_buffer);
hasher.finalize()
};
let hash: u32 =
u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32;
@ -265,7 +293,7 @@ mod tests {
use serde_json::from_reader;
use crate::crypto::EllipticCurve;
use crate::{bink1998, crypto::EllipticCurve};
#[test]
fn verify_test() {
@ -291,8 +319,8 @@ mod tests {
let curve = EllipticCurve::new(p, a, b, gx, gy, kx, ky).unwrap();
assert!(super::ProductKey::from_key(&curve, product_key).is_ok());
assert!(super::ProductKey::from_key(&curve, "11111-R6BG2-39J83-RYKHF-W47TT").is_err());
assert!(bink1998::ProductKey::from_key(&curve, product_key).is_ok());
assert!(bink1998::ProductKey::from_key(&curve, "11111-R6BG2-39J83-RYKHF-W47TT").is_err());
}
#[test]

View file

@ -1,21 +1,25 @@
use std::fmt::{Display, Formatter};
use std::{
cmp::Ordering,
fmt::{Display, Formatter},
};
use anyhow::{bail, Result};
use bitreader::BitReader;
use openssl::{
bn::{BigNum, BigNumContext, MsbOption},
ec::{EcGroup, EcPoint},
rand::rand_bytes,
sha::sha1,
};
use num_bigint::{BigInt, BigUint, RandomBits};
use num_integer::Integer;
use num_traits::ToPrimitive;
use rand::Rng;
use sha1::{Digest, Sha1};
use crate::{
crypto::{EllipticCurve, PrivateKey},
key::{base24_decode, base24_encode, strip_key},
math::{bitmask, by_dword, next_sn_bits},
msr::mod_sqrt,
weierstrass_curve::{Point, WeierstrassCurve},
};
const FIELD_BITS: i32 = 512;
const FIELD_BITS: u64 = 512;
const FIELD_BYTES: usize = 64;
const SHA_MSG_LENGTH: usize = 3 + 2 * FIELD_BYTES;
@ -47,9 +51,9 @@ impl ProductKey {
let auth_info = match auth_info {
Some(auth_info) => auth_info,
None => {
let mut auth_info_bytes = [0_u8; 4];
rand_bytes(&mut auth_info_bytes)?;
u32::from_ne_bytes(auth_info_bytes) & ((1 << 10) - 1)
let mut rng = rand::thread_rng();
let random: BigInt = rng.sample(RandomBits::new(32));
u32::from_be_bytes(random.to_bytes_be().1[0..4].try_into().unwrap())
}
};
@ -88,38 +92,50 @@ impl ProductKey {
}
fn generate(
e_curve: &EcGroup,
base_point: &EcPoint,
gen_order: &BigNum,
private_key: &BigNum,
e_curve: &WeierstrassCurve,
base_point: &Point,
gen_order: &BigInt,
private_key: &BigInt,
channel_id: u32,
auth_info: u32,
upgrade: bool,
) -> Result<Self> {
let mut num_context = BigNumContext::new().unwrap();
let mut c = BigNum::new()?;
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let data = channel_id << 1 | upgrade as u32;
let mut rng = rand::thread_rng();
let mut no_square = false;
let key = loop {
let mut r = EcPoint::new(e_curve)?;
let c: BigUint = rng.sample(RandomBits::new(FIELD_BITS));
let mut c: BigInt = c.into();
c.rand(FIELD_BITS, MsbOption::MAYBE_ZERO, false)?;
let r = e_curve.multiply_point(&c, base_point);
r.mul(e_curve, base_point, &c, &num_context)?;
r.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?;
let (x, y) = match r {
Point::Point { x, y } => (x, y),
Point::Infinity => bail!("Point at infinity!"),
};
let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH];
let mut x_bin = x.to_vec_padded(FIELD_BYTES as i32)?;
x_bin.reverse();
let mut y_bin = y.to_vec_padded(FIELD_BYTES as i32)?;
y_bin.reverse();
let x_bin = x.to_signed_bytes_le();
let x_bin = match x_bin.len().cmp(&FIELD_BYTES) {
Ordering::Less => (0..FIELD_BYTES - x_bin.len() - 1)
.map(|_| 0)
.chain(x_bin.into_iter())
.collect(),
Ordering::Greater => continue,
Ordering::Equal => x_bin,
};
let y_bin = y.to_signed_bytes_le();
let y_bin = match y_bin.len().cmp(&FIELD_BYTES) {
Ordering::Less => (0..FIELD_BYTES - y_bin.len() - 1)
.map(|_| 0)
.chain(y_bin.into_iter())
.collect(),
Ordering::Greater => continue,
Ordering::Equal => y_bin,
};
msg_buffer[0x00] = 0x79;
msg_buffer[0x01] = (data & 0x00FF) as u8;
@ -128,7 +144,11 @@ impl ProductKey {
msg_buffer[3..3 + FIELD_BYTES].copy_from_slice(&x_bin);
msg_buffer[3 + FIELD_BYTES..3 + FIELD_BYTES * 2].copy_from_slice(&y_bin);
let msg_digest = sha1(&msg_buffer);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(msg_buffer);
hasher.finalize()
};
let hash: u32 = by_dword(&msg_digest[0..4]) & bitmask(31) as u32;
@ -144,42 +164,40 @@ impl ProductKey {
msg_buffer[0x09] = 0x00;
msg_buffer[0x0A] = 0x00;
let msg_digest = sha1(&msg_buffer[..=0x0A]);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(&msg_buffer[..=0x0A]);
hasher.finalize()
};
let i_signature = next_sn_bits(by_dword(&msg_digest[4..8]) as u64, 30, 2) << 32
| by_dword(&msg_digest[0..4]) as u64;
let mut e = BigNum::from_slice(&i_signature.to_be_bytes())?;
let mut e = BigInt::from(i_signature);
let e_2 = e.to_owned()?;
e.mod_mul(&e_2, private_key, gen_order, &mut num_context)?;
e = (e * private_key).mod_floor(gen_order);
let mut s = e.to_owned()?;
let mut s = e.clone();
let s_2 = s.to_owned()?;
s.mod_sqr(&s_2, gen_order, &mut num_context)?;
s = (&s * &s).mod_floor(gen_order);
let c_2 = c.to_owned()?;
c.lshift(&c_2, 2)?;
c <<= 2;
s = &s + &c;
let s_2 = s.to_owned()?;
if s.mod_sqrt(&s_2, gen_order, &mut num_context).is_err() {
if mod_sqrt(&s, gen_order).is_none() {
no_square = true;
};
let s_2 = s.to_owned()?;
s.mod_sub(&s_2, &e, gen_order, &mut num_context)?;
s = (s - e).mod_floor(gen_order);
if s.is_bit_set(0) {
if s.is_odd() {
s = &s + gen_order;
}
let s_2 = s.to_owned()?;
s.rshift1(&s_2)?;
s >>= 1;
let signature = u64::from_be_bytes(s.to_vec_padded(8)?.try_into().unwrap());
let signature = s.to_u64().unwrap_or(0);
let product_key = Self {
upgrade,
@ -201,12 +219,10 @@ impl ProductKey {
fn verify(
&self,
e_curve: &EcGroup,
base_point: &EcPoint,
public_key: &EcPoint,
e_curve: &WeierstrassCurve,
base_point: &Point,
public_key: &Point,
) -> Result<bool> {
let mut num_context = BigNumContext::new()?;
let data = self.channel_id << 1 | self.upgrade as u32;
let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH];
@ -223,39 +239,47 @@ impl ProductKey {
msg_buffer[0x09] = 0x00;
msg_buffer[0x0A] = 0x00;
let msg_digest = sha1(&msg_buffer[..=0x0A]);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(&msg_buffer[..=0x0A]);
hasher.finalize()
};
let i_signature = next_sn_bits(by_dword(&msg_digest[4..8]) as u64, 30, 2) << 32
| by_dword(&msg_digest[0..4]) as u64;
let e = BigNum::from_slice(&i_signature.to_be_bytes())?;
let s = BigNum::from_slice(&self.signature.to_be_bytes())?;
let e = BigInt::from(i_signature);
let s = BigInt::from(self.signature);
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let t = e_curve.multiply_point(&s, base_point);
let mut p = e_curve.multiply_point(&e, public_key);
let mut p = EcPoint::new(e_curve)?;
let mut t = EcPoint::new(e_curve)?;
p = e_curve.add_points(&t, &p);
p = e_curve.multiply_point(&s, &p);
t.mul(e_curve, base_point, &s, &num_context)?;
p.mul(e_curve, public_key, &e, &num_context)?;
let (x, y) = match p {
Point::Point { x, y } => (x, y),
Point::Infinity => bail!("Point at infinity!"),
};
{
let p_2 = p.to_owned(e_curve)?;
p.add(e_curve, &t, &p_2, &mut num_context)?;
}
{
let p_2 = p.to_owned(e_curve)?;
p.mul(e_curve, &p_2, &s, &num_context)?;
}
p.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?;
let mut x_bin = x.to_vec_padded(FIELD_BYTES as i32)?;
x_bin.reverse();
let mut y_bin = y.to_vec_padded(FIELD_BYTES as i32)?;
y_bin.reverse();
let x_bin = x.to_signed_bytes_le();
let x_bin = if x_bin.len() < FIELD_BYTES {
(0..FIELD_BYTES - x_bin.len() - 1)
.map(|_| 0)
.chain(x_bin.into_iter())
.collect()
} else {
x_bin
};
let y_bin = y.to_signed_bytes_le();
let y_bin = if y_bin.len() < FIELD_BYTES {
(0..FIELD_BYTES - y_bin.len() - 1)
.map(|_| 0)
.chain(y_bin.into_iter())
.collect()
} else {
y_bin
};
msg_buffer[0x00] = 0x79;
msg_buffer[0x01] = (data & 0x00FF) as u8;
@ -264,7 +288,11 @@ impl ProductKey {
msg_buffer[3..3 + FIELD_BYTES].copy_from_slice(&x_bin);
msg_buffer[3 + FIELD_BYTES..3 + FIELD_BYTES * 2].copy_from_slice(&y_bin);
let msg_digest = sha1(&msg_buffer);
let msg_digest = {
let mut hasher = Sha1::new();
hasher.update(msg_buffer);
hasher.finalize()
};
let hash: u32 = by_dword(&msg_digest[0..4]) & bitmask(31) as u32;

View file

@ -1,24 +1,24 @@
use anyhow::Result;
use openssl::{
bn::{BigNum, BigNumContext},
ec::{EcGroup, EcPoint},
};
use num_bigint::BigInt;
use num_traits::Num;
use crate::weierstrass_curve::{Point, WeierstrassCurve};
pub struct EllipticCurve {
pub curve: EcGroup,
pub gen_point: EcPoint,
pub pub_point: EcPoint,
pub curve: WeierstrassCurve,
pub gen_point: Point,
pub pub_point: Point,
}
pub struct PrivateKey {
pub gen_order: BigNum,
pub private_key: BigNum,
pub gen_order: BigInt,
pub private_key: BigInt,
}
impl PrivateKey {
pub fn new(gen_order: &str, private_key: &str) -> Result<Self> {
let gen_order = BigNum::from_dec_str(gen_order)?;
let private_key = &gen_order - &BigNum::from_dec_str(private_key)?;
let gen_order = BigInt::from_str_radix(gen_order, 10)?;
let private_key = &gen_order - &BigInt::from_str_radix(private_key, 10)?;
Ok(Self {
gen_order,
private_key,
@ -36,23 +36,25 @@ impl EllipticCurve {
public_key_x: &str,
public_key_y: &str,
) -> Result<Self> {
let mut context = BigNumContext::new()?;
let p = BigInt::from_str_radix(p, 10)?;
let a = BigInt::from_str_radix(a, 10)?;
let b = BigInt::from_str_radix(b, 10)?;
let generator_x = BigInt::from_str_radix(generator_x, 10)?;
let generator_y = BigInt::from_str_radix(generator_y, 10)?;
let public_key_x = BigInt::from_str_radix(public_key_x, 10)?;
let public_key_y = BigInt::from_str_radix(public_key_y, 10)?;
let p = BigNum::from_dec_str(p)?;
let a = BigNum::from_dec_str(a)?;
let b = BigNum::from_dec_str(b)?;
let generator_x = BigNum::from_dec_str(generator_x)?;
let generator_y = BigNum::from_dec_str(generator_y)?;
let public_key_x = BigNum::from_dec_str(public_key_x)?;
let public_key_y = BigNum::from_dec_str(public_key_y)?;
let curve = WeierstrassCurve::new(a, b, p);
let curve = EcGroup::from_components(p, a, b, &mut context)?;
let gen_point = Point::Point {
x: generator_x,
y: generator_y,
};
let mut gen_point = EcPoint::new(&curve)?;
gen_point.set_affine_coordinates_gfp(&curve, &generator_x, &generator_y, &mut context)?;
let mut pub_point = EcPoint::new(&curve)?;
pub_point.set_affine_coordinates_gfp(&curve, &public_key_x, &public_key_y, &mut context)?;
let pub_point = Point::Point {
x: public_key_x,
y: public_key_y,
};
Ok(Self {
curve,

View file

@ -4,3 +4,5 @@ pub mod confid;
pub mod crypto;
mod key;
mod math;
mod msr;
mod weierstrass_curve;

90
umskt/src/msr.rs Normal file
View file

@ -0,0 +1,90 @@
use num_bigint::BigInt;
use num_traits::{One, Zero};
// Legendre symbol, returns 1, 0, or -1 mod p
fn ls(a: &BigInt, p: &BigInt) -> BigInt {
let exp = (p - BigInt::one()) / BigInt::from(2);
a.modpow(&exp, p)
}
// Tonelli-Shanks algorithm
pub fn mod_sqrt(n: &BigInt, p: &BigInt) -> Option<BigInt> {
if !ls(n, p).is_one() {
return None;
}
let mut q = p - 1;
let mut s = BigInt::zero();
while (&q & &BigInt::one()).is_zero() {
s += 1;
q >>= 1
}
if s.is_one() {
let exp = (p + 1) / 4;
let r1 = n.modpow(&exp, p);
return Some(p - &r1);
}
let mut z = BigInt::from(2);
while ls(&z, p) != p - 1 {
z += 1
}
let mut c = z.modpow(&q, p);
let mut r = n.modpow(&((&q + 1) / 2), p);
let mut t = n.modpow(&q, p);
let mut m = s;
loop {
if t.is_one() {
return Some(p - &r);
}
let mut i = BigInt::zero();
let mut z = t.clone();
let mut b = c.clone();
while !z.is_one() && i < &m - 1 {
z = &z * &z % p;
i += 1;
}
let mut e = &m - &i - 1;
while e > BigInt::zero() {
b = &b * &b % p;
e -= 1;
}
r = &r * &b % p;
c = &b * &b % p;
t = &t * &c % p;
m = i;
}
}
#[cfg(test)]
mod tests {
use openssl::bn::{BigNum, BigNumContext};
#[test]
fn test() {
let n = "1573769205219068003359487454943505465350185201622247106224183162699789934914421854093895861489537435530695722027283028984396996570717144433549421890648379";
let p = "5622613991231344109";
{
let n = num_bigint::BigInt::parse_bytes(n.as_bytes(), 10).unwrap();
let p = num_bigint::BigInt::parse_bytes(p.as_bytes(), 10).unwrap();
let r = super::mod_sqrt(&n, &p).unwrap();
dbg!(r);
}
{
let mut ctx = BigNumContext::new().unwrap();
let n = BigNum::from_dec_str(n).unwrap();
let p = BigNum::from_dec_str(p).unwrap();
let mut r = BigNum::new().unwrap();
r.mod_sqrt(&n, &p, &mut ctx).unwrap();
dbg!(r.to_dec_str().unwrap());
}
}
}

View file

@ -0,0 +1,89 @@
use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::{One, Zero};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Point {
Infinity,
Point { x: BigInt, y: BigInt },
}
#[derive(Debug, Clone)]
pub struct WeierstrassCurve {
a: BigInt,
b: BigInt,
p: BigInt,
}
impl WeierstrassCurve {
pub fn new(a: BigInt, b: BigInt, p: BigInt) -> Self {
WeierstrassCurve { a, b, p }
}
fn mod_inverse(a: &BigInt, p: &BigInt) -> BigInt {
let egcd = a.extended_gcd(p);
egcd.x.mod_floor(p)
}
fn double_point(&self, point: &Point) -> Point {
match point {
Point::Point { x, y } => {
if y.is_zero() {
Point::Infinity
} else {
let three = BigInt::from(3);
let two = BigInt::from(2);
let lambda = (three * x * x + &self.a) * Self::mod_inverse(&(two * y), &self.p);
let lamba_sqr = (&lambda * &lambda).mod_floor(&self.p);
let x3 = (&lamba_sqr - x - x).mod_floor(&self.p);
let y3 = (&lambda * (x - &x3) - y).mod_floor(&self.p);
Point::Point { x: x3, y: y3 }
}
}
Point::Infinity => Point::Infinity,
}
}
pub fn add_points(&self, point1: &Point, point2: &Point) -> Point {
match (point1, point2) {
(Point::Point { x: x1, y: y1 }, Point::Point { x: x2, y: y2 }) => {
if point1 == point2 {
self.double_point(point1)
} else {
let lambda = (y2 - y1) * Self::mod_inverse(&(x2 - x1), &self.p);
let x3 = ((&lambda * &lambda) - x1 - x2).mod_floor(&self.p);
let y3: BigInt = ((&lambda * (x1 - &x3)) - y1).mod_floor(&self.p);
Point::Point { x: x3, y: y3 }
}
}
(Point::Point { x, y }, Point::Infinity) | (Point::Infinity, Point::Point { x, y }) => {
Point::Point {
x: x.clone(),
y: y.clone(),
}
}
(Point::Infinity, Point::Infinity) => Point::Infinity,
}
}
pub fn multiply_point(&self, s: &BigInt, point: &Point) -> Point {
let mut res = Point::Infinity;
let mut temp = point.clone();
let mut s = s.clone();
while s > BigInt::zero() {
if (&s % BigInt::from(2)) == BigInt::one() {
res = self.add_points(&res, &temp);
}
temp = self.double_point(&temp);
s >>= 1;
}
res
}
}