Condense code and add docs
This commit is contained in:
parent
369b8b04bc
commit
1085bec913
9 changed files with 221 additions and 183 deletions
|
@ -5,3 +5,9 @@ members = [
|
|||
"umskt",
|
||||
"xpkey",
|
||||
]
|
||||
|
||||
[profile.release.package.xpkey]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
|
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# UMSKT Rust Edition
|
|
@ -1,3 +1,4 @@
|
|||
//! Structs to deal with older BINK (< `0x40`) product keys
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter},
|
||||
|
@ -12,10 +13,9 @@ use rand::Rng;
|
|||
use sha1::{Digest, Sha1};
|
||||
|
||||
use crate::{
|
||||
crypto::{EllipticCurve, PrivateKey},
|
||||
crypto::{EllipticCurve, Point, PrivateKey},
|
||||
key::{base24_decode, base24_encode, strip_key},
|
||||
math::bitmask,
|
||||
weierstrass_curve::{Point, WeierstrassCurve},
|
||||
};
|
||||
|
||||
const FIELD_BITS: u64 = 384;
|
||||
|
@ -27,6 +27,9 @@ const SERIAL_LENGTH_BITS: u8 = 30;
|
|||
const UPGRADE_LENGTH_BITS: u8 = 1;
|
||||
const EVERYTHING_ELSE: u8 = HASH_LENGTH_BITS + SERIAL_LENGTH_BITS + UPGRADE_LENGTH_BITS;
|
||||
|
||||
/// A product key for a BINK ID less than `0x40`
|
||||
///
|
||||
/// Every `ProductKey` contains a valid key for its given parameters.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ProductKey {
|
||||
upgrade: bool,
|
||||
|
@ -37,6 +40,9 @@ pub struct ProductKey {
|
|||
}
|
||||
|
||||
impl ProductKey {
|
||||
/// Generates a new product key for the given parameters.
|
||||
///
|
||||
/// The key is verified to be valid before being returned.
|
||||
pub fn new(
|
||||
curve: &EllipticCurve,
|
||||
private_key: &PrivateKey,
|
||||
|
@ -62,7 +68,7 @@ impl ProductKey {
|
|||
|
||||
// Generate a new random key
|
||||
let product_key = Self::generate(
|
||||
&curve.curve,
|
||||
curve,
|
||||
&curve.gen_point,
|
||||
&private_key.gen_order,
|
||||
&private,
|
||||
|
@ -72,24 +78,30 @@ impl ProductKey {
|
|||
)?;
|
||||
|
||||
// Make sure the key is valid
|
||||
product_key.verify(&curve.curve, &curve.gen_point, &curve.pub_point)?;
|
||||
product_key.verify(curve, &curve.gen_point, &curve.pub_point)?;
|
||||
|
||||
// Ship it
|
||||
Ok(product_key)
|
||||
}
|
||||
|
||||
/// Validates an existing product key string and tried to create a new `ProductKey` from it.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `curve` - The elliptic curve to use for verification.
|
||||
/// * `key` - Should be 25 characters long, not including the (optional) hyphens.
|
||||
pub fn from_key(curve: &EllipticCurve, key: &str) -> Result<Self> {
|
||||
let key = strip_key(key)?;
|
||||
let Ok(packed_key) = base24_decode(&key) else {
|
||||
bail!("Product key is in an incorrect format!")
|
||||
};
|
||||
let product_key = Self::from_packed(&packed_key)?;
|
||||
product_key.verify(&curve.curve, &curve.gen_point, &curve.pub_point)?;
|
||||
product_key.verify(curve, &curve.gen_point, &curve.pub_point)?;
|
||||
Ok(product_key)
|
||||
}
|
||||
|
||||
fn generate(
|
||||
e_curve: &WeierstrassCurve,
|
||||
e_curve: &EllipticCurve,
|
||||
base_point: &Point,
|
||||
gen_order: &BigInt,
|
||||
private_key: &BigInt,
|
||||
|
@ -103,16 +115,11 @@ impl ProductKey {
|
|||
let mut rng = rand::thread_rng();
|
||||
|
||||
let product_key = loop {
|
||||
// Generate a random number c consisting of 384 bits without any constraints.
|
||||
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;
|
||||
let r = e_curve.multiply_point(&c, base_point);
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
let (x, y) = match r {
|
||||
Point::Point { x, y } => (x, y),
|
||||
Point::Infinity => bail!("Point at infinity!"),
|
||||
|
@ -175,7 +182,7 @@ impl ProductKey {
|
|||
|
||||
fn verify(
|
||||
&self,
|
||||
e_curve: &WeierstrassCurve,
|
||||
e_curve: &EllipticCurve,
|
||||
base_point: &Point,
|
||||
public_key: &Point,
|
||||
) -> Result<bool> {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//! Structs to deal with newer BINK (>= `0x40`) product keys
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter},
|
||||
|
@ -12,11 +13,9 @@ use rand::Rng;
|
|||
use sha1::{Digest, Sha1};
|
||||
|
||||
use crate::{
|
||||
crypto::{EllipticCurve, PrivateKey},
|
||||
crypto::{mod_sqrt, EllipticCurve, Point, 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: u64 = 512;
|
||||
|
@ -30,6 +29,9 @@ const UPGRADE_LENGTH_BITS: u8 = 1;
|
|||
const EVERYTHING_ELSE: u8 =
|
||||
SIGNATURE_LENGTH_BITS + HASH_LENGTH_BITS + CHANNEL_ID_LENGTH_BITS + UPGRADE_LENGTH_BITS;
|
||||
|
||||
/// A product key for a BINK ID `0x40` or higher
|
||||
///
|
||||
/// Every `ProductKey` contains a valid key for its given parameters.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ProductKey {
|
||||
upgrade: bool,
|
||||
|
@ -40,6 +42,9 @@ pub struct ProductKey {
|
|||
}
|
||||
|
||||
impl ProductKey {
|
||||
/// Generates a new product key for the given parameters.
|
||||
///
|
||||
/// The key is verified to be valid before being returned.
|
||||
pub fn new(
|
||||
curve: &EllipticCurve,
|
||||
private_key: &PrivateKey,
|
||||
|
@ -63,7 +68,7 @@ impl ProductKey {
|
|||
|
||||
// Generate a new random key
|
||||
let product_key = Self::generate(
|
||||
&curve.curve,
|
||||
curve,
|
||||
&curve.gen_point,
|
||||
&private_key.gen_order,
|
||||
&private_key.private_key,
|
||||
|
@ -73,19 +78,25 @@ impl ProductKey {
|
|||
)?;
|
||||
|
||||
// Make sure the key is valid
|
||||
product_key.verify(&curve.curve, &curve.gen_point, &curve.pub_point)?;
|
||||
product_key.verify(curve, &curve.gen_point, &curve.pub_point)?;
|
||||
|
||||
// Ship it
|
||||
Ok(product_key)
|
||||
}
|
||||
|
||||
/// Validates an existing product key string and tried to create a new `ProductKey` from it.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `curve` - The elliptic curve to use for verification.
|
||||
/// * `key` - Should be 25 characters long, not including the (optional) hyphens.
|
||||
pub fn from_key(curve: &EllipticCurve, key: &str) -> Result<Self> {
|
||||
let key = strip_key(key)?;
|
||||
let Ok(packed_key) = base24_decode(&key) else {
|
||||
bail!("Product key is in an incorrect format!")
|
||||
};
|
||||
let product_key = Self::from_packed(&packed_key)?;
|
||||
let verified = product_key.verify(&curve.curve, &curve.gen_point, &curve.pub_point)?;
|
||||
let verified = product_key.verify(curve, &curve.gen_point, &curve.pub_point)?;
|
||||
if !verified {
|
||||
bail!("Product key is invalid! Wrong BINK ID?");
|
||||
}
|
||||
|
@ -93,7 +104,7 @@ impl ProductKey {
|
|||
}
|
||||
|
||||
fn generate(
|
||||
e_curve: &WeierstrassCurve,
|
||||
e_curve: &EllipticCurve,
|
||||
base_point: &Point,
|
||||
gen_order: &BigInt,
|
||||
private_key: &BigInt,
|
||||
|
@ -223,7 +234,7 @@ impl ProductKey {
|
|||
|
||||
fn verify(
|
||||
&self,
|
||||
e_curve: &WeierstrassCurve,
|
||||
e_curve: &EllipticCurve,
|
||||
base_point: &Point,
|
||||
public_key: &Point,
|
||||
) -> Result<bool> {
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
//! Code that deals with elliptic curve cryptography
|
||||
use anyhow::Result;
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::Num;
|
||||
use num_integer::Integer;
|
||||
use num_traits::{Num, One, Zero};
|
||||
|
||||
use crate::weierstrass_curve::{Point, WeierstrassCurve};
|
||||
/// Represents a point (possibly) on an elliptic curve.
|
||||
///
|
||||
/// This is either the point at infinity, or a point with affine coordinates `x` and `y`.
|
||||
/// It is not guaranteed to be on the curve.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Point {
|
||||
Infinity,
|
||||
Point { x: BigInt, y: BigInt },
|
||||
}
|
||||
|
||||
/// Represents an elliptic curve of the form `y^2 = x^3 + ax + b (mod p)`
|
||||
///
|
||||
/// `b` is not used in any of the calculations, so is not stored.
|
||||
///
|
||||
/// This implements all the necessary elliptic curve arithmetic for verifying and generating
|
||||
/// product keys.
|
||||
pub struct EllipticCurve {
|
||||
pub curve: WeierstrassCurve,
|
||||
a: BigInt,
|
||||
p: BigInt,
|
||||
pub gen_point: Point,
|
||||
pub pub_point: Point,
|
||||
}
|
||||
|
||||
/// Stores the additional data necessary to generate product keys.
|
||||
pub struct PrivateKey {
|
||||
pub gen_order: BigInt,
|
||||
pub private_key: BigInt,
|
||||
|
@ -27,6 +45,7 @@ impl PrivateKey {
|
|||
}
|
||||
|
||||
impl EllipticCurve {
|
||||
/// Creates a new elliptic curve from the given parameters. `b` is not necessary.
|
||||
pub fn new(
|
||||
p: &str,
|
||||
a: &str,
|
||||
|
@ -42,8 +61,6 @@ impl EllipticCurve {
|
|||
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 curve = WeierstrassCurve::new(a, p);
|
||||
|
||||
let gen_point = Point::Point {
|
||||
x: generator_x,
|
||||
y: generator_y,
|
||||
|
@ -55,9 +72,152 @@ impl EllipticCurve {
|
|||
};
|
||||
|
||||
Ok(Self {
|
||||
curve,
|
||||
a,
|
||||
p,
|
||||
gen_point,
|
||||
pub_point,
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds two points on the curve together.
|
||||
///
|
||||
/// If the points are the same, it doubles the point.
|
||||
///
|
||||
/// If one of the points is the point at infinity, it returns the other point.
|
||||
///
|
||||
/// If both points are the point at infinity, it returns the point at infinity.
|
||||
pub(crate) 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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies a point by a scalar.
|
||||
///
|
||||
/// Uses the double-and-add algorithm.
|
||||
pub(crate) 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the legendre symbol of `p`: `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)
|
||||
}
|
||||
|
||||
/// Calculates the modular square root of `n` such that `result^2 = n (mod p)`
|
||||
/// using the Tonelli-Shanks algorithm. Returns `None` if `p` is not prime.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `n` - The number to find the square root of
|
||||
/// * `p` - The prime modulus (_must_ be prime)
|
||||
pub(crate) 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
//! # UMSKT Rust Edition
|
||||
//!
|
||||
//! This is an unofficial Rust port of the UMSKT project. It is a pure Rust implementation
|
||||
//! rather than a binding, so it does not require any C or C++ dependencies and can be
|
||||
//! built for any platform supported by Rust and `std`.
|
||||
//!
|
||||
//! It does not include the required `keys.json` file used by UMSKT. That needs to be found elsewhere.
|
||||
//!
|
||||
//! See `README.md` for more information.
|
||||
//!
|
||||
pub mod bink1998;
|
||||
pub mod bink2002;
|
||||
pub mod confid;
|
||||
pub mod crypto;
|
||||
mod key;
|
||||
mod math;
|
||||
mod msr;
|
||||
mod weierstrass_curve;
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
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,
|
||||
p: BigInt,
|
||||
}
|
||||
|
||||
impl WeierstrassCurve {
|
||||
pub fn new(a: BigInt, p: BigInt) -> Self {
|
||||
WeierstrassCurve { a, 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
|
||||
}
|
||||
}
|
|
@ -3,12 +3,6 @@ name = "xpkey"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
umskt = { path = "../umskt" }
|
||||
anyhow = "1.0.71"
|
||||
|
|
Loading…
Add table
Reference in a new issue