diff --git a/Cargo.lock b/Cargo.lock index 1284077..015dce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,18 +57,6 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "bitflags" version = "1.3.2" @@ -84,15 +72,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "cc" version = "1.0.79" @@ -153,63 +132,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "cpufeatures" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" -dependencies = [ - "base16ct", - "crypto-bigint", - "ff", - "generic-array", - "group", - "rand_core", - "subtle", - "zeroize", -] - [[package]] name = "errno" version = "0.3.1" @@ -232,47 +154,19 @@ dependencies = [ ] [[package]] -name = "ff" -version = "0.13.0" +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "rand_core", - "subtle", + "foreign-types-shared", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "heck" @@ -333,37 +227,6 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "rand", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.18.0" @@ -371,10 +234,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "openssl" +version = "0.10.55" +source = "git+https://github.com/sfackler/rust-openssl#9d180ec94a92d2d08d6463ad047d9d7e7a8d9561" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "git+https://github.com/sfackler/rust-openssl#9d180ec94a92d2d08d6463ad047d9d7e7a8d9561" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "git+https://github.com/sfackler/rust-openssl#9d180ec94a92d2d08d6463ad047d9d7e7a8d9561" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" @@ -394,36 +292,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "rustix" version = "0.37.20" @@ -475,17 +343,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "simple_logger" version = "4.2.0" @@ -502,12 +359,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - [[package]] name = "syn" version = "2.0.21" @@ -539,25 +390,14 @@ dependencies = [ "syn", ] -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - [[package]] name = "umskt" version = "0.1.0" dependencies = [ "anyhow", "bitreader", - "elliptic-curve", - "num-bigint", - "num-integer", - "num-traits", - "rand", + "openssl", "serde_json", - "sha1", "thiserror", ] @@ -574,16 +414,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "version_check" -version = "0.9.4" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "windows-sys" @@ -720,9 +554,3 @@ dependencies = [ "simple_logger", "umskt", ] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index 582ac5a..cd188fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [workspace] -resolver = "2" members = [ "umskt", diff --git a/umskt/Cargo.toml b/umskt/Cargo.toml index 0957c9a..701f790 100644 --- a/umskt/Cargo.toml +++ b/umskt/Cargo.toml @@ -6,12 +6,7 @@ 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" -rand = "0.8.5" -sha1 = "0.10.5" +openssl = { git = "https://github.com/sfackler/rust-openssl" } thiserror = "1.0.40" [dev-dependencies] diff --git a/umskt/src/bink1998.rs b/umskt/src/bink1998.rs index 3c893ec..cd8556a 100644 --- a/umskt/src/bink1998.rs +++ b/umskt/src/bink1998.rs @@ -1,24 +1,20 @@ -use std::{ - cmp::Ordering, - fmt::{Display, Formatter}, -}; +use std::fmt::{Display, Formatter}; use anyhow::{bail, Result}; use bitreader::BitReader; -use num_bigint::{BigInt, BigUint, RandomBits}; -use num_integer::Integer; -use num_traits::{FromPrimitive, ToPrimitive}; -use rand::Rng; -use sha1::{Digest, Sha1}; +use openssl::{ + bn::{BigNum, BigNumContext, MsbOption}, + ec::{EcGroup, EcPoint}, + sha::sha1, +}; use crate::{ crypto::{EllipticCurve, PrivateKey}, key::{base24_decode, base24_encode, strip_key}, math::bitmask, - weierstrass_curve::{Point, WeierstrassCurve}, }; -const FIELD_BITS: u64 = 384; +const FIELD_BITS: i32 = 384; const FIELD_BYTES: usize = 48; const SHA_MSG_LENGTH: usize = 4 + 2 * FIELD_BYTES; @@ -48,24 +44,22 @@ impl ProductKey { let sequence = match sequence { Some(serial) => serial, None => { - 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 + 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 } }; // Default to upgrade=false let upgrade = upgrade.unwrap_or(false); - let private = &private_key.gen_order - &private_key.private_key; - // Generate a new random key let product_key = Self::generate( &curve.curve, &curve.gen_point, &private_key.gen_order, - &private, + &private_key.private_key, channel_id, sequence, upgrade, @@ -89,75 +83,62 @@ impl ProductKey { } fn generate( - e_curve: &WeierstrassCurve, - base_point: &Point, - gen_order: &BigInt, - private_key: &BigInt, + e_curve: &EcGroup, + base_point: &EcPoint, + gen_order: &BigNum, + private_key: &BigNum, channel_id: u32, sequence: u32, upgrade: bool, ) -> Result { + 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 mut rng = rand::thread_rng(); - let product_key = loop { + let mut r = EcPoint::new(e_curve)?; + // 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(); + c.rand(FIELD_BITS, MsbOption::MAYBE_ZERO, false)?; // Pick a random derivative of the base point on the elliptic curve. // R = cG; - let r = e_curve.multiply_point(&c, base_point); + r.mul(e_curve, base_point, &c, &num_context)?; // 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!"), - }; + r.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?; let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH]; - let x_bin = x.to_bytes_le().1; - let x_bin = match x_bin.len().cmp(&FIELD_BYTES) { - Ordering::Less => (0..FIELD_BYTES - x_bin.len()) - .map(|_| 0) - .chain(x_bin.into_iter()) - .collect(), - Ordering::Greater => continue, - Ordering::Equal => x_bin, - }; - let y_bin = y.to_bytes_le().1; - let y_bin = match y_bin.len().cmp(&FIELD_BYTES) { - Ordering::Less => (0..FIELD_BYTES - y_bin.len()) - .map(|_| 0) - .chain(y_bin.into_iter()) - .collect(), - Ordering::Greater => continue, - Ordering::Equal => y_bin, - }; + 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(); 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 = { - let mut hasher = Sha1::new(); - hasher.update(msg_buffer); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer); let hash: u32 = u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32; - let mut ek = private_key.clone(); - ek *= hash; + ek = (*private_key).to_owned()?; + ek.mul_word(hash)?; - let s = (ek + c).mod_floor(gen_order); + s.mod_add(&ek, &c, gen_order, &mut num_context)?; - let signature = s.to_u64().unwrap_or(0); + let signature = u64::from_be_bytes(s.to_vec_padded(8)?.try_into().unwrap()); if signature <= bitmask(55) { break Self { @@ -175,43 +156,36 @@ impl ProductKey { fn verify( &self, - e_curve: &WeierstrassCurve, - base_point: &Point, - public_key: &Point, + e_curve: &EcGroup, + base_point: &EcPoint, + public_key: &EcPoint, ) -> Result { - let e = BigInt::from_u32(self.hash).unwrap(); - let s = BigInt::from_u64(self.signature).unwrap(); + let mut ctx = BigNumContext::new()?; - let t = e_curve.multiply_point(&s, base_point); - let mut p = e_curve.multiply_point(&e, public_key); + 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()?; - p = e_curve.add_points(&p, &t); + let mut t = EcPoint::new(e_curve)?; + let mut p = EcPoint::new(e_curve)?; - let (x, y) = match p { - Point::Point { x, y } => (x, y), - Point::Infinity => bail!("Point at infinity!"), - }; + 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 mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH]; - let x_bin = x.to_bytes_le().1; - let x_bin = if x_bin.len() < FIELD_BYTES { - (0..FIELD_BYTES - x_bin.len()) - .map(|_| 0) - .chain(x_bin.into_iter()) - .collect() - } else { - x_bin - }; - let y_bin = y.to_bytes_le().1; - let y_bin = if y_bin.len() < FIELD_BYTES { - (0..FIELD_BYTES - y_bin.len()) - .map(|_| 0) - .chain(y_bin.into_iter()) - .collect() - } else { - y_bin - }; + 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 serial = self.channel_id * 1_000_000 + self.sequence; let data = serial << 1 | self.upgrade as u32; @@ -220,11 +194,7 @@ 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 = { - let mut hasher = Sha1::new(); - hasher.update(msg_buffer); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer); let hash: u32 = u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32; @@ -295,7 +265,7 @@ mod tests { use serde_json::from_reader; - use crate::{bink1998, crypto::EllipticCurve}; + use crate::crypto::EllipticCurve; #[test] fn verify_test() { @@ -313,15 +283,16 @@ mod tests { let p = bink["p"].as_str().unwrap(); let a = bink["a"].as_str().unwrap(); + let b = bink["b"].as_str().unwrap(); let gx = bink["g"]["x"].as_str().unwrap(); let gy = bink["g"]["y"].as_str().unwrap(); let kx = bink["pub"]["x"].as_str().unwrap(); let ky = bink["pub"]["y"].as_str().unwrap(); - let curve = EllipticCurve::new(p, a, gx, gy, kx, ky).unwrap(); + let curve = EllipticCurve::new(p, a, b, gx, gy, kx, ky).unwrap(); - assert!(bink1998::ProductKey::from_key(&curve, product_key).is_ok()); - assert!(bink1998::ProductKey::from_key(&curve, "11111-R6BG2-39J83-RYKHF-W47TT").is_err()); + assert!(super::ProductKey::from_key(&curve, product_key).is_ok()); + assert!(super::ProductKey::from_key(&curve, "11111-R6BG2-39J83-RYKHF-W47TT").is_err()); } #[test] diff --git a/umskt/src/bink2002.rs b/umskt/src/bink2002.rs index 8e182b9..96e37fb 100644 --- a/umskt/src/bink2002.rs +++ b/umskt/src/bink2002.rs @@ -1,25 +1,21 @@ -use std::{ - cmp::Ordering, - fmt::{Display, Formatter}, -}; +use std::fmt::{Display, Formatter}; use anyhow::{bail, Result}; use bitreader::BitReader; -use num_bigint::{BigInt, BigUint, RandomBits}; -use num_integer::Integer; -use num_traits::ToPrimitive; -use rand::Rng; -use sha1::{Digest, Sha1}; +use openssl::{ + bn::{BigNum, BigNumContext, MsbOption}, + ec::{EcGroup, EcPoint}, + rand::rand_bytes, + sha::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: u64 = 512; +const FIELD_BITS: i32 = 512; const FIELD_BYTES: usize = 64; const SHA_MSG_LENGTH: usize = 3 + 2 * FIELD_BYTES; @@ -51,10 +47,9 @@ impl ProductKey { let auth_info = match auth_info { Some(auth_info) => auth_info, None => { - 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 % (bitmask(10) as u32) + let mut auth_info_bytes = [0_u8; 4]; + rand_bytes(&mut auth_info_bytes)?; + u32::from_ne_bytes(auth_info_bytes) & ((1 << 10) - 1) } }; @@ -93,50 +88,38 @@ impl ProductKey { } fn generate( - e_curve: &WeierstrassCurve, - base_point: &Point, - gen_order: &BigInt, - private_key: &BigInt, + e_curve: &EcGroup, + base_point: &EcPoint, + gen_order: &BigNum, + private_key: &BigNum, channel_id: u32, auth_info: u32, upgrade: bool, ) -> Result { - let data = channel_id << 1 | upgrade as u32; + let mut num_context = BigNumContext::new().unwrap(); - let mut rng = rand::thread_rng(); + 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 no_square = false; let key = loop { - let c: BigUint = rng.sample(RandomBits::new(FIELD_BITS)); - let mut c: BigInt = c.into(); + let mut r = EcPoint::new(e_curve)?; - let r = e_curve.multiply_point(&c, base_point); + c.rand(FIELD_BITS, MsbOption::MAYBE_ZERO, false)?; - let (x, y) = match r { - Point::Point { x, y } => (x, y), - Point::Infinity => bail!("Point at infinity!"), - }; + r.mul(e_curve, base_point, &c, &num_context)?; + + r.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?; let mut msg_buffer: [u8; SHA_MSG_LENGTH] = [0; SHA_MSG_LENGTH]; - let x_bin = x.to_bytes_le().1; - let x_bin = match x_bin.len().cmp(&FIELD_BYTES) { - Ordering::Less => (0..FIELD_BYTES - x_bin.len()) - .map(|_| 0) - .chain(x_bin.into_iter()) - .collect(), - Ordering::Greater => continue, - Ordering::Equal => x_bin, - }; - let y_bin = y.to_bytes_le().1; - let y_bin = match y_bin.len().cmp(&FIELD_BYTES) { - Ordering::Less => (0..FIELD_BYTES - y_bin.len()) - .map(|_| 0) - .chain(y_bin.into_iter()) - .collect(), - Ordering::Greater => continue, - Ordering::Equal => y_bin, - }; + 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(); msg_buffer[0x00] = 0x79; msg_buffer[0x01] = (data & 0x00FF) as u8; @@ -145,11 +128,7 @@ 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 = { - let mut hasher = Sha1::new(); - hasher.update(msg_buffer); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer); let hash: u32 = by_dword(&msg_digest[0..4]) & bitmask(31) as u32; @@ -165,43 +144,42 @@ impl ProductKey { msg_buffer[0x09] = 0x00; msg_buffer[0x0A] = 0x00; - let msg_digest = { - let mut hasher = Sha1::new(); - hasher.update(&msg_buffer[..=0x0A]); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer[..=0x0A]); 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 = BigInt::from(i_signature); + let mut e = BigNum::from_slice(&i_signature.to_be_bytes())?; - e = (e * private_key).mod_floor(gen_order); + let e_2 = e.to_owned()?; + e.mod_mul(&e_2, private_key, gen_order, &mut num_context)?; - let mut s = e.clone(); + let mut s = e.to_owned()?; - s = (&s * &s).mod_floor(gen_order); + let s_2 = s.to_owned()?; + s.mod_sqr(&s_2, gen_order, &mut num_context)?; - c <<= 2; + let c_2 = c.to_owned()?; + c.lshift(&c_2, 2)?; s = &s + &c; - match mod_sqrt(&s, gen_order) { - Some(res) => s = res, - None => { - no_square = true; - } - } + let s_2 = s.to_owned()?; + if s.mod_sqrt(&s_2, gen_order, &mut num_context).is_err() { + no_square = true; + }; - s = (s - e).mod_floor(gen_order); + let s_2 = s.to_owned()?; + s.mod_sub(&s_2, &e, gen_order, &mut num_context)?; - if s.is_odd() { + if s.is_bit_set(0) { s = &s + gen_order; } - s >>= 1; + let s_2 = s.to_owned()?; + s.rshift1(&s_2)?; - let signature = s.to_u64().unwrap_or(0); + let signature = u64::from_be_bytes(s.to_vec_padded(8)?.try_into().unwrap()); let product_key = Self { upgrade, @@ -223,10 +201,12 @@ impl ProductKey { fn verify( &self, - e_curve: &WeierstrassCurve, - base_point: &Point, - public_key: &Point, + e_curve: &EcGroup, + base_point: &EcPoint, + public_key: &EcPoint, ) -> Result { + 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]; @@ -243,47 +223,39 @@ impl ProductKey { msg_buffer[0x09] = 0x00; msg_buffer[0x0A] = 0x00; - let msg_digest = { - let mut hasher = Sha1::new(); - hasher.update(&msg_buffer[..=0x0A]); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer[..=0x0A]); 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 = BigInt::from(i_signature); - let s = BigInt::from(self.signature); + let e = BigNum::from_slice(&i_signature.to_be_bytes())?; + let s = BigNum::from_slice(&self.signature.to_be_bytes())?; - let t = e_curve.multiply_point(&s, base_point); - let mut p = e_curve.multiply_point(&e, public_key); + let mut x = BigNum::new()?; + let mut y = BigNum::new()?; - p = e_curve.add_points(&t, &p); - p = e_curve.multiply_point(&s, &p); + let mut p = EcPoint::new(e_curve)?; + let mut t = EcPoint::new(e_curve)?; - let (x, y) = match p { - Point::Point { x, y } => (x, y), - Point::Infinity => bail!("Point at infinity!"), - }; + t.mul(e_curve, base_point, &s, &num_context)?; + p.mul(e_curve, public_key, &e, &num_context)?; - let x_bin = x.to_bytes_le().1; - let x_bin = if x_bin.len() < FIELD_BYTES { - (0..FIELD_BYTES - x_bin.len()) - .map(|_| 0) - .chain(x_bin.into_iter()) - .collect() - } else { - x_bin - }; - let y_bin = y.to_bytes_le().1; - let y_bin = if y_bin.len() < FIELD_BYTES { - (0..FIELD_BYTES - y_bin.len()) - .map(|_| 0) - .chain(y_bin.into_iter()) - .collect() - } else { - y_bin - }; + { + 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(); msg_buffer[0x00] = 0x79; msg_buffer[0x01] = (data & 0x00FF) as u8; @@ -292,11 +264,7 @@ 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 = { - let mut hasher = Sha1::new(); - hasher.update(msg_buffer); - hasher.finalize() - }; + let msg_digest = sha1(&msg_buffer); let hash: u32 = by_dword(&msg_digest[0..4]) & bitmask(31) as u32; @@ -385,12 +353,13 @@ mod tests { let p = bink["p"].as_str().unwrap(); let a = bink["a"].as_str().unwrap(); + let b = bink["b"].as_str().unwrap(); let gx = bink["g"]["x"].as_str().unwrap(); let gy = bink["g"]["y"].as_str().unwrap(); let kx = bink["pub"]["x"].as_str().unwrap(); let ky = bink["pub"]["y"].as_str().unwrap(); - let curve = EllipticCurve::new(p, a, gx, gy, kx, ky).unwrap(); + 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-YRGC8-4KYTG-C3FCC-JCFDY").is_err()); diff --git a/umskt/src/crypto.rs b/umskt/src/crypto.rs index f817858..831971a 100644 --- a/umskt/src/crypto.rs +++ b/umskt/src/crypto.rs @@ -1,24 +1,24 @@ use anyhow::Result; -use num_bigint::BigInt; -use num_traits::Num; - -use crate::weierstrass_curve::{Point, WeierstrassCurve}; +use openssl::{ + bn::{BigNum, BigNumContext}, + ec::{EcGroup, EcPoint}, +}; pub struct EllipticCurve { - pub curve: WeierstrassCurve, - pub gen_point: Point, - pub pub_point: Point, + pub curve: EcGroup, + pub gen_point: EcPoint, + pub pub_point: EcPoint, } pub struct PrivateKey { - pub gen_order: BigInt, - pub private_key: BigInt, + pub gen_order: BigNum, + pub private_key: BigNum, } impl PrivateKey { pub fn new(gen_order: &str, private_key: &str) -> Result { - let gen_order = BigInt::from_str_radix(gen_order, 10)?; - let private_key = BigInt::from_str_radix(private_key, 10)?; + let gen_order = BigNum::from_dec_str(gen_order)?; + let private_key = &gen_order - &BigNum::from_dec_str(private_key)?; Ok(Self { gen_order, private_key, @@ -30,29 +30,29 @@ impl EllipticCurve { pub fn new( p: &str, a: &str, + b: &str, generator_x: &str, generator_y: &str, public_key_x: &str, public_key_y: &str, ) -> Result { - let p = BigInt::from_str_radix(p, 10)?; - let a = BigInt::from_str_radix(a, 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 mut context = BigNumContext::new()?; - let curve = WeierstrassCurve::new(a, p); + 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 gen_point = Point::Point { - x: generator_x, - y: generator_y, - }; + let curve = EcGroup::from_components(p, a, b, &mut context)?; - let pub_point = Point::Point { - x: public_key_x, - y: public_key_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)?; Ok(Self { curve, diff --git a/umskt/src/key.rs b/umskt/src/key.rs index 4dd8b2d..562a8a2 100644 --- a/umskt/src/key.rs +++ b/umskt/src/key.rs @@ -1,9 +1,7 @@ use std::collections::VecDeque; use anyhow::{anyhow, Result}; -use num_bigint::{BigInt, Sign}; -use num_integer::Integer; -use num_traits::{ToPrimitive, Zero}; +use openssl::bn::BigNum; const PK_LENGTH: usize = 25; @@ -19,24 +17,20 @@ pub(crate) fn base24_decode(cd_key: &str) -> Result> { .filter_map(|c| KEY_CHARSET.iter().position(|&x| x == c).map(|i| i as u8)) .collect(); - let mut y = BigInt::zero(); + let mut y = BigNum::from_u32(0).unwrap(); for i in decoded_key { - y *= PK_LENGTH - 1; - y += i as u32; + y.mul_word((PK_LENGTH - 1) as u32).unwrap(); + y.add_word(i.into()).unwrap(); } - Ok(y.to_bytes_be().1) + Ok(y.to_vec()) } pub(crate) fn base24_encode(byte_seq: &[u8]) -> Result { - let mut z = BigInt::from_bytes_be(Sign::Plus, byte_seq); + let mut z = BigNum::from_slice(byte_seq).unwrap(); let mut out: VecDeque = VecDeque::new(); - (0..=24).for_each(|_| { - let (quo, rem) = z.div_rem(&BigInt::from(24)); - z = quo; - out.push_front(KEY_CHARSET[rem.to_u32().unwrap() as usize]); - }); + (0..=24).for_each(|_| out.push_front(KEY_CHARSET[z.div_word(24).unwrap() as usize])); Ok(out.iter().collect()) } diff --git a/umskt/src/lib.rs b/umskt/src/lib.rs index df0ac79..2910152 100644 --- a/umskt/src/lib.rs +++ b/umskt/src/lib.rs @@ -4,5 +4,3 @@ pub mod confid; pub mod crypto; mod key; mod math; -mod msr; -mod weierstrass_curve; diff --git a/umskt/src/msr.rs b/umskt/src/msr.rs deleted file mode 100644 index 780b216..0000000 --- a/umskt/src/msr.rs +++ /dev/null @@ -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 { - 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; - } -} diff --git a/umskt/src/weierstrass_curve.rs b/umskt/src/weierstrass_curve.rs deleted file mode 100644 index c03fe01..0000000 --- a/umskt/src/weierstrass_curve.rs +++ /dev/null @@ -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 - } -} diff --git a/xpkey/src/main.rs b/xpkey/src/main.rs index 600b8fd..f9e709e 100644 --- a/xpkey/src/main.rs +++ b/xpkey/src/main.rs @@ -102,6 +102,7 @@ fn validate(args: &ValidateArgs) -> Result<()> { fn initialize_curve(bink: &Bink, bink_id: &str) -> Result { let p = &bink.p; let a = &bink.a; + let b = &bink.b; let gx = &bink.g.x; let gy = &bink.g.y; let kx = &bink.public.x; @@ -109,7 +110,7 @@ fn initialize_curve(bink: &Bink, bink_id: &str) -> Result { log::info!("Elliptic curve parameters for BINK ID {bink_id}:\n{bink}"); - EllipticCurve::new(p, a, gx, gy, kx, ky) + EllipticCurve::new(p, a, b, gx, gy, kx, ky) } fn bink1998_generate(