##
## idea.py -- IDEA en/decryption.
##
## Copyleft 1998 Teemu Kalvas <chery@s2.org>,
##          1995-1997 Timo J. Rinne <tri@iki.fi>
##

from types import *

import b64
import crc32
import rnd

def mask_16bit(p):
    return p & 65535

def _plus(p, q):
    return mask_16bit(p + q)

def _minus(p, q):
    return mask_16bit(p - q)

def _div(p, q):
    return mask_16bit(p / q)

def _mod(p, q):
    return mask_16bit(p % q)

def _mul(p, q):
    pl = p & 255
    ph = (p >> 8) & 255
    ql = q & 255
    qh = (q >> 8) & 255
    return mask_16bit((pl * ql) + 256 * (pl * qh + ph * ql))

def _xor(p, q):
    return p ^ q

def xor_blocks(p, q):
    return map(_xor, p, q)

def _and(p, q):
    return mask_16bit(p) & mask_16bit(q)

def _or(p, q):
    return mask_16bit(p) | mask_16bit(q)

def mul(p, q):
    pl = p & 255
    ph = (p >> 8) & 255
    ql = q & 255
    qh = (q >> 8) & 255
    r1 = pl * ql
    r2 = pl * qh + ph * ql
    r3 = ph * qh
    l = r1 + ((r2 & 255) << 8)
    h = (r2 >> 8) + (l >> 16) + r3
    l = mask_16bit(l)
    if l or h:
	return mask_16bit(_plus(_minus(l, h), l < h))
    elif not p:
	return mask_16bit(_minus(1, q))
    else:
	return mask_16bit(_minus(1, p))

def random():
    return mask_16bit(abs(rnd.rnd()))

def random_char():
    return _and(random(), 255)

def _left(p, q):
    if not p or not q:
	return 0
    return mask_16bit(p << q)

def _right(p, q):
    if not p or not q:
	return 0
    return mask_16bit(p >> q)

def additive_inverse(p):
    return mask_16bit(-p)

def multiplicative_inverse(p):
    x = mask_16bit(p)
    if x < 2:
	return x
    h2 = _div(65537, x)
    y = _mod(65537, x)
    if y == 1:
	return mask_16bit(_minus(1, h2))
    h1 = 1
    while 1:
	q = _div(x, y)
	x = _mod(x, y)
	h1 = _plus(h1, _mul(q, h2))
	if x == 1:
	    return mask_16bit(h1)
	q = _div(y, x)
	y = _mod(y, x)
	h2 = _plus(h2, _mul(q, h1))
	if y == 1:
	    return mask_16bit(_minus(1, h2))

def shift_key_25bits_left(key):
    return [_or(_left(key[1], 9), _right(key[2], 7)),
	    _or(_left(key[2], 9), _right(key[3], 7)),
	    _or(_left(key[3], 9), _right(key[4], 7)),
	    _or(_left(key[4], 9), _right(key[5], 7)),
	    _or(_left(key[5], 9), _right(key[6], 7)),
	    _or(_left(key[6], 9), _right(key[7], 7)),
	    _or(_left(key[7], 9), _right(key[0], 7)),
	    _or(_left(key[0], 9), _right(key[1], 7))]

# new
def interlace_blocklist(bl):
    l = len(bl)
    r = l * [None]
    i = 0
    while i < l:
        j = 4 * i
        r[i] = [bl[j % l][j / l],
                bl[(j + 1) % l][(j + 1) / l],
                bl[(j + 2) % l][(j + 2) / l],
                bl[(j + 3) % l][(j + 3) / l]]
        i = i + 1
    return r

def expand_string_to_key(str, version = 0):
    if not version:
        version = config.idea_default_version
    return [expand_string_to_key_1, expand_string_to_key_2,
            expand_string_to_key_3][version - 1](str)

def expand_string_to_key_1(str):
    if not str:
	return [0, 0, 0, 0, 0, 0, 0, 0]
    if len(str) < 64:
	for i in range(0, 8):
	    str = str + crc32.string(str)
    l = len(str)
    str = str + repr(l)
    l = len(str)
    v1 = crc32.string(str)
    v2 = crc32.string(str[l / 4:])
    v3 = crc32.string(str[2 * (l / 4):])
    v4 = crc32.string(str[3 * (l / 4):])
    return [_4hex_to_int(v1[:4]), _4hex_to_int(v1[4:]), _4hex_to_int(v2[:4]),
	    _4hex_to_int(v2[4:]), _4hex_to_int(v3[:4]), _4hex_to_int(v3[4:]),
	    _4hex_to_int(v4[:4]), _4hex_to_int(v4[4:])]

