From bd51609f05ee1977b611413b54e6c352e6da44c3 Mon Sep 17 00:00:00 2001 From: Alex Page Date: Mon, 19 Jun 2023 16:42:50 -0400 Subject: [PATCH] Impelement generation for bink1998 --- src/bink1998.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++----- src/cli.rs | 39 +++++++++++++++++++-- src/main.rs | 1 + 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/bink1998.rs b/src/bink1998.rs index 8d396c6..16babb8 100644 --- a/src/bink1998.rs +++ b/src/bink1998.rs @@ -1,12 +1,17 @@ use anyhow::Result; use bitreader::BitReader; use openssl::{ - bn::{BigNum, BigNumContext}, + bn::{BigNum, BigNumContext, MsbOption}, ec::{EcGroup, EcPoint}, sha::sha1, }; -use crate::{key::base24_decode, FIELD_BYTES}; +use crate::{ + key::{base24_decode, base24_encode}, + FIELD_BITS, FIELD_BYTES, +}; + +const SHA_MSG_LENGTH_XP: usize = 4 + 2 * FIELD_BYTES; #[derive(Clone, Copy, Debug)] struct ProductKey { @@ -46,11 +51,11 @@ pub fn verify( p.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?; - let mut msg_buffer: [u8; 100] = [0; 100]; + let mut msg_buffer: [u8; SHA_MSG_LENGTH_XP] = [0; SHA_MSG_LENGTH_XP]; - let mut x_bin = x.to_vec(); + let mut x_bin = x.to_vec_padded(FIELD_BYTES as i32)?; x_bin.reverse(); - let mut y_bin = y.to_vec(); + let mut y_bin = y.to_vec_padded(FIELD_BYTES as i32)?; y_bin.reverse(); msg_buffer[0..4].copy_from_slice(&p_data.to_le_bytes()); @@ -59,7 +64,8 @@ pub fn verify( let msg_digest = sha1(&msg_buffer); - let hash: u32 = u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & ((1 << 28) - 1); + let hash: u32 = + u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32; Ok(hash == product_key.hash) } @@ -71,8 +77,65 @@ pub fn generate( private_key: &BigNum, p_serial: u32, p_upgrade: bool, -) -> String { - todo!() +) -> Result { + let mut num_context = BigNumContext::new().unwrap(); + + let mut c = BigNum::new()?; + let mut s = BigNum::new()?; + let mut s_2 = BigNum::new()?; + let mut x = BigNum::new()?; + let mut y = BigNum::new()?; + + let p_data = p_serial << 1 | p_upgrade as u32; + + let p_raw = loop { + let mut r = EcPoint::new(e_curve)?; + + // Generate a random number c consisting of 384 bits without any constraints. + c.rand(FIELD_BITS, MsbOption::MAYBE_ZERO, true)?; + + // Pick a random derivative of the base point on the elliptic curve. + // R = cG; + r.mul(e_curve, base_point, &c, &num_context)?; + + // Acquire its coordinates. + // x = R.x; y = R.y; + r.affine_coordinates(e_curve, &mut x, &mut y, &mut num_context)?; + + let mut msg_buffer: [u8; SHA_MSG_LENGTH_XP] = [0; SHA_MSG_LENGTH_XP]; + + 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(&p_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 p_hash: u32 = + u32::from_le_bytes(msg_digest[0..4].try_into().unwrap()) >> 4 & bitmask(28) as u32; + + s_2.copy_from_slice(&private_key.to_vec())?; + s_2.mul_word(p_hash)?; + + s.mod_add(&s_2, &c, gen_order, &mut num_context)?; + + let p_signature = u64::from_be_bytes(s.to_vec_padded(8)?.try_into().unwrap()); + + if p_signature <= bitmask(55) { + break pack(ProductKey { + upgrade: p_upgrade, + serial: p_serial, + hash: p_hash, + signature: p_signature, + }); + } + }; + + Ok(base24_encode(&p_raw)) } const HASH_LENGTH_BITS: u8 = 28; @@ -113,6 +176,10 @@ fn pack(p_key: ProductKey) -> Vec { .collect() } +fn bitmask(n: u64) -> u64 { + (1 << n) - 1 +} + #[cfg(test)] mod tests { use std::{fs::File, io::BufReader}; @@ -146,6 +213,13 @@ mod tests { let (e_curve, gen_point, pub_point) = initialize_elliptic_curve(p, a, b, gx, gy, kx, ky); assert!(super::verify(&e_curve, &gen_point, &pub_point, product_key).unwrap()); + assert!(!super::verify( + &e_curve, + &gen_point, + &pub_point, + "11111-R6BG2-39J83-RYKHF-W47TT" + ) + .unwrap()); } #[test] diff --git a/src/cli.rs b/src/cli.rs index 77bb25e..35dd1de 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,12 +3,12 @@ use std::{fs::File, io::BufReader, path::Path}; use anyhow::{anyhow, Result}; use clap::Parser; use openssl::{ - bn::BigNum, + bn::{BigNum, MsbOption}, ec::{EcGroup, EcPoint}, }; use serde_json::from_reader; -use crate::crypto::initialize_elliptic_curve; +use crate::{bink1998, crypto::initialize_elliptic_curve}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Mode { @@ -52,7 +52,7 @@ pub struct Options { /// Specify which Channel Identifier to use #[arg(short = 'c', long = "channel", default_value = "640")] - channel_id: i32, + channel_id: u32, #[clap(skip)] application_mode: Mode, @@ -190,6 +190,39 @@ impl Cli { } fn bink1998(&mut self) -> Result<()> { + let mut n_raw = self.options.channel_id * 1_000_000; // <- change + + let mut bn_rand = BigNum::new()?; + bn_rand.rand(19, MsbOption::MAYBE_ZERO, false)?; + + let o_raw: u32 = u32::from_be_bytes(bn_rand.to_vec_padded(4)?.try_into().unwrap()); + n_raw += o_raw % 999999; + + if self.options.verbose { + println!("> PID: {n_raw:09}"); + } + + let private_key = &self.gen_order - &self.private_key; + + let upgrade = false; + + for _ in 0..self.options.num_keys { + let p_key = bink1998::generate( + &self.e_curve, + &self.gen_point, + &self.gen_order, + &private_key, + n_raw, + upgrade, + )?; + Cli::print_key(&p_key); + + if bink1998::verify(&self.e_curve, &self.gen_point, &self.pub_point, &p_key)? { + self.count += 1; + } + } + + println!("Success count: {}/{}", self.count, self.options.num_keys); Ok(()) } diff --git a/src/main.rs b/src/main.rs index 99ce75a..df4c8e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod cli; mod crypto; mod key; +pub const FIELD_BITS: i32 = 384; pub const FIELD_BYTES: usize = 48; fn main() -> Result<()> {