Toby DiPasquale on 25 Apr 2006 04:07:17 -0000 |
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 ... end end feature :extra class Y def func2 ... end end feature :premium class Z def func3 ... end end 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): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 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: <snip> require 'unlock' unlock 'file' unlock 'otherfile' unlock 'notafile' ReallyGoodAppStarterJammie.start </snip> ...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 talk@phillyonrails.org http://lists.phillyonrails.org/mailman/listinfo/talk
|
|