def expand_string_to_key_2(str):
    if not str:
        return [0, 0, 0, 0, 0, 0, 0, 0]
    if len(str) > 3:
        s = str
    else:
        s = str + crc32.string(str, "raw-string")
    l = len(s)
    s1 = expand_substring(chr(0) + chr(l & 255) + s[:l / 4])
    s2 = expand_substring(chr(85) + chr(l & 255) + s[l / 4:l / 2])
    s3 = expand_substring(chr(170) + chr(l & 255) + s[l / 2:l / 2 + l / 4])
    s4 = expand_substring(chr(255) + chr(l & 255) + s[l / 2 + l / 4:])
    v1 = crc32.string(s1)
    v2 = crc32.string(s2)
    v3 = crc32.string(s3)
    v4 = crc32.string(s4)
    return [_4hex_to_int(v1[:4]), _4hex_to_int(v1[4:]), _4hex_to_int(v2[:4]),
            _4hex_to_int(v2[4:]), _4hex_to_int(v3[:4]), _4hex_to_int(v3[4:]),
            _4hex_to_int(v4[:4]), _4hex_to_int(v4[4:])]

def expand_string_to_key_3(str):
    if not str:
        return [0, 0, 0, 0, 0, 0, 0, 0]
    kk = [31415, 58979, 32384, 62643, 38327, 16939, 5820, 45923]
    ek = build_encryption_key(kk, 666)
    bl = interlace_blocklist(cleartext_string_to_block_list(str, 0))
    r1 = [7816, 40628, 62089, 3482]
    r2 = [53421, 17067, 13282, 30664]
    r3 = [44609, 55058, 22317, 25359]
    r4 = [40812, 17450, 28410, 27019]
    i = 0
    l = len(bl)
    while i < l:
        r1 = xor_blocks(crypt_transform_block(xor_blocks(bl[i], r2), ek), r1)
        if i + 1 < l:
            r2 = xor_blocks(crypt_transform_block(xor_blocks(bl[i + 1], r1),
                                                  ek), r2)
        i = i + 1
    ek = build_encryption_key([r1[0], r2[0], r1[1], r2[1], r1[2], r2[2], r1[3],
                               r2[3]], 666)
    bl = interlace_blocklist(bl)
    i = 0
    while i < l:
        r3 = xor_blocks(crypt_transform_block(xor_blocks(bl[i], r4), ek), r3)
        if i + 1 < l:
            r4 = xor_blocks(crypt_transform_block(xor_blocks(bl[i + 1], r3),
                                                  ek), r4)
        i = i + 1
    return [r3[0], r4[0], r3[1], r4[1], r3[2], r4[2], r3[3], r4[3]]

def expand_substring(str):
    if not str:
        return str
    str = crc32.string(str, "raw-string") + str
    i = 3 + (ord(str[0]) & 3)
    while i > 0:
        i = i - 1
        str = crc32.string(str, "raw-string") + str
    return str

def random_key():
    return [random(), random(), random(), random(),
	    random(), random(), random(), random()]

def build_encryption_key(passphrase, version = 0):
    if type(passphrase) == StringType:
	s1 = expand_string_to_key(passphrase, version)
    elif type(passphrase) == ListType:
	s1 = passphrase
    annotation = build_key_annotation(s1, 'e', version)
    s2 = shift_key_25bits_left(s1)
    s3 = shift_key_25bits_left(s2)
    s4 = shift_key_25bits_left(s3)
    s5 = shift_key_25bits_left(s4)
    s6 = shift_key_25bits_left(s5)
    s7 = shift_key_25bits_left(s6)
    return [s1[:6], s1[6:] + s2[:4], s2[4:] + s3[:2], s3[2:], s4[:6],
	    s4[6:] + s5[:4], s5[4:] + s6[:2], s6[2:], s7[:4], annotation]

