initial code for dumping imessages in a reasonable format

This commit is contained in:
Jeffrey Paul
2014-02-09 00:30:49 +01:00
parent c0021efb13
commit 9dd7628f04
157 changed files with 24178 additions and 0 deletions

View File

@@ -0,0 +1,354 @@
#!/usr/bin/python
# -*- coding: ascii -*-
###########################################################################
# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
#
# Copyright (C) 2007, 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation.
#
# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Country of origin: Canada
#
###########################################################################
# Sample PBKDF2 usage:
# from Crypto.Cipher import AES
# from PBKDF2 import PBKDF2
# import os
#
# salt = os.urandom(8) # 64-bit salt
# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
# iv = os.urandom(16) # 128-bit IV
# cipher = AES.new(key, AES.MODE_CBC, iv)
# ...
#
# Sample crypt() usage:
# from PBKDF2 import crypt
# pwhash = crypt("secret")
# alleged_pw = raw_input("Enter password: ")
# if pwhash == crypt(alleged_pw, pwhash):
# print "Password good"
# else:
# print "Invalid password"
#
###########################################################################
# History:
#
# 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Initial Release (v1.0)
#
# 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Bugfix release (v1.1)
# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
# function in the previous release) silently truncates all keys to 64
# bytes. The way it was used in the previous release, this would only be
# problem if the pseudorandom function that returned values larger than
# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
# anything that silently reduces the security margin from what is
# expected.
#
# 2008-06-17 Dwayne C. Litzenberger <dlitz@dlitz.net>
# - Compatibility release (v1.2)
# - Add support for older versions of Python (2.2 and 2.3).
#
###########################################################################
__version__ = "1.2"
from struct import pack
from binascii import b2a_hex
from random import randint
import string
try:
# Use PyCrypto (if available)
from Crypto.Hash import HMAC, SHA as SHA1
except ImportError:
# PyCrypto not available. Use the Python standard library.
import hmac as HMAC
import sha as SHA1
def strxor(a, b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
def b64encode(data, chars="+/"):
tt = string.maketrans("+/", chars)
return data.encode('base64').replace("\n", "").translate(tt)
class PBKDF2(object):
"""PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
This implementation takes a passphrase and a salt (and optionally an
iteration count, a digest module, and a MAC module) and provides a
file-like object from which an arbitrarily-sized key can be read.
If the passphrase and/or salt are unicode objects, they are encoded as
UTF-8 before they are processed.
The idea behind PBKDF2 is to derive a cryptographic key from a
passphrase and a salt.
PBKDF2 may also be used as a strong salted password hash. The
'crypt' function is provided for that purpose.
Remember: Keys generated using PBKDF2 are only as strong as the
passphrases they are derived from.
"""
def __init__(self, passphrase, salt, iterations=1000,
digestmodule=SHA1, macmodule=HMAC):
self.__macmodule = macmodule
self.__digestmodule = digestmodule
self._setup(passphrase, salt, iterations, self._pseudorandom)
def _pseudorandom(self, key, msg):
"""Pseudorandom function. e.g. HMAC-SHA1"""
return self.__macmodule.new(key=key, msg=msg,
digestmod=self.__digestmodule).digest()
def read(self, bytes):
"""Read the specified number of key bytes."""
if self.closed:
raise ValueError("file-like object is closed")
size = len(self.__buf)
blocks = [self.__buf]
i = self.__blockNum
while size < bytes:
i += 1
if i > 0xffffffffL or i < 1:
# We could return "" here, but
raise OverflowError("derived key too long")
block = self.__f(i)
blocks.append(block)
size += len(block)
buf = "".join(blocks)
retval = buf[:bytes]
self.__buf = buf[bytes:]
self.__blockNum = i
return retval
def __f(self, i):
# i must fit within 32 bits
assert 1 <= i <= 0xffffffffL
U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
result = U
for j in xrange(2, 1+self.__iterations):
U = self.__prf(self.__passphrase, U)
result = strxor(result, U)
return result
def hexread(self, octets):
"""Read the specified number of octets. Return them as hexadecimal.
Note that len(obj.hexread(n)) == 2*n.
"""
return b2a_hex(self.read(octets))
def _setup(self, passphrase, salt, iterations, prf):
# Sanity checks:
# passphrase and salt must be str or unicode (in the latter
# case, we convert to UTF-8)
if isinstance(passphrase, unicode):
passphrase = passphrase.encode("UTF-8")
if not isinstance(passphrase, str):
raise TypeError("passphrase must be str or unicode")
if isinstance(salt, unicode):
salt = salt.encode("UTF-8")
if not isinstance(salt, str):
raise TypeError("salt must be str or unicode")
# iterations must be an integer >= 1
if not isinstance(iterations, (int, long)):
raise TypeError("iterations must be an integer")
if iterations < 1:
raise ValueError("iterations must be at least 1")
# prf must be callable
if not callable(prf):
raise TypeError("prf must be callable")
self.__passphrase = passphrase
self.__salt = salt
self.__iterations = iterations
self.__prf = prf
self.__blockNum = 0
self.__buf = ""
self.closed = False
def close(self):
"""Close the stream."""
if not self.closed:
del self.__passphrase
del self.__salt
del self.__iterations
del self.__prf
del self.__blockNum
del self.__buf
self.closed = True
def crypt(word, salt=None, iterations=None):
"""PBKDF2-based unix crypt(3) replacement.
The number of iterations specified in the salt overrides the 'iterations'
parameter.
The effective hash length is 192 bits.
"""
# Generate a (pseudo-)random salt if the user hasn't provided one.
if salt is None:
salt = _makesalt()
# salt must be a string or the us-ascii subset of unicode
if isinstance(salt, unicode):
salt = salt.encode("us-ascii")
if not isinstance(salt, str):
raise TypeError("salt must be a string")
# word must be a string or unicode (in the latter case, we convert to UTF-8)
if isinstance(word, unicode):
word = word.encode("UTF-8")
if not isinstance(word, str):
raise TypeError("word must be a string or unicode")
# Try to extract the real salt and iteration count from the salt
if salt.startswith("$p5k2$"):
(iterations, salt, dummy) = salt.split("$")[2:5]
if iterations == "":
iterations = 400
else:
converted = int(iterations, 16)
if iterations != "%x" % converted: # lowercase hex, minimum digits
raise ValueError("Invalid salt")
iterations = converted
if not (iterations >= 1):
raise ValueError("Invalid salt")
# Make sure the salt matches the allowed character set
allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
for ch in salt:
if ch not in allowed:
raise ValueError("Illegal character %r in salt" % (ch,))
if iterations is None or iterations == 400:
iterations = 400
salt = "$p5k2$$" + salt
else:
salt = "$p5k2$%x$%s" % (iterations, salt)
rawhash = PBKDF2(word, salt, iterations).read(24)
return salt + "$" + b64encode(rawhash, "./")
# Add crypt as a static method of the PBKDF2 class
# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
# crypt.
PBKDF2.crypt = staticmethod(crypt)
def _makesalt():
"""Return a 48-bit pseudorandom salt for crypt().
This function is not suitable for generating cryptographic secrets.
"""
binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
return b64encode(binarysalt, "./")
def test_pbkdf2():
"""Module self-test"""
from binascii import a2b_hex
#
# Test vectors from RFC 3962
#
# Test 1
result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
if result != expected:
raise RuntimeError("self-test failed")
# Test 2
result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
"a7e52ddbc5e5142f708a31e2e62b1e13")
if result != expected:
raise RuntimeError("self-test failed")
# Test 3
result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
expected = ("139c30c0966bc32ba55fdbf212530ac9"
"c5ec59f1a452f5cc9ad940fea0598ed1")
if result != expected:
raise RuntimeError("self-test failed")
# Test 4
result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
expected = ("9ccad6d468770cd51b10e6a68721be61"
"1a8b4d282601db3b36be9246915ec82a")
if result != expected:
raise RuntimeError("self-test failed")
#
# Other test vectors
#
# Chunked read
f = PBKDF2("kickstart", "workbench", 256)
result = f.read(17)
result += f.read(17)
result += f.read(1)
result += f.read(2)
result += f.read(3)
expected = PBKDF2("kickstart", "workbench", 256).read(40)
if result != expected:
raise RuntimeError("self-test failed")
#
# crypt() test vectors
#
# crypt 1
result = crypt("cloadm", "exec")
expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
if result != expected:
raise RuntimeError("self-test failed")
# crypt 2
result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
if result != expected:
raise RuntimeError("self-test failed")
# crypt 3
result = crypt("dcl", "tUsch7fU", iterations=13)
expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
if result != expected:
raise RuntimeError("self-test failed")
# crypt 4 (unicode)
result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
if result != expected:
raise RuntimeError("self-test failed")
if __name__ == '__main__':
test_pbkdf2()
# vim:set ts=4 sw=4 sts=4 expandtab:

