Like everyone else, there is no magic bullet. The user can turn off the machine, get HD as a slave on another machine, back up everything, cancel the engine of your code, and then successfully crack it. After the user has physical access to the executable file, he is potentially compromised, and there is nothing to do to stop him in 100% of cases.
The best you can do is make the job of a potential cracker as hellish, but no matter what you do, it will not be indestructible.
Using self-destruction in case of something wrong can be handled by an attacker who has backed up everything.
Using the key in the USB driver helps make the life of the cracker more complicated, but can ultimately be defeated by a competent decisive cracker: code that unencrypted things cannot be in an encrypted state (including the part that receives the key), so this is a big weakness. Hacking this part of the code to save the key in another place hits the key.
If the software authenticates on a remote server, this can be done by attacking the client and bypassing authentication. If he receives the key from the server, you can sniff the network to intercept the server data containing the key. If the server data is encrypted, the cracker can decrypt it by analyzing software that unencrypts it and catches unencrypted data.
In particular, all that would be much easier for an attacker if he uses an emulator to run your software that can save snapshots of memory (including an unencrypted version of the algorithm). Even easier, if it can manipulate and eject memory right away while starting your software.
If you do not expect your unreliable client to be very decisive, you can simply complicate the situation and hope that they never get enough energy and skill to break them.
The best solution, in my opinion, is to get all the software on your trusted server and get their server to just ask your server to do the job and save your algorithms on your server. This is much safer and simpler than anything else, since it eliminates the main problem: the user no longer has physical access to the algorithm. You should really think about how to do this, eliminating your needs in order to save the code in the client. However, even this is not indestructible, the hacker can determine what the algorithm does by analyzing what is the output to the input function. In most scenarios (it doesn't seem like this is your case), the algorithm is not the most important in the system, but data instead.
So, if you really cannot avoid running the algorithm on the untrustworthy side, you cannot do much more than what you have already said to do: encrypt everything (mainly on the hardware level), authenticate and verify everything, destroy important data before than someone would think about backing it up if you suspect something is wrong and have someone crack it.
BUT IF YOU REALLY WANT SOME IDEAS AND REALLY WANT THIS, WE ARE HERE:
I could suggest you make your program a mutant. IE: When you decrypt your code, encrypt it with a different key and discard the old key. Get a new key from the server and make sure that the key itself is encoded in such a way that it would be very difficult to make fun of the server with something that gives compromised new keys. Make sure the key is unique and never reused. Again, this is not indestructible (and the first thing the cracker would do was attack this very feature).
One more thing: Put a lot of non-obvious red herrings that do insensitive weird sequence checks that contain a lot of non-functional dummy version of your algorithm and add a lot of complex overflow that does nothing effectively and claims that it works as expected from real code. Make real code do some things that look weird and no-sense too. This makes debugging and reverse work even more difficult, since hacking will require a lot of effort, trying to separate what is useful from what is undesirable.
EDIT: And obviously, make the junk e-mail part of the code better than the right one, so the cracker will look there first, effectively wasting time and patience. It goes without saying that everyone is confused, so even if the cracker gets a simple unencrypted start code, he still looks confusing and very strange.