def build_decryption_key(passphrase, version = 0):
    k = build_encryption_key(passphrase, version)
    annotation = build_key_annotation(k[0][:6] + k[1][:2], 'd', version)
    return [[multiplicative_inverse(k[8][0]), additive_inverse(k[8][1]),
	     additive_inverse(k[8][2]), multiplicative_inverse(k[8][3]),
	     k[7][4], k[7][5]],
	    [multiplicative_inverse(k[7][0]), additive_inverse(k[7][2]),
	     additive_inverse(k[7][1]), multiplicative_inverse(k[7][3]),
	     k[6][4], k[6][5]],
	    [multiplicative_inverse(k[6][0]), additive_inverse(k[6][2]),
	     additive_inverse(k[6][1]), multiplicative_inverse(k[6][3]),
	     k[5][4], k[5][5]],
	    [multiplicative_inverse(k[5][0]), additive_inverse(k[5][2]),
	     additive_inverse(k[5][1]), multiplicative_inverse(k[5][3]),
	     k[4][4], k[4][5]],
	    [multiplicative_inverse(k[4][0]), additive_inverse(k[4][2]),
	     additive_inverse(k[4][1]), multiplicative_inverse(k[4][3]),
	     k[3][4], k[3][5]],
	    [multiplicative_inverse(k[3][0]), additive_inverse(k[3][2]),
	     additive_inverse(k[3][1]), multiplicative_inverse(k[3][3]),
	     k[2][4], k[2][5]],
	    [multiplicative_inverse(k[2][0]), additive_inverse(k[2][2]),
	     additive_inverse(k[2][1]), multiplicative_inverse(k[2][3]),
	     k[1][4], k[1][5]],
	    [multiplicative_inverse(k[1][0]), additive_inverse(k[1][2]),
	     additive_inverse(k[1][1]), multiplicative_inverse(k[1][3]),
	     k[0][4], k[0][5]],
	    [multiplicative_inverse(k[0][0]), additive_inverse(k[0][1]),
	     additive_inverse(k[0][2]), multiplicative_inverse(k[0][3])],
	    annotation]

def encryption_round(data, subkey):
    v1 = mul(data[0], subkey[0])
    v2 = _plus(data[1], subkey[1])
    v3 = _plus(data[2], subkey[2])
    v4 = mul(data[3], subkey[3])
    v5 = _xor(v1, v3)
    v6 = _xor(v2, v4)
    v7 = mul(v5, subkey[4])
    v8 = _plus(v6, v7)
    v9 = mul(v8, subkey[5])
    v10 = _plus(v7, v9)
    return [_xor(v1, v9), _xor(v3, v9), _xor(v2, v10), _xor(v4, v10)]

def final_transform(data, subkey):
    return [mul(data[0], subkey[0]), _plus(data[2], subkey[1]),
	    _plus(data[1], subkey[2]), mul(data[3], subkey[3])]

def hex_to_key(h):
    h1 = _4hex_to_int(h[:4])
    h2 = _4hex_to_int(h[4:8])
    h3 = _4hex_to_int(h[8:12])
    h4 = _4hex_to_int(h[12:16])
    h5 = _4hex_to_int(h[16:20])
    h6 = _4hex_to_int(h[20:24])
    h7 = _4hex_to_int(h[24:28])
    h8 = _4hex_to_int(h[28:32])
    if h1 > -1 and h2 > -1 and h3 > -1 and h4 > -1 and \
       h5 > -1 and h6 > -1 and h7 > -1 and h8 > -1:
	return [h1, h2, h3, h4, h5, h6, h7, h8]
    else:
	return [0, 0, 0, 0, 0, 0, 0, 0]

def _4hex_to_int(h):
    x0 = hex_char_to_int(h[3])
    x1 = hex_char_to_int(h[2])
    x2 = hex_char_to_int(h[1])
    x3 = hex_char_to_int(h[0])
    if x0 > -1 and x1 > -1 and x2 > -1 and x3 > -1:
	return 4096 * x3 + 256 * x2 + 16 * x1 + x0
    else:
	return -1

def _2hex_to_int(h):
    x0 = hex_char_to_int(h[1])
    x1 = hex_char_to_int(h[0])
    if x0 > -1 and x1 > -1:
        return x0 + 16 * x1
    else:
        return -1

def crypt_transform_block(data, key):
    for k in key[:8]:
	data = encryption_round(data, k)
    return final_transform(data, key[8])

def encrypt_block(data, key, version = 0):
    if type(key) <> ListType:
	key = build_encryption_key(key, version)
    return crypt_transform_block(data, key)

def decrypt_block(data, key, version = 0):
    if type(key) <> ListType:
	key = build_decryption_key(key, version)
    return crypt_transform_block(data, key)

