Toby DiPasquale on 25 Apr 2006 04:07:17 -0000

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[PhillyOnRails] Ruby source code obfuscation

Hi all,

Tonight at the meeting, Erin asked our excellent and engaging speaker Obie
Fernandez about source code obfuscation techniques one could use to keep
clients from abusing the dynamic nature of purchased Ruby code and use
only what they paid for. Obie couldn't really comment, as in his line of 
work there isn't much call for that sort of thing. I think I may have a
workable solution, though...

Here's the high-level overview of how it would work:

Decide which pieces of the code go with what logical piece of functionality.
This is the weakest part of my scheme right now, as I'm not sure the best 
way to do this. I'm thinking a new method that defines a "scope" for code, 
similar to "public", "protected", etc in Ruby, but it would be more like:

feature :video_processing

or something similar. Work with me here...

Assuming there's a way to mark up the code so you know which pieces of
functionality are comprised of which code, you can then encrypt each piece
with a different private key. So, lets say in file.rb, there are three
class definitions, each associated with a different feature. So the source
code might look like this:

feature :base
class X
  def func1

feature :extra
class Y
  def func2

feature :premium
class Z
  def func3

So then class X is associated with the 'base' feature, class Y with the
'extra' feature set and class Z with the 'premium' feature. You might
default to file-level features with a YAML or XML config file or the like
for simplicity, but this might be handy to have when working with code 
where the features aren't broken out cleanly.

The encrypted output would look something like this (imagine it in binary):


where each part could be unlocked with the proper public key. The
encrypted files would get names like 'file.rb.lock' or something like
that (i.e. just '.lock' appended to the original file name).

Client buys your app and you'd then give the client the keys for the 
features they've payed for, but not the others.

The app has a small wrapper in unencrypted Ruby that would use a new
method, 'unlock' to do the special loading required to unlock the code for
the features they've purchased. Let's call it Kernel#unlock. Kernel#unlock
would know where to find the public keys the client has purchased from
your (via some Rail-ish magic or something such, or you could tell it) and
when you invoke it, it would load said public keys and then start
decrypting the .rb.lock files in your app.

So maybe your app's wrapper looks like:

require 'unlock'

unlock 'file'
unlock 'otherfile'
unlock 'notafile'


...or something like that.

If it encounters a piece of a file that can't be decrypted with the keys
it has, its simply skipped, as if it didn't exist. So in the above, if for
some reason, the client had 'base' and 'premium' keys, but not an 'extra'
key, their instance of the app just wouldn't see class Y.

Kernel#unlock could be written in pure Ruby using the crypto libraries
that come standard with Ruby 1.8.x. Specifically, you'd want to encrypt
with an asymmetric crypto here (e.g. RSA), and very likely also employ some
checksumming and the appropriate PKCS standards, as well. But you wouldn't
need any C extensions for this.

As an ISV, you could generate the files anew for each customer by
generating a new set of feature keys for that customer and encrypting that
client's download with the newly-generated feature set keys. Give them the
initial keys they pay for at time of delivery and save up the rest in the
event they want to buy additional functionality. You could generate less
than the 1:1 ratio of feature key sets to clients, but this would weaken
the security, although lower the processing on your servers, who would
have to do this key generation and encryption on the way out for every
customer downloading your app.... that's a big load if it gets popular.
You could also mitigate that by going the allofmp3 route, where the client
orders the app, downloads their keys and then waits for the app to be
encrypted in a queue of others before they can download it.

You'd also have to escrow the keys, but that could be easily integrated into
your existing backup solution (you do have backup, don't you?).

I'm hoping I haven't overlooked something stoopidly simple that will crack
this wide open... let me know if I have. What do you guys think of that? 
I might have some (nonexistent) free time to code this up this week, or 
maybe on the plane this weekend.

P.S. I know this can be defeated with a custom Ruby interpreter, but it
still requires someone to pay for all feature keys. Obviously, the fewer
feature key sets you generate and the more clients you have, the higher
the chances that malicious, collaborating clients will use the
aforementioned custom Ruby interpreter to dump out the source of the app
for the pieces they've collectively purchased (which, for a small enough
set of feature keys sets and a large enough set of clients, is all of the
source). The legal option of suing them is still on the table in that
case, and they have the additional burden of having to prove why they
violated not only copyright law but also the DMCA.

Toby DiPasquale
talk mailing list