Tuxera NTFS is a performance optimized, fail-safe, fully compatible NTFS file system driver. It ships for example in smart TVs, set-top boxes, smartphones, tablets, routers, NAS and other devices. It is available for Android and other Linux platforms, QNX, WinCE Series 40, Nucleus RTOS and VxWorks. Supported architectures are ARM architecture, MIPS architecture, PowerPC, SuperH and x86.
From Wikipedia
There are 2 types of keys (also called Product Key
in Tuxera NTFS) that can activate Tuxera NTFS, which are:
Key Type | Key Length | Format |
---|---|---|
Long Product Key | 34 chars | xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx |
Short Product Key | 23 chars | xxxxx-xxxxx-xxxxx-xxxxx |
The x
in Format
column represents a character in encoding table. This encoding table is:
// defined in CustomBase32Encode function, helper.c file.
const char SubstitutionTable[33] = "0123456789ACDEFGHJKLMNPQRTUVWXYZ";
where null-terminator \0
is not contained.
Tuxera NTFS uses ECC (Elliptic-curve Cryptography) to generate long product keys.
The curve it uses is secp112r1
of which the equation is
whose order is
Tuxera NTFS has stored its official public key in the following files:
Path |
---|
/Library/PreferencePanes/Tuxera\ NTFS.prefPane/Contents/MacOS/Tuxera\ NTFS |
/Library/PreferencePanes/Tuxera\ NTFS.prefPane/Contents/Resources/WriteActivationData |
/Library/PreferencePanes/Tuxera\ NTFS.prefPane/Contents/Resources/WriteActivationDataTiger |
/Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.4/ntfsck |
/Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.4/tuxera_ntfs |
/Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.5/ntfsck |
/Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.5/tuxera_ntfs |
The public key is
So far I don't know what the corresponding private key is. If you know, please tell me and I will be appreciated with your generous. So we must make a patch to those files which is to replace the official public key with our own public key.
The following is how long product key is generated:
Prepare a buffer uint8_t bin_rG[2][14]
. Save big number and to bin_rG[0]
and bin_rG[1]
respectively with big-endian. If big number is not 14-bytes-long, pad zero byte at Most Significant Bit.
Prepare a buffer uint8_t Hash[5]
. Calculate the hash of bin_rG
with argon2_hash
function.
argon2_hash(1,
1 << 16,
1,
bin_rG,
sizeof(bin_rG),
salt,
sizeof(salt),
Hash,
sizeof(Hash),
NULL,
0,
Argon2_d,
ARGON2_VERSION_13);
where salt
is
const uint8_t salt[16] = {
0xa1, 0x38, 0x11, 0x98, 0x12, 0x2f, 0x28, 0xee,
0x2c, 0x3a, 0xa0, 0x57, 0xbd, 0xcf, 0x2d, 0x83
};
Then clear the lower two bits of Hash[4]
. In other words, execute Hash[4] &= 0xFC;
.
Prepare a buffer uint8_t bin_s[14]
. Calculate
and save to bin_s
with big-endian. If big number is not 14-bytes-long, pad zero byte at Most Significant Bit.
Join bin_s
(14 bytes) and Hash
(5 bytes) and you will get uint8_t key_data[14 + 5]
where the first 14 bytes are bin_s
.
Encode key_data
by custom Base32 and you will get prekey_str
that has 31 chars. The differences between custom Base32 and standard Base32 are:
The alphabet table in custom Base32 is 0123456789ACDEFGHJKLMNPQRTUVWXYZ
while ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
in standard Base32.
When a 5-bits-long unit crosses over a byte, swap bits in the byte and bits in the next byte.
Example:
If 2-bytes-long data to encode is 10111010
11110100
, the units to encode are 10111
11010
11010
00000
in custom Base32 while 10111
01011
11010
00000
in standard Base32. That means 010
and 11
in unit 01011
in standard Base32 are swapped because 01011
crossed those two bytes.
No =
padding in custom Base32.
The last character in prekey_str
must be '0'
because Hash[4]
was &-masked by 0xFC
. Remove the last character and the length of prekey_str
becomes 30.
Reverse prekey_str
. And divide 30 chars in prekey_str
to five 6-chars-long blocks. Join them with hyphen('-'
) and you will get long product key.
Short product key is generated by 20 integers of which each is 5-bits-long. In order to explain it easily, I use uint8_t data[20]
to represent these 20 integers.
There are two types of short product key. They have different format:
Key Type | 1 | 2 |
---|---|---|
data[0] | x0x0x |
xxx10 |
data[1] | 10xx0 |
1xx1x |
data[2] | x10xx |
0xx1x |
data[3] | 0xxx0 |
|
data[4] | xx01x |
11xxx |
data[5] | xxx10 |
1xx1x |
data[6] | xx10x |
xx1x1 |
data[7] | xx0x1 |
xx01x |
data[8] | x1xxx |
x00xx |
data[9] | 1x1xx |
xx1x1 |
data[10] | xxxx1 |
1x1xx |
data[11] | xx11x |
x0xx0 |
data[12] | x01xx |
|
data[13] | x0xx1 |
x0xx0 |
data[14] | x00xx |
00xxx |
data[15] | 10xxx |
1xx0x |
data[16] | xx0x1 |
x0x1x |
data[17] | 101xx |
xxx01 |
data[18] | xxx0x |
x11xx |
data[19] | 10xx1 |
x10xx |
The x
in the last two columns means that the corresponding bit can be bit 1
or bit 0
. Other bits must be as shown in the table.
data[12]
in key type 1 and data[3]
in key type 2 are checksum. They must be the value of the sum of other 19 integers in module 32
.
Substitute uint8_t data[20]
according to the following table
const char SubstitutionTable[33] = "0123456789ACDEFGHJKLMNPQRTUVWXYZ";
then you will get you will get prekey_str
. Reverse prekey_str
and divide 20 chars in prekey_str
to four 5-chars-long blocks. Join them with hyphen('-'
) and you will get short product key.
However a short product key that is randomly generated cannot activate Tuxera NTFS. Because Tuxera has hashed all of valid short product keys and stored their hash values in his binary release. A short product key is valid if and only if the hash value of this short product key matches one of hash values that are stored in his binary release.
It seems that they have 150388 valid short product keys. I have extracted their hash values from /Library/PreferencePanes/Tuxera\ NTFS.prefPane/Contents/MacOS/Tuxera\ NTFS
file. You can see them in code/key_hashes.c
.
The following is how the hash value of a short product key is calculated:
A short product key is 23 chars long. Pad \n\0
at the end and you will get 25 chars data. Let it be uint8_t buf[25]
.
Use argon2_hash
to hash buf
and you will get uint8_t Hash[6]
:
argon2_hash(1,
1 << 16,
1,
buf,
sizeof(buf),
salt,
sizeof(salt),
Hash,
sizeof(Hash),
NULL,
0,
Argon2_d,
ARGON2_VERSION_13);
where salt
is
const uint8_t salt[16] = {
0x06, 0x9c, 0x5e, 0xf4, 0x90, 0x67, 0x39, 0x4c,
0x7b, 0x61, 0xa7, 0xee, 0x36, 0x97, 0xc6, 0x02
};
uint8_t Hash[6]
is the hash value of a short product key.
Decode long product key to 19-bytes-long key_data
;
Convert the first 14 bytes and the latter 5 bytes to big number and with big endian.
Use argon2_hash
to hash bin_R
and you will get uint8_t Hash[5]
. Don't forget that the lower two bits of Hash[4]
must be cleared.
Check if Hash
is equal to the latter 5 bytes in key_data
. If true the long product key is valid, otherwise invalid.
Why? Because if the long product key is valid, we must have
So that Hash
shall be equal to the latter 5 bytes in key_data
. This is the prerequisite for a valid key. If not equal, the long product key must be an invalid key.
Decode short product key to uint8_t data[20]
.
Check if uint8_t data[20]
matches the format of key type 1 or key type 2. If none is matched, the short product key is invalid.
The following short product keys are banned explicitly:
N0P1N-0N267-Z9TWU-24CP1 // it may be development-used only
J0M1H-37VYL-YEVNK-VFVM5 // a widely used leaked key
9DTQN-166PM-XLUEY-VTCZF // a widely used leaked key
If match one of these keys, the short product key is invalid.
Calculate the hash value of the short product key and check if it match one of hash values that are stored in the binary release. If match, the short product key is valid. Otherwise invalid.
PLEASE MAKE SURE YOU HAVE openssl
and argon2
. You can install them by Homebrew.
$ brew install openssl
$ brew install argon2
To make patcher, in console:
$ cd code
$ make patcher
and you will get file TuxeraNTFS-patcher
.
To make keygen, in console:
$ cd code
$ make keygen
and you will get file TuxeraNTFS-keygen
.
To clean, in console:
$ cd code
$ make clean
Last Test Time: 2018-07-13
Last Test Version: 2018 (released 2018-01-25) Download from here:
Build patcher and keygen.
Use TuxeraNTFS-patcher
to make a patch for Tuxera NTFS. In console:
$ sudo ./TuxeraNTFS-patcher
Example:
$ sudo ./TuxeraNTFS-patcher
Password:
-----secp112r1 Private Key-----
Bin: 42 EE 5D 2C CD 53 0A 06 43 B9 9A 9E 29 B0
-----secp112r1 Public Key-----
Bin: X = C3 15 26 EC 75 DE AA 90 4C 70 7B 09 2B EC
Bin: Y = 68 49 70 AA 04 3D 9F B3 DF 42 63 3D 55 FF
Write private key to tuxera_key.bin successfully.
Patching...
Target file: /Library/PreferencePanes/Tuxera NTFS.prefPane/Contents/MacOS/Tuxera NTFS
Open file successfully!
File size: 3669616 byte(s).
Map file successfully!
offset = 0x000000000002ec2a, writing data.....Patch is done.
offset = 0x000000000014f05e, writing data.....Patch is done.
offset = 0x0000000000284c40, writing data.....Patch is done.
Modified: 3
Target file: /Library/PreferencePanes/Tuxera NTFS.prefPane/Contents/Resources/WriteActivationData
Open file successfully!
File size: 3180416 byte(s).
Map file successfully!
offset = 0x000000000002366e, writing data.....Patch is done.
offset = 0x000000000011eae6, writing data.....Patch is done.
offset = 0x0000000000226c74, writing data.....Patch is done.
Modified: 3
Target file: /Library/PreferencePanes/Tuxera NTFS.prefPane/Contents/Resources/WriteActivationDataTiger
Open file successfully!
File size: 2132524 byte(s).
Map file successfully!
offset = 0x0000000000023c6b, writing data.....Patch is done.
offset = 0x0000000000126c84, writing data.....Patch is done.
Modified: 2
Target file: /Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.4/ntfsck
Open file successfully!
File size: 3135728 byte(s).
Map file successfully!
offset = 0x0000000000099d07, writing data.....Patch is done.
offset = 0x000000000021bd4c, writing data.....Patch is done.
Modified: 2
Target file: /Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.4/tuxera_ntfs
Open file successfully!
File size: 3005576 byte(s).
Map file successfully!
offset = 0x000000000008a747, writing data.....Patch is done.
offset = 0x00000000001fc754, writing data.....Patch is done.
Modified: 2
Target file: /Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.5/ntfsck
Open file successfully!
File size: 6195032 byte(s).
Map file successfully!
offset = 0x0000000000098bc2, writing data.....Patch is done.
offset = 0x000000000021d890, writing data.....Patch is done.
offset = 0x0000000000379f0a, writing data.....Patch is done.
offset = 0x00000000005057a0, writing data.....Patch is done.
Modified: 4
Target file: /Library/Filesystems/tuxera_ntfs.fs/Contents/Resources/Support/10.5/tuxera_ntfs
Open file successfully!
File size: 5958616 byte(s).
Map file successfully!
offset = 0x0000000000089382, writing data.....Patch is done.
offset = 0x00000000001fe27c, writing data.....Patch is done.
offset = 0x000000000034ec42, writing data.....Patch is done.
offset = 0x00000000004cc178, writing data.....Patch is done.
Modified: 4
You will get tuxera_key.bin
file at current directory.
Re-codesign Tuxera NTFS. Because we made a patch to tuxera_ntfs.fs
and Tuxera NTFS.prefPane
, their original code signatures became invalid. So we have to re-codesign them. In console:
$ sudo codesign -f -s "your code-sign certificate name" /Library/Filesystems/tuxera_ntfs.fs
$ sudo codesign -f -s "your code-sign certificate name" /Library/PreferencePanes/Tuxera\ NTFS.prefPane
NOTICE: "your code-sign certificate name"
should be the name of your code-sign certificate, which is displayed in Keychain.app
.
Run TuxeraNTFS-keygen
to generate the product key. In console:
$ ./TuxeraNTFS-keygen ./tuxera_key.bin
Example:
$ ./TuxeraNTFS-keygen ./tuxera_key.bin
-----secp112r1 Private Key-----
Bin: 42 EE 5D 2C CD 53 0A 06 43 B9 9A 9E 29 B0
-----secp112r1 Public Key-----
Bin: X = C3 15 26 EC 75 DE AA 90 4C 70 7B 09 2B EC
Bin: Y = 68 49 70 AA 04 3D 9F B3 DF 42 63 3D 55 FF
Long product key: 4KPGHH-147M3Q-UHN3M2-C4DYAN-ENACL0
Now you can see product key. Just use it to activate your Tuxera NTFS.