Dev:Tweak DRM

Building tweak DRM means building technical measures to control access to using a tweak you made, especially to prevent access by people who were supposed to pay for it but didn't, aka pirates. (DRM stands for digital rights management.)

Making DRM-related mistakes is a reliable way to get a thread at the top of /r/jailbreak with upset commenters saying that they won't buy anything from you again. Here is advice to help you avoid that situation. See "advice for dealing with piracy" on the advice for new developers page for other bits of useful information.

Philosophical advice
Many developers choose to skip working on DRM for their paid tweaks and instead spend that time on making a great piece of software with lots of marketing and good support. Almost all DRM systems get cracked; many people who crack software have more free time than you do.

To read a wide variety of perspectives from users and developers about the pros and cons of DRM, check out [http://www.reddit.com/r/jailbreak/comments/2tdxnc/this_communitys_thoughts_on_drm/ "This community's thoughts on DRM?" from /r/jailbreak].

If you choose to implement DRM, you need to build it in a way that preserves access for legitimate users even if your DRM system fails. If your server goes down (and it'll inevitably go down), or if other mistakes happen, that must not prevent access to your product for people who have purchased it. saurik is willing to block purchases of Cydia Store products with faulty DRM, and the default repositories (especially BigBoss) also don't consider faulty DRM acceptable.

Some advice from saurik:

"DRM...increases the risk of your product receiving complaints, complaints that will translate into your vendor account no longer being trusted and your products being blocked from sale. This instead must be viewed as a tradeoff that you must consider."

"I do not consider hard DRM checks that happen during package scripts acceptable. For various reasons (related to how other packages that modify things related to network access are implemented) it cannot be assumed to be the case that Cydia still has working Internet access during the package installation phase (all packages are downloaded before this phase begins, which then happens as a semi-atomic unit)."

"However, the entire idea of having a DRM system that doesn't let the user use the product they just purchased unless your server is working at that very first instant is fundamentally flawed: even simple networking glitches or inevitable hardware failures lead to situations where users can't enable their purchases. You thereby really need some kind of "leniency" or "grace period" in your implementation to be acceptable."

"One common way of getting most of the way there is simply to implement a trial period for the product: the trial period tends to act as a buffer, decreasing the number of people who experience the failure (as most people who ever purchase at all actually purchase quite quickly and do not wait for the product to entirely expire). A more epic implementation involves a model where the system gives the user a few hours of grace."

Practical advice


It's reasonable to make pirated versions display a friendly message (as a pop-up, as a banner in settings, or similar) that explains that (1) it's a pirated tweak, and (2) please uninstall this pirated version and purchase the legitimate version from the authorized repository, with a link to the proper package page. If this message is polite and not very annoying, tweak crackers might not bother to patch it out.

It's fair to make pirated versions of your tweak not work, but keep in mind that if you do this without explaining why, this may confuse people into thinking the tweak doesn't work at all, even for paid users. (Some people do use piracy to "try before they buy".) Also, tweak crackers like to find ways to bypass this so that the pirated tweak will work.

Keep in mind that some people pirate without realizing that they're using pirated versions - for example, their friend suggested that they add a shady repository, and they ignored the pirate warning on the repository. This is part of why it's helpful to include a clear message explaining that it's pirated.

Not recommended
DRM shouldn't act like malware. Some of the things you shouldn't do (and also shouldn't scare people by claiming or threatening to do them):


 * Don't take action without permission from users, even if the person has pirated your package - for example, don't modify people's /etc/hosts files or send tweets without permission. This kind of negative surprise makes people angry and reduces their trust in you (both pirates and legitimate users who have already purchased your package or were considering purchasing it), which doesn't make them want to support you by purchasing your package.
 * Don't do annoying things to the device that frustrate people, such as making the screen black after they install a pirated package, even if that's solvable by going into safe mode and uninstalling the package. This makes people scared and irritated - they'll be angry at you instead of wanting to support your work by purchasing the package.
 * Don't cause unrelated problems that make people think there's something wrong with their jailbreak, such as making SpringBoard randomly crash once in a while - pirates are just going to think that they need to restore and go back to normal iOS, instead of realizing that installing your specific package caused their problem. You want to encourage them to purchase your package, not make them think jailbroken devices are unreliable.
 * Don't cause harm to files or the operating system (such as damaging system files, intentionally causing bootloops, etc.) - this is bad and will get your package purchase-blocked by saurik and/or removed from default repositories.

Also, please don't make life difficult for other tweak developers who may need to coordinate their work with your work - for example, don't disable debuggers or other tools developers use.

Technical advice
You can add some advice here about writing good DRM!

Cydia Store Integration and crack prevention might be helpful articles.

You can also ask experienced developers for advice on this. One suggestion:

I've used a few different lightweight forms of DRM that don't interfere with User Experience at all, most of which have been unsuccessful (only delaying the inevitable). But there are a few schemes that can be successfully implemented (and so far moderately unbeaten) with appropriate use cases of the tweak. Message me on twitter (@apocolipse269) or IRC (apocolipse on ircsaurik, freenode, chronic-dev, etc) and I can offer some advise. -Apocolipse

Here's a thread on /r/jailbreak with some examples from developers.

File Check
This type of protection uses NSFileManager to check for a file usually unique to the system when your package is installed on a device. A protection like this is usually not the strongest against crackers as the only thing they would need to do is change your path as seen in the binary.

/var/lib/dpkg/info/com.example.packagename.list    -->    /var/lib/dpkg/info///////////////////cydia.list

On the left is an classic example seen in countless binaries to protect the tweak. The one on the right is how a cracker would change the string, making the tweak check for Cydia rather than checking the files you had given it to check. Now an easy way to protect this file path from getting changed is a little term called obfuscation, in which you hide the string in the binary making it not editable. This type of protection even with obfuscation is still a bit too easy for a cracker to crack as the cracker could eventually find the hidden string. if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/lib/dpkg/info/com.example.packagename.list"]) { //Do if the file exists } else { //Do if file doesn't exist }

DPKG Status Check
This is bit of a tricky process, since as of iOS 8 the system function was deprecated by Apple. dpkg has a simple function where it will tell you the status of the package.

Package: dpkg Status: install ok installed Priority: required Section: Packaging Installed-Size: 828 Maintainer: Jay Freeman (saurik)  Architecture: iphoneos-arm Version: 1.14.25-9 Depends: bash, bzip2, coreutils-bin, diffutils, findutils, gzip, lzma, ncurses, tar Description: package maintainance tools from Debian Name: Debian Packager Homepage: http://wiki.debian.org/Teams/Dpkg

This is the status you get for dpkg itself. There are many ways you can implement this in your tweak - some developers end up directly going to the path of the status file and searching that, but as you would have already guessed, it could be cracked by simply changing the path of the file. Another method is to use a call like system to run a bash command and getting its response, and than comparing with the string. This can also be cracked as you can't really encrypt a bash command string, so someone can change "dpkg -s packageName" to whatever bash command "craK -s packageName", which would basically run the made up bash of a person cracking it. The bash command for this is: dpkg -s packageName

Server Checks
This can be a more effective protection. You'll need to find a way to get the device's UDID from the device and to the server where the UDID will be checked with Cydia. Here is the php code that Cydia has provided to developers: <?php $vendor = "vendorID"; $secret_key = "secretKey"; $udid = $_GET['UDID']); //the udid sent from device   function urlsafe_b64encode($string) {        return str_replace(array('+','/','='), array('-','_',''), base64_encode($string));    }    function cydia_($url, $dict, $key) {        ksort($dict);        $query = http_build_query($dict);        $response = file_get_contents("$url?$query&signature=". urlsafe_b64encode(hash_hmac("sha1", $query, $key, true)));       $values = array;        parse_str($response, $values);        return $values;    }    function cydia_check($vendor, $package, $version, $device, $host, $mode, $key) {        return cydia_("http://cydia.saurik.com/api/check", array('device' => $device, 'package' => $package, 'vendor' => $vendor, 'version' => $version, 'mode' => $mode, 'host' => $host, 'nonce' => uniqid, 'timestamp' => time), $key);    }    if (!isset($udid) {       header("Status: 403 Unique device identifier not provided"); }   else {       $response = cydia_check($vendor, 'com.example.packageName', 'PackageVersion', $udid, $_SERVER['REMOTE_ADDR'], 'local', $secret_key); //make sure this is correct if (!isset($response['state']) or $response['state'] != 'completed') {           echo "UDID has not purchased such package and this string will be returned"; }       else {           echo "UDID has purchased this and this string will be returned"; }   }   ?>

You can encrypt an unique string sent to the device and then check with the device to see if the string is what you had assumed it would be. Make sure not to make it too easy for someone cracking it to see what the string is you're checking with, or the response from the server is encrypted and only accessible by your tweak using .htaccess.

NSError* error = nil; NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.example.com/server/protection.php?1=%@", UDID]] options:NSDataReadingUncached error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]);//device not connected to wifi, usually... } else { NSLog(@"Data has loaded successfully.");//data returned with your response from server, this is where you will be checking the return value }

It's always good to encrypt the URL string so it can't be edited by crackers even if they find out your returning response.