def fixed_string_to_block(str):
    return [_or(_left(ord(str[0]), 8), ord(str[1])),
	    _or(_left(ord(str[2]), 8), ord(str[3])),
	    _or(_left(ord(str[4]), 8), ord(str[5])),
	    _or(_left(ord(str[6]), 8), ord(str[7]))]

def block_to_fixed_string(block):
    return chr(_right(block[0], 8)) + chr(_and(block[0], 255)) + \
	   chr(_right(block[1], 8)) + chr(_and(block[1], 255)) + \
	   chr(_right(block[2], 8)) + chr(_and(block[2], 255)) + \
	   chr(_right(block[3], 8)) + chr(_and(block[3], 255))

def add_padding(str, rndpad):
    pad = 8 - len(str) % 8
    if pad <= 0:
	pad = 8
    if rndpad:
	p1 = random_char()
    else:
	p1 = 0
    p1 = _or(_and(p1, 31), _left(pad - 1, 5))
    p = chr(p1)
    for i in range(1, pad):
	if rndpad:
	    p = p + chr(random_char())
	else:
	    p = p + '\000'
    return p + str

def remove_padding(str):
    if len(str) < 8:
	return ()
    return str[1 + _right(ord(str[0]), 5):]

def cleartext_string_to_block_list(str, rndpad = 0):
    str = add_padding(add_crc(str), rndpad)
    l = len(str)
    r = (l / 8) * [0]
    i = 0
    while i < l:
	r[i / 8] = fixed_string_to_block(str[i:i + 8])
	i = i + 8
    return r

def ciphertext_string_to_block_list(str):
    l = len(str)
    r = (l / 8) * [0]
    i = 0
    while i < l:
	r[i / 8] = fixed_string_to_block(str[i:i + 8])
	i = i + 8
    return r

def ecb_encrypt_string(str, key, version = 0):
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-decryption my-key-type':
	return () ## (error "KEY is not an encryption key.")
    elif my_key_type == 'key-complete-encryption':
	my_key = key
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
	my_key = build_encryption_key(key, version)
    else:
	return () ## (error "Invalid key.")
    data = cleartext_string_to_block_list(str, 0)
    l = len(data)
    r = ''
    i = 0
    while i < l:
	b = block_to_fixed_string(encrypt_block(data[i], my_key))
	r = r + b
	i = i + 1
    return b64.encode_string(r)

def ecb_decrypt_string(cipstr, key, version = 0):
    str = b64.decode_string(cipstr)
    if not str or len(str) % 8:
	return ()
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-encryption':
	return () ## (error "KEY is not an decryption key.")
    elif my_key_type == 'key-complete-decryption':
	my_key = key
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
	my_key = build_decryption_key(key, version)
    else:
	return () ## (error "Invalid key.")
    data = ciphertext_string_to_block_list(str)
    l = len(data)
    r = ''
    i = 0
    while i < l:
	b = block_to_fixed_string(encrypt_block(data[i], my_key))
	r = r + b
	i = i + 1
    return check_crc(remove_padding(r))

def cbc_encrypt_string(str, key, version = 0):
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-decryption my-key-type':
	return () ## (error "KEY is not an encryption key.")
    elif my_key_type == 'key-complete-encryption':
	my_key = key
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
	my_key = build_encryption_key(key, version)
    else:
	return () ## (error "Invalid key.")
    data = cleartext_string_to_block_list(str, 1)
    c = [0, 0, 0, 0]
    l = len(data)
    r = ''
    i = 0
    while i < l:
	c = encrypt_block(xor_blocks(data[i], c), my_key)
	b = block_to_fixed_string(c)
	r = r + b
	i = i + 1
    return b64.encode_string(r)

def cbc_decrypt_string(cipstr, key, version = 0):
    str = b64.decode_string(cipstr)
    if not str or len(str) % 8:
	return ()
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-encryption':
	return () ## (error "KEY is not an decryption key.")
    elif my_key_type == 'key-complete-decryption':
	my_key = key
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
	my_key = build_decryption_key(key, version)
    else:
	return () ## (error "Invalid key.")
    data = ciphertext_string_to_block_list(str)
    c = [0, 0, 0, 0]
    l = len(data)
    r = ''
    i = 0
    while i < l:
	b = block_to_fixed_string(xor_blocks(encrypt_block(data[i], my_key),
					     c))
	c = data[i]
	r = r + b
	i = i + 1
    return check_crc(remove_padding(r))

def add_crc(str):
    return crc32.string(str) + str