View File

@@ -0,0 +1,26 @@
from Crypto.Cipher import AES
ZEROIV = "\x00"*16
def removePadding(blocksize, s):
'Remove rfc 1423 padding from string.'
n = ord(s[-1]) # last byte contains number of padding bytes
if n > blocksize or n > len(s):
raise Exception('invalid padding')
return s[:-n]
def AESdecryptCBC(data, key, iv=ZEROIV, padding=False):
if len(data) % 16:
print "AESdecryptCBC: data length not /16, truncating"
data = data[0:(len(data)/16) * 16]
data = AES.new(key, AES.MODE_CBC, iv).decrypt(data)
if padding:
return removePadding(16, data)
return data
def AESencryptCBC(data, key, iv=ZEROIV, padding=False):
if len(data) % 16:
print "AESdecryptCBC: data length not /16, truncating"
data = data[0:(len(data)/16) * 16]
data = AES.new(key, AES.MODE_CBC, iv).encrypt(data)
return data

View File

@@ -0,0 +1,70 @@
import struct
from Crypto.Cipher import AES
"""
http://www.ietf.org/rfc/rfc3394.txt
quick'n'dirty AES wrap implementation
used by iOS 4 KeyStore kernel extension for wrapping/unwrapping encryption keys
"""
def unpack64bit(s):
return struct.unpack(">Q",s)[0]
def pack64bit(s):
return struct.pack(">Q",s)
def AESUnwrap(kek, wrapped):
C = []
for i in xrange(len(wrapped)/8):
C.append(unpack64bit(wrapped[i*8:i*8+8]))
n = len(C) - 1
R = [0] * (n+1)
A = C[0]
for i in xrange(1,n+1):
R[i] = C[i]
for j in reversed(xrange(0,6)):
for i in reversed(xrange(1,n+1)):
todec = pack64bit(A ^ (n*j+i))
todec += pack64bit(R[i])
B = AES.new(kek).decrypt(todec)
A = unpack64bit(B[:8])
R[i] = unpack64bit(B[8:])
#assert A == 0xa6a6a6a6a6a6a6a6, "AESUnwrap: integrity check FAIL, wrong kek ?"
if A != 0xa6a6a6a6a6a6a6a6:
#print "AESUnwrap: integrity check FAIL, wrong kek ?"
return None
res = "".join(map(pack64bit, R[1:]))
return res
def AESwrap(kek, data):
A = 0xa6a6a6a6a6a6a6a6
R = [0]
for i in xrange(len(data)/8):
R.append(unpack64bit(data[i*8:i*8+8]))
n = len(R) - 1
for j in xrange(0,6):
for i in xrange(1,n+1):
B = AES.new(kek).encrypt(pack64bit(A) + pack64bit(R[i]))
A = unpack64bit(B[:8]) ^ (n*j+i)
R[i] = unpack64bit(B[8:])
res = pack64bit(A) + "".join(map(pack64bit, R[1:]))
return res
if __name__ == "__main__":
#format (kek, data, expected_ciphertext)
test_vectors = [
("000102030405060708090A0B0C0D0E0F", "00112233445566778899AABBCCDDEEFF", "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5"),
("000102030405060708090A0B0C0D0E0F1011121314151617", "00112233445566778899AABBCCDDEEFF", "96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D"),
("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF", "64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7"),
("000102030405060708090A0B0C0D0E0F1011121314151617", "00112233445566778899AABBCCDDEEFF0001020304050607", "031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2"),
("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF0001020304050607", "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1"),
("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F", "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21")
]
for kek, data, expected in test_vectors:
ciphertext = AESwrap(kek.decode("hex"), data.decode("hex"))
assert ciphertext == expected.decode("hex")
assert AESUnwrap(kek.decode("hex"), ciphertext) == data.decode("hex")
print "All tests OK !"

