This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
authentic2-idp-ltpa/authentic2_idp_ltpa/utils.py

100 lines
3.4 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
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
if __name__ == '__main__':
import argparse
import datetime
parser = argparse.ArgumentParser(description='Process some integers.')
secret_arg = parser.add_argument('--secret',
help='secret as plain, hex or base-64 encoded string, prefix '
'with hex: or b64: for encoded strings')
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('user', help='user\'s username')
# create the parser for the "b" command
parser_parse = subparsers.add_parser('parse', help='b help')
parser_parse.set_defaults(command='parse')
parser_parse.add_argument('token', help='the LTPA cookie content')
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':
if not args.secret:
raise argparse.ArgumentError(secret_arg,
'is required to generate a token')
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)