def decrypt(self, cipher_line): if not util.is_encrypted(cipher_line): raise KarnError('"%s" is not encrypted!' % cipher_line) # convert from base 32 to bytes literal cipherbytes = bytearray.fromhex(unicode(util.inttohex(int(cipher_line.strip(), 32)))) output = bytearray() # start at byte 1 because byte 0 is the guard byte for i in xrange(1, len(cipherbytes), BLOCK_SIZE): cipher = cipherbytes[i:i+BLOCK_SIZE] # + is the concatenation operator in python # ^ is the xor operator leftmd = bytearray(hashlib.sha1(str(util.right(cipher)) + str(self.rightkey)).digest()) leftcipher = util.left(cipher) plaintext = bytearray(leftmd[i] ^ leftcipher[i] for i in xrange(len(leftmd))) rightmd = bytearray(hashlib.sha1(str(plaintext) + str(self.leftkey)).digest()) rightcipher = util.right(cipher) plaintext.extend(bytearray(rightmd[i] ^ rightcipher[i] for i in xrange(len(rightmd)))) # break if we decrypted a null byte output.extend(plaintext.partition('\x00')[0]) if '\x00' in plaintext: break # we are expecting an ascii string, so print debug info # if any decrypted character is out of ascii range if any(i > 127 for i in output): print 'couldn\'t decrypt ciphertext: %s' % cipher_line print 'with key: %d' % self.key print 'output: %s' % output raise DecryptionError(cipher_line, self.key, output) return str(output)
def __init__(self, key): self.key = key # need to convert integer to hex str before getting the byte array keybytes = bytearray.fromhex(unicode(util.inttohex(key))) # the monitor uses Java's BigInteger, which uses 2s complement. # that means that it will add on a 0 byte in front to preserve the # sign of the key, if the most significant bit of the key is set if keybytes[0] & (1 << 7): keybytes.insert(0,0) # even if the key ends up being an odd number of bytes, the monitor breaks it # into two equal halves, and the least significant byte gets discarded. # probably a bug in the monitor, but that's how it is halfkeylen = len(keybytes) // 2 self.leftkey = keybytes[:halfkeylen] self.rightkey = keybytes[halfkeylen: 2*halfkeylen]