diff --git a/src/cli.rs b/src/cli.rs index b69bd44..ccfca12 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,18 +9,22 @@ use openssl::{ }; use serde_json::{from_reader, from_str}; -use crate::{bink1998, bink2002, confid, crypto::initialize_elliptic_curve}; +use crate::{ + bink1998, bink2002, confid, crypto::initialize_elliptic_curve, key::P_KEY_CHARSET, PK_LENGTH, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Mode { - Bink1998, - Bink2002, + Bink1998Generate, + Bink2002Generate, ConfirmationId, + Bink1998Validate, + Bink2002Validate, } impl Default for Mode { fn default() -> Self { - Self::Bink1998 + Self::Bink1998Generate } } @@ -55,6 +59,10 @@ pub struct Options { #[arg(short = 'c', long = "channel", default_value = "640")] channel_id: u32, + /// Product key to validate signature + #[arg(short = 'V', long = "validate")] + key_to_check: Option, + #[clap(skip)] application_mode: Mode, } @@ -165,8 +173,16 @@ impl Cli { let bink_id = u32::from_str_radix(&options.binkid, 16)?; + if options.key_to_check.is_some() { + options.application_mode = Mode::Bink1998Validate; + } + if bink_id >= 0x40 { - options.application_mode = Mode::Bink2002; + if options.key_to_check.is_some() { + options.application_mode = Mode::Bink2002Validate; + } else { + options.application_mode = Mode::Bink2002Generate; + } } if options.channel_id > 999 { @@ -187,13 +203,15 @@ impl Cli { pub fn run(&mut self) -> Result<()> { match self.options.application_mode { - Mode::Bink1998 => self.bink1998(), - Mode::Bink2002 => self.bink2002(), + Mode::Bink1998Generate => self.bink1998_generate(), + Mode::Bink2002Generate => self.bink2002_generate(), Mode::ConfirmationId => self.confirmation_id(), + Mode::Bink1998Validate => self.bink1998_validate(), + Mode::Bink2002Validate => self.bink2002_validate(), } } - fn bink1998(&mut self) -> Result<()> { + fn bink1998_generate(&mut self) -> Result<()> { let mut n_raw = self.options.channel_id * 1_000_000; // <- change let mut bn_rand = BigNum::new()?; @@ -230,7 +248,7 @@ impl Cli { Ok(()) } - fn bink2002(&mut self) -> Result<()> { + fn bink2002_generate(&mut self) -> Result<()> { let p_channel_id = self.options.channel_id; if self.options.verbose { @@ -267,6 +285,34 @@ impl Cli { Ok(()) } + fn bink1998_validate(&mut self) -> Result<()> { + let Ok(key) = Self::strip_key(self.options.key_to_check.as_ref().unwrap()) else { + return Err(anyhow!("Product key is in an incorrect format!")); + }; + + Self::print_key(&key); + if !bink1998::verify(&self.e_curve, &self.gen_point, &self.pub_point, &key)? { + return Err(anyhow!("Product key is invalid! Wrong BINK ID?")); + } + + println!("Key validated successfully!"); + Ok(()) + } + + fn bink2002_validate(&mut self) -> Result<()> { + let Ok(key) = Self::strip_key(self.options.key_to_check.as_ref().unwrap()) else { + return Err(anyhow!("Product key is in an incorrect format!")); + }; + + Self::print_key(&key); + if !bink2002::verify(&self.e_curve, &self.gen_point, &self.pub_point, &key)? { + return Err(anyhow!("Product key is invalid! Wrong BINK ID?")); + } + + println!("Key validated successfully!"); + Ok(()) + } + fn confirmation_id(&mut self) -> Result<()> { if let Some(instid) = &self.options.instid { let confirmation_id = confid::generate(instid)?; @@ -276,7 +322,7 @@ impl Cli { } fn print_key(pk: &str) { - assert_eq!(pk.len(), 25); + assert!(pk.len() >= PK_LENGTH); println!( "{}", pk.chars() @@ -290,4 +336,23 @@ impl Cli { }) ); } + + fn strip_key(in_key: &str) -> Result { + let out_key: String = in_key + .chars() + .filter_map(|c| { + let c = c.to_ascii_uppercase(); + if P_KEY_CHARSET.into_iter().any(|x| x == c) { + Some(c) + } else { + None + } + }) + .collect(); + if out_key.len() == PK_LENGTH { + Ok(out_key) + } else { + Err(anyhow!("Invalid key length")) + } + } } diff --git a/src/key.rs b/src/key.rs index 193acbc..8e0f678 100644 --- a/src/key.rs +++ b/src/key.rs @@ -2,17 +2,18 @@ use std::collections::VecDeque; use openssl::bn::BigNum; -const P_CHARSET: [char; 24] = [ +use crate::PK_LENGTH; + +/// The allowed character set in a product key. +pub const P_KEY_CHARSET: [char; 24] = [ 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R', 'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9', ]; -const PK_LENGTH: usize = 25; - pub fn base24_decode(cd_key: &str) -> Vec { let p_decoded_key: Vec = cd_key .chars() - .filter_map(|c| P_CHARSET.iter().position(|&x| x == c).map(|i| i as u8)) + .filter_map(|c| P_KEY_CHARSET.iter().position(|&x| x == c).map(|i| i as u8)) .collect(); let mut y = BigNum::from_u32(0).unwrap(); @@ -28,7 +29,7 @@ pub fn base24_decode(cd_key: &str) -> Vec { pub fn base24_encode(byte_seq: &[u8]) -> String { let mut z = BigNum::from_slice(byte_seq).unwrap(); let mut out: VecDeque = VecDeque::new(); - (0..=24).for_each(|_| out.push_front(P_CHARSET[z.div_word(24).unwrap() as usize])); + (0..=24).for_each(|_| out.push_front(P_KEY_CHARSET[z.div_word(24).unwrap() as usize])); out.iter().collect() } diff --git a/src/main.rs b/src/main.rs index 73492d7..1a5ccb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,8 @@ mod confid; mod crypto; mod key; +const PK_LENGTH: usize = 25; + fn main() -> Result<()> { cli::Cli::new()?.run() }