3
$\begingroup$

I need a way to generate temporary unlock codes for an application to use when it can't go online to confirm that its activation subscription is still current.

The application needs to able go offline for 30 days without needing to re-authorize, and this would just be a fallback option for an extended network outage/etc.

My first thought was to just store a timestamp for how long to remain unlocked combined with an RSA signature that the application could use to confirm the unlock came from us and wasn't something the user just made up.

This appears to meet our security needs if we embed the public key in the application (if a cracker modified the binary they could just remove the check entirely instead of installing their own key). For normal operation sending the data down as part of normal data transfers to/from the associated web application means that we could do this without any particular difficulty.

The problem I see is that since this needs to be usable if there's a network outage the unlock code needs to be short enough to send over a phone call or the equivalent. An RSA signature with a secure key length fails there.

$\endgroup$
  • $\begingroup$ I'm not sure if I've tagged this correctly. It fits my initial implementation idea, but not necessarily the general concept. But I suspect if I had a good name for the latter, I'd've been able to find something via Google already. $\endgroup$ – Dan Neely Jun 16 '17 at 14:18
  • $\begingroup$ Somewhat similar questions which might help (if not also answer your question): "Using a hash with a constant key to create easily verifiable codes" and/or "Implementing CD serial key system". Yet, in the end you're near to facing a white-box crypto problem if RSA and Elliptic Curve Crypto aren't an option for you (as you seem to indicate in your question). $\endgroup$ – e-sushi Jun 16 '17 at 20:31
  • $\begingroup$ Very important criterion - can the users just roll back the system time when the computer is off line (no NTP)? $\endgroup$ – Paul Uszak Jun 16 '17 at 20:38
  • $\begingroup$ @PaulUszak Since the question mentions if a cracker modified the binary they could just remove the check entirely instead of installing their own key and since setting back the system time is definitely an easier attack vector than walking the path of reverse engineering, I'm pretty confident that that can be assumed to be a problem too. (Wouldn't make much sense to exclude the easy things, while assuming the harder attack vectors.) $\endgroup$ – e-sushi Jun 16 '17 at 20:42
  • $\begingroup$ @PaulUszak Screwing with the clock like that would make a mess of any data the application generated and the reports it produced. Outside of a few fringe cases regular contact with the associated website is needed to populate data on the remote terminal and to generate more complex reports. The company we're developing this for is locking down the computers it runs on (to what extent they've never said beyond a desire for kiosk mode operation). If there's a way to do an X day offline lockout in software (special hardware's out due to cost) that's immune to clock spoofing I'd be interested. $\endgroup$ – Dan Neely Jun 16 '17 at 20:57
6
$\begingroup$

Use hash based cryptography.

First, define a one way function F. It takes as input a small (10-16 byte) string and uses a secure hash function (EG:SHA2) to produce another string of the same size.

We number each day from (Jan 1 2000) to (Jan 1 2100) with a number n (n=0 ... 36500)

Each day is assigned a code. If the user has the code for a particular day, they are allowed to use the software for all days leading up to and including that day. If we supply the code for day x we don't care if the user can find the code for day x-1 since the code for day x allows them to use the software on that day too. With that in mind, we do the following:

code(x)=f(code(x+1))

The codes form a chain. For each installation, you pick a random code for day 36500 then hash it repeatedly until you get the code for day 0. You then sign a message telling the application the day 0 code.

At that point, give out the code for a particular day to unlock the software up to that date.

Anyone can apply F to a code to get the code for the previous day. But they can't reverse F to get the code for the next day. Since you picked the code at the beginning of the chain you have all of them. This is the asymmetry that makes this system secure. Yes, asymetry ... this is public key cryptography.

To verify a code, the application applies F repeatedly until it reaches the day 0 code. If this does not happen within whatever the set length of the chain, the code isn't valid. Keep a counter during this process to get the code number. Enforcing this limit is not necessary for security it just times out the verification process if the user makes a mistake when entering the code.

Implementation

F can be truncated SHA2. The number of bits kept determines the security level. 80 bits is plenty.

You'd need a binary to text encoding to make this something humans can read and enter into your application.

My current machine can do ~1e6 SHA2 ops per second. That's 3000 years worth of unlock codes. Traversing the chain for generating codes on the server and verifying them on the client won't take much time.

Trusting the Clock

This relies on the application having access to the right time. Users can always change the system time so that's not something a license enforcement system can fix. If the application does something time sensitive (Like recording timestamped records) users might not want to run the software with the wrong time. I'd recommend keeping an internal saved time value which increases while the software is in use and can never run backwards. That's the best you can do. The user is always free to make a system image and restore from backup as many times as they like.

Some things you should already be doing.

I'm assuming you already have a per install ID saved somewhere. That's kind of a requirement for this. Each installed copy of the software has to have a unique ID, a code(0), and a stored signature of the (ID,code(0)) pair for this to work. This prevents the client from deploying lots of identical images of the same installation and using the same code for all of them. This is another thing that can't really be helped.

If you give the client software that gets installed on a machine and will run, the client can make a full system backup. As I mentioned before, they can restore from backup and turn back the clock as many times as they want. But they can also re-image another machine from that same backup. Handling both of these cases is a cat and mouse game of them trying to spoof the environment your application runs in and you trying to make sure they're not doing that.

Deriving an ID from hardware Is one simple option. This prevents the same license data from working on another machine ... but IDs can be spoofed. In this cat and mouse game you try to get legitimate unspoofed HW ids and the client tries to feed your application spoofed values. There isn't anything that can be done about this short of special hardware like TPMs.

$\endgroup$
  • 1
    $\begingroup$ Knowledge of the system lets you traverse the chain backwards but not forwards. With today's code you can get the code for yesterday but not the one for tomorrow. Doing so requires inverting F which requires finding a partial SHA2 preimage. For an 80 bit code this is not feasible. $\endgroup$ – Richard Thiessen Jun 17 '17 at 14:10
  • $\begingroup$ Does using a starting point a few centuries in the future add anything to the security of the system? The default target platform is powered by a several year old 1.3ghz tablet class Atom; and is substantially slower than your workstation. $\endgroup$ – Dan Neely Jun 17 '17 at 20:04
  • $\begingroup$ There's no security benefit. However, that figure was off by an order of magnitude. 300*365 ~=1e5 not 1e6 so 300 years is still pretty cheap. Also of note, the client only traverses a short part of the chain from the current date back to date 0. Making the chain extend further into the future puts load on your licensing server not the client. Consider pushing new chains to the clients whenever they are online. This keeps the start date closer to the present, meaning less chain to traverse during verification. $\endgroup$ – Richard Thiessen Jun 18 '17 at 0:01
  • $\begingroup$ Would I just be counting the number of hash iterations it takes to reach the zero code, and seeing if the number of days from the zero date to now is less to verify that it's a valid code for today; or am I missing something? $\endgroup$ – Dan Neely Jun 18 '17 at 4:29
  • $\begingroup$ Yup, you've got it. $\endgroup$ – Richard Thiessen Jun 18 '17 at 14:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.