def check_crc(str):
    if len(str) < 8:
	return ()
    rstr = str[8:]
    if str[:8] == crc32.string(rstr):
	return rstr
    else:
	return ()

def hex_char_to_int(x):
    if x >= '0' and x <= '9':
	return ord(x) - ord('0')
    elif x >= 'a' and x <= 'f':
	return ord(x) - ord('a') + 10
    elif x >= 'A' and x <= 'F':
	return ord(x) - ord('A') + 10

def build_key_annotation(key, type, version = 0):
    if not version:
        version = config.idea_default_version
    if version == 666:
        return type + ":xxxxxxxxxx"
    return [build_key_annotation_1, build_key_annotation_2,
            build_key_annotation_3][version - 1](key, type)

def build_key_annotation_1(key, type):
    r = chr(_and(_right(key[7], 8), 255)) + chr(_and(key[7], 255)) + \
	chr(_and(_right(key[6], 8), 255)) + chr(_and(key[6], 255)) + \
	chr(_and(_right(key[5], 8), 255)) + chr(_and(key[5], 255)) + \
	chr(_and(_right(key[4], 8), 255)) + chr(_and(key[4], 255)) + \
	chr(_and(_right(key[3], 8), 255)) + chr(_and(key[3], 255)) + \
	chr(_and(_right(key[2], 8), 255)) + chr(_and(key[2], 255)) + \
	chr(_and(_right(key[1], 8), 255)) + chr(_and(key[1], 255)) + \
	chr(_and(_right(key[0], 8), 255)) + chr(_and(key[0], 255))
    return type + ':' + crc32.string(r)

def build_key_annotation_2(key, type):
    if key[0] == 0 and key[1] == 0 and key[2] == 0 and key[3] == 0 \
       and key[4] == 0 and key[5] == 0 and key[6] == 0 and key[7] == 0:
        return type + ":000000000000"
    r = chr(_and(_right(key[3], 8), 255)) + chr(_and(key[3], 255)) + \
        chr(_and(_right(key[2], 8), 255)) + chr(_and(key[2], 255)) + \
        chr(_and(_right(key[1], 8), 255)) + chr(_and(key[1], 255)) + \
        chr(_and(_right(key[0], 8), 255)) + chr(_and(key[0], 255)) + \
        chr(0)
    s = chr(_and(_right(key[7], 8), 255)) + chr(_and(key[7], 255)) + \
        chr(_and(_right(key[6], 8), 255)) + chr(_and(key[6], 255)) + \
        chr(_and(_right(key[5], 8), 255)) + chr(_and(key[5], 255)) + \
        chr(_and(_right(key[4], 8), 255)) + chr(_and(key[4], 255)) + \
        chr(255)
    s = crc32.string(r + s, "raw-string") + s
    r = crc32.string(s + r, "raw-string") + r
    return type + ':' + crc32.string(r)[:6] + crc32.string(s)[:6]

def build_key_annotation_3(key, type):
    if key[0] == 0 and key[1] == 0 and key[2] == 0 and key[3] == 0 \
       and key[4] == 0 and key[5] == 0 and key[6] == 0 and key[7] == 0:
        return type + ":0000000000000000"
    r = chr(_and(_right(key[7], 8), 255)) + chr(_and(key[7], 255)) + \
	chr(_and(_right(key[6], 8), 255)) + chr(_and(key[6], 255)) + \
	chr(_and(_right(key[5], 8), 255)) + chr(_and(key[5], 255)) + \
	chr(_and(_right(key[4], 8), 255)) + chr(_and(key[4], 255)) + \
	chr(_and(_right(key[3], 8), 255)) + chr(_and(key[3], 255)) + \
	chr(_and(_right(key[2], 8), 255)) + chr(_and(key[2], 255)) + \
	chr(_and(_right(key[1], 8), 255)) + chr(_and(key[1], 255)) + \
	chr(_and(_right(key[0], 8), 255)) + chr(_and(key[0], 255))
    ra = expand_string_to_key_3(r)
    return type + ':' + chr(97 + ra[0] % 26) + chr(97 + ra[1] % 26) + \
           chr(97 + ra[2] % 26) + chr(97 + ra[3] % 26) + chr(97 + ra[4] % 26) \
           + chr(97 + ra[5] % 26) + chr(97 + ra[6] % 26) + \
           chr(97 + ra[7] % 26) + chr(97 + ra[0] / 256 % 26) + \
           chr(97 + ra[1] / 256 % 26) + chr(97 + ra[2] / 256 % 26) + \
           chr(97 + ra[3] / 256 % 26) + chr(97 + ra[4] / 256 % 26) + \
           chr(97 + ra[5] / 256 % 26) + chr(97 + ra[6] / 256 % 26) + \
           chr(97 + ra[7] / 256 % 26)

