If you want the cryptographically secure method you won't get around an internet connection to verify the serial. This means that such a protection scheme is better suited for subscription based software than it is for traditional shelf software.
With internet connections it's simple (since you use JS, I assume that's the one you want):
- Someone creates a key, that party has to know the secret
- Server knows secret and is able to decode the data
- Client sends the key, which is a binary blob encoded in Base32 (also see thisthis)
- Server decrypts the key and checks whether it's valid (either just syntactically or also semantically)
Without internet connection you can still use a scheme similar to PGP signing in order to verify that the blob that the user enters was created by you. Encryption won't work the same, because the decryption always requires to know the secret and the big issue here is:
- you don't trust the user (otherwise you wouldn't need the protection scheme)
- you give the user the secret, compiled into your binary
Obviously both points contradict each other. On one hand you don't trust the user, on the other hand you have to deploy the secret used for decryption.
All in all I have to conclude that without the internet connection and the server-based validity check that results in the revelation of some knowledge (e.g. content that is useful only for so long) any protection scheme I have seen so far is more or less an arms race between crackers and vendors.
Now, if it isn't important to have a cryptographically secure system, I'd still go for any binary data that you can secure by means of a simple CRC or so. What comes to mind would be:
- Have a monotonically increasing serial number
- XOR it with your salt value (or any other reversible operation)
- Take the CRC32 of that value (or Adler32 or whatever) - add more salt here if needed
- Encode it with Base32 (aids readability etc)
- Only check that the CRC32 for validity ...