118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
import hashlib
|
|
import time
|
|
import base64
|
|
|
|
def to_hex(l):
|
|
h = hex(long(l))[2:-1]
|
|
h = '0' * (8 - len(h)) + h
|
|
return h.upper()
|
|
|
|
def decode_secret(secret):
|
|
if secret.startswith('b64:'):
|
|
secret = secret[4:].decode('base64')
|
|
elif secret.startswith('hex:'):
|
|
secret = secret[4:].decode('hex')
|
|
return secret
|
|
|
|
def generate_domino_ltpa_token(user, secret, creation=None, expire=None,
|
|
user_charset='utf8', duration=3600):
|
|
'''Generate a Domino LTPA Token for a given user'''
|
|
if creation is None:
|
|
creation = time.time()
|
|
if expire is None:
|
|
expire = creation + duration
|
|
|
|
token = ''
|
|
# header
|
|
token += '\x00\x01\x02\x03'
|
|
#
|
|
token += to_hex(creation)
|
|
token += to_hex(expire)
|
|
token += user.encode(user_charset)
|
|
h = hashlib.sha1(token)
|
|
h.update(secret)
|
|
token += h.digest()
|
|
|
|
return base64.b64encode(token)
|
|
|
|
def parse_token(token, secret=None, user_charset='utf8'):
|
|
'''Parse a Domino LTPA token'''
|
|
token = token.strip()
|
|
try:
|
|
token = base64.b64decode(token)
|
|
except TypeError:
|
|
raise AssertionError('%r is not base64' % token)
|
|
assert len(token) > 40, 'token too short: %d < 41 bytes' % len(token)
|
|
header = token[:4]
|
|
assert header == '\x00\x01\x02\x03', 'wrong token header: %r' % header
|
|
creation = int(token[4:12], 16)
|
|
expire = int(token[12:20], 16)
|
|
digest = token[-20:]
|
|
user = token[20:-20].decode(user_charset)
|
|
if secret is not None:
|
|
computed_digest = hashlib.sha1(token[:-20]+secret).digest()
|
|
assert digest == computed_digest, 'invalid digest: %r != %r' % (
|
|
digest, computed_digest)
|
|
return user, creation, expire
|
|
|
|
def main():
|
|
import argparse
|
|
import datetime
|
|
|
|
parser = argparse.ArgumentParser(description='Process some integers.')
|
|
subparsers = parser.add_subparsers(help='sub-command help')
|
|
|
|
# create the parser for the "a" command
|
|
parser_generate = subparsers.add_parser('generate', help='a help')
|
|
parser_generate.set_defaults(command='generate')
|
|
parser_generate.add_argument('--secret', required=True,
|
|
help='secret as plain, hex or base-64 encoded string, prefix '
|
|
'with hex: or b64: for encoded strings')
|
|
parser_generate.add_argument('user', help='user\'s username')
|
|
|
|
# create the parser for the "b" command
|
|
parser_parse = subparsers.add_parser('parse', help='parse an LtpaToken')
|
|
parser_parse.set_defaults(command='parse')
|
|
parser_parse.add_argument('--secret',
|
|
help='secret as plain, hex or base-64 encoded string, prefix '
|
|
'with hex: or b64: for encoded strings')
|
|
parser_parse.add_argument('token', help='the LTPA cookie content')
|
|
|
|
parser_decryptkey = subparsers.add_parser('decryptkey', help='decrypt an '
|
|
'exported shared key for LtpaToken')
|
|
parser_decryptkey.set_defaults(command='decryptkey')
|
|
parser_decryptkey.add_argument('file', help='file containing an exported '
|
|
'shared key')
|
|
parser_decryptkey.add_argument('password', help='password for the '
|
|
'encryption')
|
|
|
|
args = parser.parse_args()
|
|
if args.secret:
|
|
if args.secret.startswith('hex:'):
|
|
args.secret = args.secret[4:].decode('hex')
|
|
elif args.secret.startswith('b64:'):
|
|
args.secret = args.secret[4:].decode('base64')
|
|
else:
|
|
args.secret = args.secret
|
|
|
|
if args.command == 'generate':
|
|
print generate_domino_ltpa_token(user=args.user,
|
|
secret=args.secret)
|
|
elif args.command == 'parse':
|
|
user, creation, expire = parse_token(args.token, secret=args.secret)
|
|
def from_timestamp(t):
|
|
return datetime.datetime.utcfromtimestamp(t).isoformat() + 'Z'
|
|
print 'User:', user
|
|
print 'Creation timestamp:', from_timestamp(creation)
|
|
print 'Expire timestamp:', from_timestamp(expire)
|
|
elif args.command == 'decryptkey':
|
|
from pyDes import triple_des, ECB, PAD_PKCS5
|
|
|
|
content = file(args.file).read()
|
|
key = hashlib.sha1(args.password).digest() + '\x00' * 4
|
|
k = triple_des(key, ECB, padmode=PAD_PKCS5)
|
|
print 'hex:' + k.decrypt(content).encode('hex')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|