By WitherOrNot
In the interest of convenience for those who would like to do further research into or implementation of the Confirmation ID system, a summary of the mathematics involved in hyperelliptic curve cryptography is provided below.
If you are not interested in the mathematics, please skip to Validation Mechanism.
An imaginary hyperelliptic curve
where
For this write-up, we will be focusing on the curves used by the Confirmation ID system which are genus 2 curves where
Those familiar with the related concept of elliptic curves may assume points on this curve follow a similar group law. However, the group law for hyperelliptic curves is very different.
As a refresher, for points on elliptic curves, a form of addition can be done by defining the sum of 2 points to be the reflection of their third collinear point over the y-axis.
This works because any line on the projective plane (a plane with a defined point at infinity) that passes through 2 points on this curve will intersect exactly at one other point, even if the two points are identical (we define the point at infinity to be on the curve).
As an exercise for the reader, see how the same property fails to hold on the following hyperelliptic curve.
Therefore, a different group representation will be needed for hyperelliptic curves.
We define the divisor
"Formal" means that there is no actual definition of summation or scalar multiplication on points, it simply denotes the behavior we want divisors to have.
The only operation that is defined on individual points is negation, which for a point
Divisors, however, can be added and multiplied by scalars according to their definition. This allows the construction of an abelian (commutative) additive group, but this group is too large to be cryptographically useful. Therefore, we will need to consider a subset of divisors with useful properties.
Semi-reduced divisors are divisors where for all
Reduced divisors are semi-reduced divisors where the sum of all coefficients in
The reduction algorithm is as follows:
- Let
$v(x)$ be a polynomial such that for all$P_i=(x_i,y_i)$ in$D$ ,$y_i=v(x_i)$ - Find all points
$Q_i=(x_i,y_i)$ on the curve such that$q(x)=F(x)-v(x)^2=0$ for all$x_i$ .
- If the coefficient
$c_i$ of$P_i$ is greater than 1 in$D$ ,$q$ must have a root of multiplicity$c_i$ at$x_i$ .
- From the set of points
$Q_i$ , remove all points$P_i$ . - Let
$E=-\sum Q_i$ . - If the number of points in
$E$ is less than or equal to$g$ , then stop, the reduction of$D$ is$E$ . Otherwise, let$D=E$ and repeat from step 1.
Reduction by this method is analogous to the modulo operation for integers, in that it maps the group of all divisors to a cyclic group known as the Jacobian
Some useful observations can be made about the reduction procedure. Notice first that it is necessary for the polynomial
Next, it can be seen that after step 3, the x-coordinates
Note that in many cases,
The Mumford representation of a semi-reduced divisor
With this, the divisor can now be re-defined as
From the previous observations, it can be seen that
Reduction algorithm for
- Let
$u' = \frac{F-v^2}{u}$ - Let
$v' = -v \bmod u'$ - If
$\mathrm{deg}(u') \leq g$ , then stop.$E = \left\langle u', v' \right\rangle$ is the reduction of$D$ . Otherwise, let$u=u'$ and$v=v'$ and start from step 1.
With the Mumford representation and reduction defined, we are ready to state the central problem of hyperelliptic curve cryptography.
Let the scalar multiplication of a divisor
Then the cryptographic problem for hyperelliptic curve cryptography is to find an integer
given that
for some known integer
To compute
When this value is known,
Computation of
The confirmation ID is derived directly from a value known as the Installation ID. This value is displayed to the user when selecting the "Telephone Activation" option when activating a product, as shown below.
As seen in the screenshot, the confirmation ID is broken into 6-digit groups. This is related to the first validation step, known as the checksum.
Within each 6-digit group
For instance, from the screenshot above, the checksum digit for the group
Once the checksums are validated for each digit group, the checksum digits are removed and the remaining digits are concatenated to form an integer that we will refer to as
with
Next, the divisor
Next, the divisor
Expressing
And finally, we compute the value
Note: the following information applies to MS Plus! DME, Office XP, and Windows. Office 03 and 07 use a much more complex encoding scheme that has not been fully reverse engineered as of writing.
The last step is to decrypt the data in
The installation ID is first validated and has its checksum digits removed to form an integer, just like with the confirmation ID. Then, it is converted to a little endian byte representation and decrypted using a 4-byte key.
The decryption algorithm uses a variant of a Feistel cipher. Its pseudocode is provided below:
function decrypt_feistel(data, key):
size_half := data.length / 2
size_half_dwords := size_half - (size_half % 4) // Round size_half down to multiple of 4
last_byte := data[2 * size_half:] // For odd lengths of data, this contains the last byte, otherwise empty
data = data.slice[:2 * size_half]
repeat 4 times:
first_half := data[:size_half]
second_half := data[size_half:]
hash := SHA1(first_half + key)
hash = hash[:size_half_dwords] + hash[4 + size_half_dwords - (size_half % 4):4 + size_half_dwords]
data = (second_half ^ hash) + first
return data + last_byte
The decrypted IID data is either 17 or 19 bytes, with the following structure:
struct DecryptedIID {
byte hwid[8];
// The following values correspond to the product code
// rpc-chid-seq-last
uint last : 17;
uint version : 3; // 4 for XP RTM, 5 for XP SP1+
uint seq : 24;
uint chid : 10;
uint rpc : 9;
ushort key_hash; // Only in SP1+, contains 12 bits of product key hash
}
The IID data is then rearranged into a 16-byte key like so:
key = hwid + bytes_from_int((chid << 58 | rpc << 41 | seq << 17 | last) & BITMASK(64))
Finally, the value
The structure of the decrypted CID data is as follows:
struct DecryptedCID {
byte key_hash[6]; // Genuine CIDs have bytes of the product key hash stored here, but they're usually not checked
bool check_hash; // key_hash is checked only if this byte is true
byte attempt; // When generating CIDs, this byte is incremented for each generation attempt, maximum of 0x80 times
byte zero[6]; // Must be zeroes
};
Other software that is known to use this confirmation ID system includes Microsoft Plus! Digital Media Edition and Microsoft Office XP through 2007. The parameters for these products have already been extracted, but the extraction procedure will be documented here for completeness.
The parameters for confirmation ID generation are stored in licdll.dll
(Windows), MSO.DLL
(Office), or MPA.DLL
(MS Plus). These DLLs are obfuscated, and therefore will need to be deobfuscated using AntiWPA Generic by the AntiWPA Team.
- Download AntiWPA Generic 2.3. Username and password are
planet-dl.org
. - Extract the DLL.
- Under the Options menu, uncheck "Apply OOBE Fix" and "Apply WPA Fix".
- Check "Remove selfcheck blocks" and "Remove crypt blocks". The menu that comes up after "Remove selfcheck blocks" is not important, any option can be picked.
- Ensure that "Save decrypted code to .exe" is checked.
- Open the DLL and click "Apply / Browse"
- Open the decrypted DLL file in a disassembler, and search for the byte pattern "01 00 01 00" or the integer 0x10001.
- Find an instruction fitting the pattern
mov dword ptr [esi/ebp + ??h], 10001h
. - Decompile the surrounding function.
An example when run on Windows XP SP3's licdll.dll
with IDA is provided below.
int __stdcall sub_6107FAF4(int a1)
{
int v1; // edi
_DWORD *v2; // eax
v1 = 0;
*(_DWORD *)a1 = 2;
*(_DWORD *)(a1 + 4) = 2;
*(_DWORD *)(a1 + 8) = 4;
if ( !sub_610A064B(a1) )
return 14;
v2 = *(_DWORD **)(a1 + 24);
*(_DWORD *)(a1 + 12) = 65537;
*v2 = 0x6D7F2A79;
*(_DWORD *)(*(_DWORD *)(a1 + 24) + 4) = 0x16A6B03;
**(_DWORD **)(a1 + 28) = 0;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 4) = 0;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 8) = 0x36C85381;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 12) = 0x218401;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 16) = 0x83892AD0;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 20) = 0x44197B;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 24) = 0x322B3B04;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 28) = 0x1400606;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 32) = 0x322B3B04;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 36) = 0x1400606;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 40) = 1;
*(_DWORD *)(*(_DWORD *)(a1 + 28) + 44) = 0;
return v1;
}
The variable a1
is a pointer to a WPAHyperellipticParams
struct with the following layout:
// BigInts are stored as 2 DWORDs in little endian order
struct BigInt {
uint low;
uint high;
};
struct WPAHyperellipticParams {
uint genus; // Genus of the curve, almost always 2
uint modulus_size; // Size of prime modulus in DWORDs, almost always 2
uint unknown;
uint public_multiplier; // Value multiplied by initial divisor to verify CID
uint modulus[modulus_size]; // Prime modulus of curve
BigInt coefficients[6]; // Coefficients of F(x) in order of lowest to highest degree
};
Then, download the private key solver from here. Edit solve.py to include the parameters from the DLL, run Install.sh
, then run solve.py
. The solver will then output the private key for the curve, which can then be used to generate confirmation IDs.
- diamondggg, for writing and releasing the xp_activate32 source code
- Pierrick Gaudry, for creating NTLJac2, off which our modified Confirmation ID parameter solver is based
- david4599, for providing his modified version of NTLJac2 and assisting in Confirmation ID generation
- CONIGUERO, Neo-Desktop, TheTank20, for assisting in finding parameters for non-Windows products
- Microsoft ❤️