View File

@@ -0,0 +1,74 @@
from Crypto.Util import number
CURVE_P = (2**255 - 19)
CURVE_A = 121665
def curve25519_monty(x1, z1, x2, z2, qmqp):
a = (x1 + z1) * (x2 - z2) % CURVE_P
b = (x1 - z1) * (x2 + z2) % CURVE_P
x4 = (a + b) * (a + b) % CURVE_P
e = (a - b) * (a - b) % CURVE_P
z4 = e * qmqp % CURVE_P
a = (x1 + z1) * (x1 + z1) % CURVE_P
b = (x1 - z1) * (x1 - z1) % CURVE_P
x3 = a * b % CURVE_P
g = (a - b) % CURVE_P
h = (a + CURVE_A * g) % CURVE_P
z3 = (g * h) % CURVE_P
return x3, z3, x4, z4
def curve25519_mult(n, q):
nqpqx, nqpqz = q, 1
nqx, nqz = 1, 0
for i in range(255, -1, -1):
if (n >> i) & 1:
nqpqx,nqpqz,nqx,nqz = curve25519_monty(nqpqx, nqpqz, nqx, nqz, q)
else:
nqx,nqz,nqpqx,nqpqz = curve25519_monty(nqx, nqz, nqpqx, nqpqz, q)
return nqx, nqz
def curve25519(secret, basepoint):
a = ord(secret[0])
a &= 248
b = ord(secret[31])
b &= 127
b |= 64
s = chr(a) + secret[1:-1] + chr(b)
s = number.bytes_to_long(s[::-1])
basepoint = number.bytes_to_long(basepoint[::-1])
x, z = curve25519_mult(s, basepoint)
zmone = number.inverse(z, CURVE_P)
z = x * zmone % CURVE_P
return number.long_to_bytes(z)[::-1]
if __name__ == "__main__":
from crypto.aeswrap import AESUnwrap
from Crypto.Hash import SHA256
z="04000000080000000200000048000000000000000000000000000000000000000000000002917dc2542198edeb1078c4d1ebab74d9ca87890657ba02b9825dadf20a002f44360c6f87743fac0236df1f9eedbea801e31677aef3a09adfb4e10a37ae27facf419ab3ea3f39f4".decode("hex")
mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14".decode("hex")
mypublic = "b1c652786697a5feef36a56f36fde524a21193f4e563627977ab515f600fdb3a".decode("hex")
hispublic = z[36:36+32]
#c4d9fe462a2ebbf0745195ce7dc5e8b49947bbd5b42da74175d5f8125b44582b
shared = curve25519(mysecret, hispublic)
print shared.encode("hex")
h = SHA256.new()
h.update('\x00\x00\x00\x01')
h.update(shared)
h.update(hispublic)
h.update(mypublic)
md = h.digest()
#e442c81b91ea876d3cf42d3aea75f4b0c3f90f9fd045e1f5784b91260f3bdc9c
print AESUnwrap(md, z[32+36:]).encode("hex")