def legal_subkey_p(subkey):
    return type(subkey) == ListType and len(subkey) == 6 and \
	   type(subkey[0]) == IntType and type(subkey[1]) == IntType and \
	   type(subkey[2]) == IntType and type(subkey[3]) == IntType and \
	   type(subkey[4]) == IntType and type(subkey[5]) == IntType and \
	   subkey[0] >= 0 and subkey[0] <= 65535 and \
	   subkey[1] >= 0 and subkey[1] <= 65535 and \
	   subkey[2] >= 0 and subkey[2] <= 65535 and \
	   subkey[3] >= 0 and subkey[3] <= 65535 and \
	   subkey[4] >= 0 and subkey[4] <= 65535 and \
	   subkey[5] >= 0 and subkey[5] <= 65535

def legal_finalkey_p(finalkey):
    return type(finalkey) == ListType and len(finalkey) == 4 and \
	   type(finalkey[0]) == IntType and type(finalkey[1]) == IntType and \
	   type(finalkey[2]) == IntType and type(finalkey[3]) == IntType and \
	   finalkey[0] >= 0 and finalkey[0] <= 65535 and \
	   finalkey[1] >= 0 and finalkey[1] <= 65535 and \
	   finalkey[2] >= 0 and finalkey[2] <= 65535 and \
	   finalkey[3] >= 0 and finalkey[3] <= 65535

def legal_key(key):
    if type(key) == StringType:
	return 'key-string'
    elif type(key) == ListType and len(key) == 8 and \
	 type(key[0]) == IntType and type(key[1]) == IntType and \
	 type(key[2]) == IntType and type(key[3]) == IntType and \
	 type(key[4]) == IntType and type(key[5]) == IntType and \
	 type(key[6]) == IntType and type(key[7]) == IntType and \
	 key[0] >= 0 and key[0] <= 65535 and \
	 key[1] >= 0 and key[1] <= 65535 and \
	 key[2] >= 0 and key[2] <= 65535 and \
	 key[3] >= 0 and key[3] <= 65535 and \
	 key[4] >= 0 and key[4] <= 65535 and \
	 key[5] >= 0 and key[5] <= 65535 and \
	 key[6] >= 0 and key[6] <= 65535 and \
	 key[7] >= 0 and key[7] <= 65535:
	return 'key-intlist'
    elif type(key) == ListType and len(key) == 10 and \
	 legal_subkey_p(key[0]) and legal_subkey_p(key[1]) and \
	 legal_subkey_p(key[2]) and legal_subkey_p(key[3]) and \
	 legal_subkey_p(key[4]) and legal_subkey_p(key[5]) and \
	 legal_subkey_p(key[6]) and legal_subkey_p(key[7]) and \
	 legal_finalkey_p(key[8]):
	if key_type(key) == 'encryption':
	    return 'key-complete-encryption'
	else:
	    return 'key-complete-decryption'
    else:
	return ()

def key_version(key):
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-decryption' \
       or my_key_type == 'key-complete-encryption':
        fingerprint = key_fingerprint(key)
        if len(fingerprint) == 8:
            return 1
        elif len(fingerprint) == 12:
            return 2
        elif len(fingerprint) == 16:
            return 3
        else:
            return 0
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
        return 'any'
    else:
        return 0

def key_fingerprint(key, version = 0):
    my_key_type = legal_key(key)
    if my_key_type == 'key-complete-decryption' or \
       my_key_type == 'key-complete-encryption':
	my_key = key
    elif my_key_type == 'key-string' or my_key_type == 'key-intlist':
	my_key = build_encryption_key(key, version)
    else:
	return ()
    annotation = my_key[9]
    if not (type(annotation) == StringType and len(annotation) > 9):
	return ()
    if annotation[1] <> ':' or annotation[0] not in 'de':
	return ()
    return annotation[2:]

def key_type(key):
    if type(key) <> ListType:
	return ()
    annotation = key[9]
    if not (type(annotation) == StringType and len(annotation) > 9):
	return ()
    if annotation[1] <> ':' or annotation[0] not in 'de':
	return ()
    if annotation[0] == 'e':
        return 'encryption'
    else:
        return 'decryption'

## End. ##
