WindowsXPKg/src/libumskt/pidgen2/PIDGEN2.cpp

316 lines
8.2 KiB
C++

/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 06/17/2023
* @Maintainer Neo
*/
#include "PIDGEN2.h"
const std::vector<std::string> PIDGEN2::channelIDDisallowList = {"333", "444", "555", "666", "777", "888", "999"};
const std::vector<std::string> PIDGEN2::validYears = {"95", "96", "97", "98", "99", "00", "01", "02"};
/**
* Generates a PID 2.0 key, output is placed in pKey
*
* @param pKey
* @return true
*/
BOOL PIDGEN2::Generate(std::string &pKey)
{
Integer random;
random.Randomize(rng, sizeof(DWORD32) * 8);
info.ChannelID = info.ChannelID % MaxChannelID;
info.Serial = info.Serial % MaxSerial;
if (info.isOEM)
{
info.Day = info.Day % Integer(366);
// info.Year = info.Year;
info.OEMID = (info.ChannelID * TEN) + (info.Serial / (MaxSerial / TEN));
info.Serial %= (MaxSerial / TEN);
info.OEMID = (info.OEMID * TEN) + GenerateMod7(info.OEMID);
DWORD32 day = EncodeN(info.Day), year = EncodeN(info.Year), serial = EncodeN(info.Serial),
oemid = EncodeN(info.OEMID);
if (debug)
{
fmt::print("\n{:03d}{:02d}-OEM-{:07d}-{:05d}\n", day, year, oemid, serial);
}
pKey = fmt::format("{:03d}{:02d}{:07d}{:05d}", day, year, oemid, serial);
}
else if (info.isOffice)
{
info.ChannelID = (info.ChannelID * TEN) + ((info.ChannelID % TEN) + 1);
info.Serial = (info.Serial * TEN) + GenerateMod7(info.Serial);
DWORD32 channelid = EncodeN(info.ChannelID), serial = EncodeN(info.Serial);
if (debug)
{
fmt::print("\n{:04d}-{:07d}\n", channelid, serial);
}
pKey = fmt::format("{:04d}{:07d}", channelid, serial);
}
else
{
info.Serial = (info.Serial * TEN) + GenerateMod7(info.Serial);
DWORD32 channelid = EncodeN(info.ChannelID), serial = EncodeN(info.Serial);
if (debug)
{
fmt::print("\n{:03d}-{:07d}\n", channelid, serial);
}
pKey = fmt::format("{:03d}{:07d}", channelid, serial);
}
return true;
}
/**
* Valid serial types are:
*
* C = Channel/Site ID (001 - 998)
* E = Office Channel ID (+1) Check Digit
* N = Serial
* K = Mod7 Check Digit
*
* -- OEM Specific
* D = 3 Digit day (001 - 366)
* Y = 2 Digit year
* O = OEM ID - typically seen as a channel ID + the first digit of the serial + mod7 check digit
*
* note that the N segment for OEM serials do not have a Mod7 check
*
* CCC-NNNNNNK
* CCCE-NNNNNNK
* DDDYY-ZZOOONK-NNNNN
* DDDYY-OEM-ZZOOONK-NNNNN
*
* we can determine what type of key we have
* simply by counting the numeric characters
*
* @param pKey
* @return
*/
BOOL PIDGEN2::Validate(const std::string &pKey)
{
std::string filtered;
ValidateKeyString(pKey, filtered);
bool bIsValidChannelID, bIsValidSerial, bIsValidOEMDay, bIsValidOEMYear, bIsValidOEMID;
switch (filtered.length())
{
case KeySize::FPP:
// standard FPP/CCP has 10 digits
info.ChannelID = IntegerS(filtered.substr(0, 3));
info.Serial = IntegerS(filtered.substr(3, 7));
bIsValidChannelID = isValidChannelID();
bIsValidSerial = isValidSerial();
if (debug)
{
fmt::print("\n\nisValidChannelID: {} isValidSerial: {}\n", bIsValidChannelID, bIsValidSerial);
}
return bIsValidChannelID && bIsValidSerial;
case KeySize::Office:
// so far only office 97 has been documented using this
info.isOffice = true;
info.ChannelID = IntegerS(filtered.substr(0, 4));
info.Serial = IntegerS(filtered.substr(4, 7));
bIsValidChannelID = isValidChannelID();
bIsValidSerial = isValidSerial();
if (debug)
{
fmt::print("\n\nisValidChannelID: {} isValidSerial: {}\n", bIsValidChannelID, bIsValidSerial);
}
return bIsValidChannelID && bIsValidSerial;
case KeySize::OEM:
// all OEM keys follow this pattern
info.isOEM = true;
info.Day = IntegerS(filtered.substr(0, 3));
info.Year = IntegerS(filtered.substr(3, 2));
info.OEMID = IntegerS(filtered.substr(5, 7)); // 6 + check digit
info.Serial = IntegerS(filtered.substr(12, 5));
bIsValidOEMDay = isValidOEMDay();
bIsValidOEMYear = isValidOEMYear();
bIsValidOEMID = isValidOEMID();
if (debug)
{
fmt::print("\n\nisValidOEMDay: {} isValidOEMYear: {} isValidOEMID: {}\n", bIsValidOEMDay, bIsValidOEMYear,
bIsValidOEMID);
}
return bIsValidOEMDay && bIsValidOEMYear && bIsValidOEMID;
default:
return false;
}
}
/**
*
* @param pKey
* @return
*/
std::string PIDGEN2::StringifyKey(const std::string &pKey)
{
switch (pKey.length())
{
case KeySize::FPP:
return fmt::format("{}-{}", pKey.substr(0, 3), pKey.substr(3, 7));
case KeySize::Office:
return fmt::format("{}-{}", pKey.substr(0, 4), pKey.substr(4, 7));
case KeySize::OEM:
return fmt::format("{}-OEM-{}-{}", pKey.substr(0, 5), pKey.substr(5, 7), pKey.substr(12, 5));
default:
return "";
}
}
/**
*
* @return
*/
std::string PIDGEN2::StringifyProductID()
{
if (info.isOEM)
{
return fmt::format("{:d}{:d}-OEM-{:d}-{:d}", info.Year, info.Day, info.OEMID, info.Serial);
}
return fmt::format("{}-{}", info.ChannelID, info.Serial);
}
/**
*
* @param in_key
* @param out_key
* @return
*/
BOOL INLINE PIDGEN2::ValidateKeyString(const std::string &in_key, std::string &out_key)
{
std::copy_if(in_key.begin(), in_key.end(), std::back_inserter(out_key), [](char c) { return std::isdigit(c); });
return out_key.length() == KeySize::FPP || out_key.length() == KeySize::Office || out_key.length() == KeySize::OEM;
}
/**
* Is the Serial with check digit a valid serial?
*
* standard Mod7 Check
*
* @return validity
*/
BOOL PIDGEN2::isValidSerial()
{
return isValidMod7(info.Serial);
}
/**
* Is the OEMID a valid?
*
* @return validity
*/
BOOL PIDGEN2::isValidOEMID()
{
if (info.OEMID.IsZero())
{
return false;
}
return isValidMod7(info.OEMID);
}
/**
* Is the Channel ID a valid Channel ID?
* also validates Channel ID check digit if applicable
*
* Known invalid Channel IDs are:
* 333, 444, 555, 666, 777, 888, 999
*
* @return validity
*/
BOOL PIDGEN2::isValidChannelID() const
{
// if we're office, do the last digit +1 checksum
if (info.isOffice)
{
Integer CheckDigit = (info.ChannelID % TEN), ChannelID = (info.ChannelID / TEN);
if (std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(ChannelID)) !=
channelIDDisallowList.end())
{
return false;
}
return (ChannelID % TEN) + 1 == CheckDigit;
}
// otherwise just make sure we're not in the disallow list
return std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(info.ChannelID)) ==
channelIDDisallowList.end();
}
/**
* Is the OEM year in the allow list?
*
* Known allowed years are:
* 95, 96, 97, 98, 99, 00, 01, 02
*
* @return validity
*/
BOOL PIDGEN2::isValidOEMYear() const
{
auto year = fmt::format("{:02d}", info.Year.ConvertToLong());
return std::find(validYears.begin(), validYears.end(), year) != validYears.end();
}
/**
* Is the OEM Day an allowed day?
*
* Allowed days are 1 - 366 inclusive
*
* @return validity
*/
BOOL PIDGEN2::isValidOEMDay() const
{
return info.Day >= 0 && info.Day <= 366;
}