View File

@@ -0,0 +1,129 @@
#!/usr/bin/env python
from Crypto.Cipher import AES
from Crypto.Util import strxor
from struct import pack, unpack
def gcm_rightshift(vec):
for x in range(15, 0, -1):
c = vec[x] >> 1
c |= (vec[x-1] << 7) & 0x80
vec[x] = c
vec[0] >>= 1
return vec
def gcm_gf_mult(a, b):
mask = [ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ]
poly = [ 0x00, 0xe1 ]
Z = [0] * 16
V = [c for c in a]
for x in range(128):
if b[x >> 3] & mask[x & 7]:
Z = [V[y] ^ Z[y] for y in range(16)]
bit = V[15] & 1
V = gcm_rightshift(V)
V[0] ^= poly[bit]
return Z
def ghash(h, auth_data, data):
u = (16 - len(data)) % 16
v = (16 - len(auth_data)) % 16
x = auth_data + chr(0) * v + data + chr(0) * u
x += pack('>QQ', len(auth_data) * 8, len(data) * 8)
y = [0] * 16
vec_h = [ord(c) for c in h]
for i in range(0, len(x), 16):
block = [ord(c) for c in x[i:i+16]]
y = [y[j] ^ block[j] for j in range(16)]
y = gcm_gf_mult(y, vec_h)
return ''.join(chr(c) for c in y)
def inc32(block):
counter, = unpack('>L', block[12:])
counter += 1
return block[:12] + pack('>L', counter)
def gctr(k, icb, plaintext):
y = ''
if len(plaintext) == 0:
return y
aes = AES.new(k)
cb = icb
for i in range(0, len(plaintext), aes.block_size):
cb = inc32(cb)
encrypted = aes.encrypt(cb)
plaintext_block = plaintext[i:i+aes.block_size]
y += strxor.strxor(plaintext_block, encrypted[:len(plaintext_block)])
return y
def gcm_decrypt(k, iv, encrypted, auth_data, tag):
aes = AES.new(k)
h = aes.encrypt(chr(0) * aes.block_size)
if len(iv) == 12:
y0 = iv + "\x00\x00\x00\x01"
else:
y0 = ghash(h, '', iv)
decrypted = gctr(k, y0, encrypted)
s = ghash(h, auth_data, encrypted)
t = aes.encrypt(y0)
T = strxor.strxor(s, t)
if T != tag:
raise ValueError('Decrypted data is invalid')
else:
return decrypted
def gcm_encrypt(k, iv, plaintext, auth_data):
aes = AES.new(k)
h = aes.encrypt(chr(0) * aes.block_size)
if len(iv) == 12:
y0 = iv + "\x00\x00\x00\x01"
else:
y0 = ghash(h, '', iv)
encrypted = gctr(k, y0, plaintext)
s = ghash(h, auth_data, encrypted)
t = aes.encrypt(y0)
T = strxor.strxor(s, t)
return (encrypted, T)
def main():
#http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
k = 'AD7A2BD03EAC835A6F620FDCB506B345'.decode("hex")
p = ''
a = 'D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001'.decode("hex")
iv = '12153524C0895E81B2C28465'.decode("hex")
c, t = gcm_encrypt(k, iv, '', a)
assert c == ""
assert t == "f09478a9b09007d06f46e9b6a1da25dd".decode("hex")
k = 'AD7A2BD03EAC835A6F620FDCB506B345'.decode("hex")
p = '08000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A0002'.decode("hex")
a = 'D609B1F056637A0D46DF998D88E52E00B2C2846512153524C0895E81'.decode("hex")
iv = '12153524C0895E81B2C28465'.decode("hex")
c, t = gcm_encrypt(k, iv, p, a)
assert c == '701AFA1CC039C0D765128A665DAB69243899BF7318CCDC81C9931DA17FBE8EDD7D17CB8B4C26FC81E3284F2B7FBA713D'.decode("hex")
assert t == '4F8D55E7D3F06FD5A13C0C29B9D5B880'.decode("hex")
key = "91bfb6cbcff07b93a4c68bbfe99ac63b713f0627025c0fb1ffc5b0812dc284f8".decode("hex")
data = "020000000B00000028000000DE44D22E96B1966BAEF4CBEA8675871D40BA669401BD4EBB52AF9C025134187E70549012058456BF0EC0FA1F8FF9F822AC4312AB2141FA712E6D1482358EAC1421A1BFFA81EF38BD0BF2E52675D665EFE3C534E188F575774FAA92E74345575E370B9982661FAE8BD9243B7AD7D2105B275424C0CA1145B9D43AFF04F2747E40D62EC60563960D62A894BE66F267B14D75C0572BE60CC9B339D440FCB418D4F729BBF15C14E0D3A43E4A8B44523D8B3B0F3E7DF85AA67A707EE19CB893277D2392234D7DBC17DA4A0BD7F166189FC54C16C20D287E20FD2FB11BD2CE09ADBDABB95124CD4BFE219E34D3C80E69570A5A506555D7094916C5D75E0065F1796F556EDF0DAA1AA758E0C85AE3951BD363F26B1D43F6CBAEE12D97AD3B60CFA89C1C76BB29F2B54BE31B6CE166F4860C5E5DA92588EF53AA946DF159E60E6F05009D12FB1E37".decode("hex")
ciphertext = data[12+40:-16]
tag = data[-16:]
print repr(gcm_decrypt(key, '', ciphertext, '', tag))
if __name__ == '__main__':
main()