Impelement generation for bink1998

This commit is contained in:
Alex Page 2023-06-19 16:42:50 -04:00
parent e9e7b1506f
commit bd51609f05
3 changed files with 119 additions and 11 deletions

View file

@ -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<String> {
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<u8> {
.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]

View file

@ -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(())
}

View file

@ -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<()> {