Back to Table of Contents

Nikita Shulga's (aka malfet) submission:

The Python script replicates the ancient text decoding algorithm. Text on the diskette is encoded with an RSA algorithm, though key is so short it should be very easy to hack it via brute-force. However, the original engineer has added some obfuscation which makes a brute-force attack much more tricky.

It can not be used against the disk image, because the Lisp resource fork is compressed by some ancient algorithm I haven't figured out yet.

It doesn't look as if it artfully re-encrypts the code; it just corrupts part of the binary making it unusable the 2nd time. Below is the quote from the original LISP listing that overwrites the file with 0xffs (the file handle name also suggest there is no magic happening):

(setf trash (open #P"Agrippa:Agrippa" :direction :output :if-exists :overwrite)

(dotimes (notch 40000)
       (princ (int-char 255) trash))

This code opens the Agrippa executable and writes forty thousand 255s to it.

[Editor's note: c.f. the other submissions that discuss how this original design was changed. In the shipped code this corrupting feature calls a random number generator against "genetic" symbols (CTAG) and writes these to the binary.]

Later, Nikita provided this further information:

Code is decrypted using RSA decryption algorithm:

dec=(code^11)mod 4097.

As you can see both exponent(11) and modulo(4097) are small enough to be brute-forced even on a netbook.

Since selected modulo is close to 2^12=4096, text is decoded by group of 3 characters. And to make things a bit more interesting bit-scrambling routines are used for the middle byte of the encoded triplet and then for every symbol of decrypted text.

Scramble function un-permute-it() shuffles bits from 01234567 to 27416305 and scramble function un-waymute-it shuffles bits from 01234567 to 14725036 order. (See original LISP function implementation on page 3 of facsimiles)

Script nikita-decode.py implements the algorithms described above and successfully decrypts the poem from an encrypted text recovered from the emulator's memory dump

Script nikita-decode-from-file.py decodes the poem's text directly from the file on the floppy disc (not the image). Since Agrippa is self-extracting binary, the LZW decompression algorithm is necessary to get to an encoded text of the poem from the file. And the fact that the same script can be used to decode the poem once it was opened in the emulator proves that there are no smart re-encryption of the poem once it's read. Although the LISP code corrupts the runtime so that it can not be executed/read for a 2nd time.

And finally, Nikita provided an image of the decryption algorithm

A picture is worth a thousand words, so I've used graphviz to visualize decoding of the 6th triplet of the poem (characters from "word before" from the 1st line of the point). Encoded characters are 0x1a, 0x8f, 0x35 (0x prefix denotes hexadecimal numbers, where single digit corresponds to 4 bits), after the initial bit shuffling they turn into two 12-bit numbers: 0x61a and 0x35d, which are RSA-decoded into 0x999 and 0x9cf, which in turn are bit-shuffled into ASCII codes for characters 'f'(0x66), 'o'(0x6f) and 'r'(0x72). [Editor's Note: Compare Nikita's depiction of the decryption algorithm with Jeremy's. They evaluate the same, but Jeremy's displays the intermediate step at each level, whereas Nikita's combines the permutations into a single step.] Representation of decryption