[Sublime Text 3](https://sublimetext.com/3) is a text editor for Windows, OS X, and Linux. An evaluation version of ST3 is distributed freely, and can be unlocked by loading the contents of a license file purchased from Sublime HQ.
It is straightforward to modify the application so it accepts invalid license files by forcing the return value of the `license check` procedure to be `true`. However, this technique requires a new patch to be created for each platform and each new build of ST3. Adapting such patches by hand requires a little time and effort.
A better crack would work on all future builds of ST3 for all platforms*, and would not require the modification of any code in the application. This document explains how such a crack can be made.
*Assuming Jon Skinner doesn't read this and change the way ST3 works
### Anatomy of a ST3 license file
Example ST3 license files can be [found online](https://github.com/search?utf8=%E2%9C%93&q=%22begin+license%22+ea7e&type=Code&ref=searchresults). Licenses which are shared in this way are soon blacklisted and cannot be used in future builds of ST3. Nevertheless, these once-valid licenses are useful when investigating how license files work.
The structure of a license file is as follows:
-----BEGIN LICENSE-----
John Q. Public -- registrant's name
Single User License -- license type
EA7E-1234 -- license number
00112233445566778899AABBCCDDEEFF \
00112233445566778899AABBCCDDEEFF |
00112233445566778899AABBCCDDEEFF |
00112233445566778899AABBCCDDEEFF | license signature
00112233445566778899AABBCCDDEEFF |
00112233445566778899AABBCCDDEEFF |
00112233445566778899AABBCCDDEEFF |
00112233445566778899AABBCCDDEEFF /
-----END LICENSE-----
The first three lines are the license data — the information which gets shown in the application's _About_ dialog and the ID number of the license (used for blacklisting). The next eight lines of the license are a hex-encoded signature over this data to confirm its authenticity and integrity.
Disassembly reveals that ST3 is statically linked to [Crypto++](http://www.cryptopp.com/) and uses the `RSASSA-PKCS1-v1_5` scheme from PKCS #1 for this signature, with SHA-1 as the hash function.
When a customer purchases a license, Sublime HQ sign the hash of the license data using their private key. The application contains the corresponding public key against which the signature can be verified. This public key can be extracted from memory using a debugger:
30819D300D06092A864886F70D010101050003818B0030818702818100D87BA2
4562F7C5D14A0CFB12B9740C195C6BDC7E6D6EC92BAC0EB29D59E1D9AE67890C
2B88C3ABDCAFFE7D4A33DCC1BFBE531A251CEF0C923F06BE79B2328559ACFEE9
86D5E15E4D1766EA56C4E10657FA74DB0977C3FB7582B78CD47BB2C7F9B252B4
A9463D15F6AE6EE9237D54C5481BF3E0B09920190BCFB31E5BE509C33B020111
This DER-encoded ASN.1 structure can then be parsed, and the RSA parameters `n` and `e` extracted:
Public-Key: (1024 bit)
Modulus:
00:d8:7b:a2:45:62:f7:c5:d1:4a:0c:fb:12:b9:74:
0c:19:5c:6b:dc:7e:6d:6e:c9:2b:ac:0e:b2:9d:59:
e1:d9:ae:67:89:0c:2b:88:c3:ab:dc:af:fe:7d:4a:
33:dc:c1:bf:be:53:1a:25:1c:ef:0c:92:3f:06:be:
79:b2:32:85:59:ac:fe:e9:86:d5:e1:5e:4d:17:66:
ea:56:c4:e1:06:57:fa:74:db:09:77:c3:fb:75:82:
b7:8c:d4:7b:b2:c7:f9:b2:52:b4:a9:46:3d:15:f6:
ae:6e:e9:23:7d:54:c5:48:1b:f3:e0:b0:99:20:19:
0b:cf:b3:1e:5b:e5:09:c3:3b
Exponent: 17 (0x11)
We can confirm that this key is correct using one of the genuine license files we found online earlier:
# Convert the public key to PEM format
echo 30819d30[...]3b020111 | xxd -r -p | openssl rsa -pubin -inform DER -out pub
# Hex-decode the signature block from the found license file
echo 813A03DD[...]0E9C8D55 | xxd -r -p > sig
# Verify license data of found license file
echo -en "Fred Smith\nSingle User License\nEA7E-987654" | openssl dgst -sha1 -verify pub -signature sig
# "Verified OK"
If we were able to replace the public key in ST3 with one to which we possess the corresponding private key, we would be able to sign our own license files which would pass verification.
In ST2, the public key was stored in plain text and could easily be located and modified. Unfortunately this is no longer the case in ST3.
### Locating the public key on disk
Disassembly reveals that ST3 obfuscates the public key on disk with a single-byte XOR cipher. The XOR key used varies between different builds and platforms:
Windows OS X Linux
Build 3059 C5h 26h 4Bh
Build 3065 19h FAh 97h
Build 3080 E7h 04h 69h
Build 3083 76h 95h F8h
Because future builds will likely use different keys still, we need a method which can locate the public key, irrespective of the XOR key with which it is obfuscated, and overwrite it with one of our own (XORed in the same way as the original).
To locate the XORed public key in the binary, we can take advantage of the observation that `(x^k) ^ (y^k) == x ^ y`. Instead of searching for specific bytes in the binary, we search for specific differences between adjacent bytes, causing the unknown XOR key to be cancelled out.
The XOR key used can then be recovered trivially, and used to obfuscate our replacement public key.
### Defeating the public key checks
Unfortunately ST3 contains additional code to verify that the public key has not been tampered with in this way. These checks make it slightly more difficult to replace the public key than in ST2.
ST3 takes the DER representation of the public key embedded in the binary and calculates its MD5 hash:
b5 5b 86 64 a4 84 8b 9e 71 5d 3f 55 ec 09 31 aa
^^ ^^ ^^ ^^
A B C D
Specific digits of this hash are then checked against hardcoded values. The digits which are checked again depend on the platform and build:
Windows OS X Linux
Build 3059 B C,D (none)
Build 3065 B C,D (none)
Build 3080 (none) A B
Build 3083 C,D B B
These checks, like the `license check` procedure itself, can be defeated trivially by modifying the code of the binary. However, to keep such patches up-to-date would again require manual intervention or some form of automated pattern-matching.
The less intrusive way to defeat these checks is to generate an RSA key pair whose MD5 hash coincides with the real key pair's hash in all of the positions A, B, C and D. About one in every 256^4 key pairs satisfies this property. This is low enough that we can find one by brute force.
The bottleneck in the naive implementation of this strategy is the generation of RSA key pairs — specifically the Miller-Rabin primality test, which is many times slower than calculating an MD5 hash.
Significant performance gains can be achieved by deferring the full primality test until _after_ a value with a suitable MD5 hash has been found, instead using trial division by a small number of primes to skip obviously composite values of _n_.
My implementation found a colliding key pair after about 48 hours on EC2:
Public-Key: (1024 bit)
Modulus:
00:83:a6:f0:cc:89:e6:78:44:df:12:2a:f5:ef:0f:
d4:3d:69:41:e4:0a:16:3f:cf:a8:28:9f:81:a7:6f:
09:c8:29:4b:e5:ae:44:e5:9e:20:72:b4:92:22:d1:
cd:30:3d:35:34:7b:19:23:2f:b5:2c:79:5f:f6:ad:
92:bc:c1:b3:9c:1e:cf:fa:a4:9a:f1:6a:3b:2e:ae:
44:49:04:98:77:57:a9:43:40:05:90:30:9e:cd:d7:
82:88:62:65:56:2d:c8:0c:02:64:a1:65:f5:ad:19:
33:c3:e7:19:f8:ff:19:81:b7:82:9f:cb:2a:ca:18:
44:31:c7:06:ae:c5:07:f2:0b
Exponent: 17 (0x11)
With corresponding private key exponent _d_:
5cee4f9f7066730370490f44304768a3d1d4287f973c1a1c58e91039f406e7a4
ae0b8a1283f725f69d9454d0549a858ef7de6c18d661c50a61db4d58853d6fb8
7620899760a1a242266a198a07632422d26cabd8b3dbd7a3cf9c7ea3289f974e
926a69852a9c2f70f53c4f997c34f5f9e316e2148bfdfd67395827b01465ef91
The DER-encoded ASN.1, when hashed, passes the checks on positions A, B, C and D as required:
Original: b5 5b 86 64 a4 84 8b 9e 71 5d 3f 55 ec 09 31 aa
Collided: b5 5b 06 55 06 15 d5 b2 e8 54 b5 0d 40 73 31 aa
^^ ^^ ^^ ^^
A B C D
### Generating keys
We can now sign our own keys trivially:
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA
n = 0x83a6[...]f20bL
d = 0x5cee[...]ef91L
e = 17L
msg = 'Your name here\nUnlimited User License\nEA7E-555555'
digest = SHA.new(msg)
rsa = RSA.construct((n, e, d))
signer = PKCS1_v1_5.new(rsa)
sig_hex = signer.sign(digest).encode('hex').upper()
sig_block = '\n'.join([sig_hex[start:start+32] for start in range(0, 256, 32)])
print '----- BEGIN LICENSE -----'
print
print msg
print sig_block
print
print '----- END LICENSE -----'
And these keys will be accepted by our modified version of ST3:

### Source code
- [Public key patcher (C)](/sublime-patcher.c)
- [Keygen (Python)](/sublime-kg.py)