Allen writes: This is an eexec encoder written in PostScript. It will encode text presented to it in the same way fonts are encrypted. There's no graphic output. You can run the output of abdict begin (encode.ps) (r) file encodefile just like the original! This file is encoded.ps, which was originally submitted for the contest, but discounted because it was machine generated. When I first heard of the contest, this (encoded.ps) was my immediate idea for an entry. The original code was pretty straightforward, once the number promotion problem was solved (large intermediate results would go to floating point and the bit operations would fail), but the encoded version was nearly unreadable without mechanical assistance. The text version (encode.ps) is a fairer submission since it is not "machine-generated in any way". I have a strong hunch that many of the submitted programs will have machine-generated strings of data, and while that's certainly not good, I'd accept it if the program that generated the data was also written in PostScript and included. Curiously, encoded.ps is the program that generated itself- sort of. The result is reproduceable by encoding encode.ps. You can also encode encoded.ps if you want. Would that program work? encode.ps is block formatted not to obscure its meaning, but to make it a little shorter because the encoding process expands it. You shouldn't consider its lexical presentation part of the obscurity. Notice that considerable dictionary dancing happens at the top. Why is that? Is it necessary? Would it be if this program were eexec encoded? The encode function encodes a single character. You shouldn't find that too difficult if you've read the description of eexec in The Black Book. The two ifs make sure the printed output comes out as we'd expect. The second isn't needed, but long lines are annoying even in an obfuscated program. :-) The next function, encodestring, is very straightforward. What would you do with an encoded string if you had one? Notice that since this is in sync with encode, you can call encode and encodestring alternatively at your convenience. The last function, encodefile, is the real meat of the program. It initializes the names used in the encode engine, prints out enough code to get things rolling, eexec encodes all the characters in the file, and then adds some convenience code to the end of the encoded program. Notice also that instead of randomly generating four inital bytes, I stuck a vanity line in. This was to make the output reproduceable. In general use, you'd probably take that out and use the random number generator. Several tricks were necessary to get around PostScript's implicit integer to floating point conversion. If you try to write it directly from the C version, you will fail. I've found this program rather useful, but there is a hitch: eexec doesn't work the same on all non-Adobe PostScript clones. Play with it and see. What should happen when the eexec is encountered: A new file object is pushed on the exec stack. This is the unencypted stream. systemdict is pushed on the dict stack. Note that this means you can't def anything because you can't write on systemdict! (Post 1.7 on the Amiga doesn't do this!) The text is decrypted and executed (see page 63 in The Black Book). If the currentfile is closed within the encrypted portion, execution resumes after the encrypted text. My program automatically adds this at the end of the program text. My program should leave the new dictionary containing the encryption routines on the dict stack (and the encrypted version should also leave systemdict there). GhostScript 2.3 pops it, Adobe PostScripts don't. This might be fixed in newer versions of gs. To be safe, I defined the dictionary in userdict so you could begin it if it wasn't on the dict stack. It'd be a little shorter if you didn't have to. Before and after you run encode.ps, do a countdictstack == to see how many dictionaries are on the dict stack. Try it on different PostScript interpreters. Report bugs as needed. :-) eexec encryption is usually only used for fonts, but it can be used (as we've seen) on nearly anything. Just be careful with your dictionaries. If all you do is draw, you should be OK! Try writing a simple program that does some graphics and no defs and encodefile it. Add code to an encoded file that interacts with the encrypted code. 24#Allen is a base 24 radix number. Mostly a red herring to throw the dogs off the scent. Kinky way to put in vanity info as well. :-) The lines to calculate the encoding (in /encode): r (the "random" number used by the encryption) starts at 55665. The job of encode is to take a character from the op stack, leave the encoded character on the op stack when it's done, and update r. Let's say the incoming character is a space (actually, r would be something else probably because of the ignored bytes up front): on op stack operator why -- -- ----- -------- --- 32 55665 -8 bitshift We want to mask the incoming 32 217 xor character with 8 bits of r. 249 255 and Make sure just 8 bits * 249 dup Eventually leave the crypted byte 249 249 55665 add Update r as: 249 55914 10569 mul r = ((cipher+r) * C1 + C2) & 16#ffff 249 590955066 65535 and This and 249 16954 5 mul and this mul are to keep it from 249 84770 22719 add going to floating point. 249 107489 65535 and Get new r back in range 249 41953 /r exch Update r 249 /r 41953 def 249 Encrypted character (plain^(r>>8)) C1 is 52485 and C2 is 22719 (mandated by the Black Magic hoodoo). If the intermediate result ever gets to 2^30, the number will be promoted to a real and you'll lose bits (so the xor won't work). The 65535 and is used to keep results lower. C1 was factored to keep the result from exploding for values of r greater than 20458. The first 65535 and (*) is in case some joker set r to something unreasonable. Obviously, this function never will. It's a lot clearer if you keep the integer limitations in mind and then go read the C example in The Black Book. Why you can do the and in the "middle" of the multiplication might puzzle you. Think about how r is used. Left as an exercise to the student is the function cexee that takes either a file or a string as an argument and leaves the encoding in an object on the stack for eexec to decode and execute.