from __future__ import division, print_function
from collections import namedtuple as _namedtuple
from random import SystemRandom as _SystemRandom
import argparse
import getpass
import hashlib
import re
import sys
_BASE64_CHARACTERS = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
_SALT_RE = re.compile(r'\$(?P<algo>\d)\$(?:rounds=(?P<rounds>\d+)\$)?(?P<salt>.{1,16})')
_ROUNDS_DEFAULT = 5000 # As used by crypt(3)
_PY2 = sys.version_info < (3, 0, 0)
_sr = _SystemRandom()
class _Method(_namedtuple('_Method', 'name ident salt_chars total_size')):
"""Class representing a salt method per the Modular Crypt Format or the
legacy 2-character crypt method."""
def __repr__(self):
return '<crypt.METHOD_{0}>'.format(self.name)
# available salting/crypto methods
METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
methods = (
METHOD_SHA512,
METHOD_SHA256,
)
def mksalt(method=None, rounds=None):
"""Generate a salt for the specified method.
If not specified, the strongest available method will be used.
"""
if method is None:
method = methods[0]
salt = ['${0}$'.format(method.ident) if method.ident else '']
if rounds:
salt.append('rounds={0:d}$'.format(rounds))
salt.append(''.join(_sr.choice(_BASE64_CHARACTERS) for char in range(method.salt_chars)))
return ''.join(salt)
def crypt(word, salt=None, rounds=_ROUNDS_DEFAULT):
"""Return a string representing the one-way hash of a password, with a salt
prepended.
If ``salt`` is not specified or is ``None``, the strongest
available method will be selected and a salt generated. Otherwise,
``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
returned by ``crypt.mksalt()``.
"""
if salt is None or isinstance(salt, _Method):
salt = mksalt(salt, rounds)
algo, rounds, salt = extract_components_from_salt(salt)
if algo == 5:
hashfunc = hashlib.sha256
elif algo == 6:
hashfunc = hashlib.sha512
else:
raise ValueError('Unsupported algorithm, must be either 5 (sha256) or 6 (sha512)')
return sha2_crypt(word, salt, hashfunc, rounds)
def sha2_crypt(key, salt, hashfunc, rounds=_ROUNDS_DEFAULT):
"""
This algorithm is insane. History can be found at
https://e...content-available-to-author-only...a.org/wiki/Crypt_%28C%29
"""
key = key.encode('utf-8')
h = hashfunc()
alt_h = hashfunc()
digest_size = h.digest_size
key_len = len(key)
# First, feed key, salt and then key again to the alt hash
alt_h.update(key)
alt_h.update(salt.encode('utf-8'))
alt_h.update(key)
alt_result = alt_h.digest()
# Feed key and salt to the primary hash
h.update(key)
h.update(salt.encode('utf-8'))
# Feed as many (loopping) bytes from alt digest as the length of the key
for i in range(key_len//digest_size):
h.update(alt_result)
h.update(alt_result[:(key_len % digest_size)])
# Take the binary representation of the length of the key and for every
# 1 add the alternate digest, for every 0 the key
bits = key_len
while bits > 0:
if bits & 1 == 0:
h.update(key)
else:
h.update(alt_result)
bits >>= 1
# Store the results from the primary hash
alt_result = h.digest()
h = hashfunc()
# Add password for each character in the password
for i in range(key_len):
h.update(key)
temp_result = h.digest()
# Compute a P array of the bytes in temp repeated for the length of the key
p_bytes = temp_result * (key_len // digest_size)
p_bytes += temp_result[:(key_len % digest_size)]
alt_h = hashfunc()
# Add the salt 16 + arbitrary amount decided by first byte in alt digest
for i in range(16 + byte2int(alt_result[0])):
alt_h.update(salt.encode('utf-8'))
temp_result = alt_h.digest()
# Compute a S array of the bytes in temp_result repeated for the length
# of the salt
s_bytes = temp_result * (len(salt) // digest_size)
s_bytes += temp_result[:(len(salt) % digest_size)]
# Do the actual iterations
for i in range(rounds):
h = hashfunc()
# Alternate adding either the P array or the alt digest
if i & 1 != 0:
h.update(p_bytes)
else:
h.update(alt_result)
# If the round is divisible by 3, add the S array
if i % 3 != 0:
h.update(s_bytes)
# If the round is divisible by 7, add the P array
if i % 7 != 0:
h.update(p_bytes)
# Alternate adding either the P array or the alt digest, opposite
# of first step
if i & 1 != 0:
h.update(alt_result)
else:
h.update(p_bytes)
alt_result = h.digest()
# Compute the base64-ish representation of the hash
ret = []
if digest_size == 64:
# SHA-512
ret.append(b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4))
ret.append(b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4))
ret.append(b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4))
ret.append(b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4))
ret.append(b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4))
ret.append(b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4))
ret.append(b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4))
ret.append(b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4))
ret.append(b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4))
ret.append(b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4))
ret.append(b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4))
ret.append(b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4))
ret.append(b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4))
ret.append(b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4))
ret.append(b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4))
ret.append(b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4))
ret.append(b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4))
ret.append(b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4))
ret.append(b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4))
ret.append(b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4))
ret.append(b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4))
ret.append(b64_from_24bit(int2byte(0), int2byte(0), alt_result[63], 2))
else:
# SHA-256
ret.append(b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4))
ret.append(b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4))
ret.append(b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4))
ret.append(b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4))
ret.append(b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4))
ret.append(b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4))
ret.append(b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4))
ret.append(b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4))
ret.append(b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4))
ret.append(b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4))
ret.append(b64_from_24bit(int2byte(0), alt_result[31], alt_result[30], 3))
algo = 6 if digest_size == 64 else 5
if rounds == _ROUNDS_DEFAULT:
return '${0}${1}${2}'.format(algo, salt, ''.join(ret))
else:
return '${0}$rounds={1}${2}${3}'.format(algo, rounds, salt, ''.join(ret))
def byte2int(value):
if _PY2:
return ord(value)
else:
return value
def int2byte(value):
if _PY2:
return chr(value)
else:
return value
def extract_components_from_salt(salt):
salt_match = _SALT_RE.match(salt)
if salt_match:
algo, rounds, salt = salt_match.groups(_ROUNDS_DEFAULT)
algo = int(algo)
rounds = int(rounds)
else:
algo = 6
rounds = _ROUNDS_DEFAULT
return _namedtuple('Salt', 'algo rounds salt')(algo, rounds, salt)
def b64_from_24bit(b2, b1, b0, n):
b2 = byte2int(b2)
b1 = byte2int(b1)
b0 = byte2int(b0)
index = b2 << 16 | b1 << 8 | b0
ret = []
for i in range(n):
ret.append(_BASE64_CHARACTERS[index & 0x3f])
index >>= 6
return ''.join(ret)
def cli(argv=None):
parser = argparse.ArgumentParser(description='Compute a password hash for '
'SHA256/SHA512 in crypt(3)-compatible format. Password will be prompted for.')
parser.add_argument('-r', '--rounds', default=_ROUNDS_DEFAULT, type=int,
help='How many rounds of hashing to perform. More rounds are slower, making'
' it harder to reverse a hash through brute force. Default: %(default)s')
parser.add_argument('-a', '--algo', choices=('sha256', 'sha512'), default='sha512',
help='Which algorithm to use. Default: %(default)s')
parser.add_argument('-s', '--single-prompt', action='store_true',
help="Don't ask to repeat the password")
args = parser.parse_args(argv)
if not 1000 < args.rounds < 999999999:
# limits fetched from crypt(3) source
sys.stderr.write('Rounds must be between 1000 and 999999999.\n')
sys.exit(1)
if sys.stdin.isatty():
if args.single_prompt:
password = getpass.getpass('Enter password: ')
else:
password = double_prompt_for_plaintext_password()
else:
password = sys.stdin.readline().rstrip('\n')
method = METHOD_SHA256 if args.algo == 'sha256' else METHOD_SHA512
print(crypt(password, method, rounds=args.rounds), end='\n' if sys.stdout.isatty() else '')
def double_prompt_for_plaintext_password():
"""Get the desired password from the user through a double prompt."""
password = 1
password_repeat = 2
while password != password_repeat:
password = getpass.getpass('Enter password: ')
password_repeat = getpass.getpass('Repeat password: ')
if password != password_repeat:
sys.stderr.write('Passwords do not match, try again.\n')
return password
print(crypt("MySecretPassword", "$5$LgsPuaeR"))
ZnJvbSBfX2Z1dHVyZV9fIGltcG9ydCBkaXZpc2lvbiwgcHJpbnRfZnVuY3Rpb24KZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgbmFtZWR0dXBsZSBhcyBfbmFtZWR0dXBsZQpmcm9tIHJhbmRvbSBpbXBvcnQgU3lzdGVtUmFuZG9tIGFzIF9TeXN0ZW1SYW5kb20KaW1wb3J0IGFyZ3BhcnNlCmltcG9ydCBnZXRwYXNzCmltcG9ydCBoYXNobGliCmltcG9ydCByZQppbXBvcnQgc3lzCgpfQkFTRTY0X0NIQVJBQ1RFUlMgPSAnLi8wMTIzNDU2Nzg5QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eicKX1NBTFRfUkUgPSByZS5jb21waWxlKHInXCQoP1A8YWxnbz5cZClcJCg/OnJvdW5kcz0oP1A8cm91bmRzPlxkKylcJCk/KD9QPHNhbHQ+LnsxLDE2fSknKQpfUk9VTkRTX0RFRkFVTFQgPSA1MDAwICMgQXMgdXNlZCBieSBjcnlwdCgzKQpfUFkyID0gc3lzLnZlcnNpb25faW5mbyA8ICgzLCAwLCAwKQpfc3IgPSBfU3lzdGVtUmFuZG9tKCkKCgpjbGFzcyBfTWV0aG9kKF9uYW1lZHR1cGxlKCdfTWV0aG9kJywgJ25hbWUgaWRlbnQgc2FsdF9jaGFycyB0b3RhbF9zaXplJykpOgogICAgIiIiQ2xhc3MgcmVwcmVzZW50aW5nIGEgc2FsdCBtZXRob2QgcGVyIHRoZSBNb2R1bGFyIENyeXB0IEZvcm1hdCBvciB0aGUKICAgIGxlZ2FjeSAyLWNoYXJhY3RlciBjcnlwdCBtZXRob2QuIiIiCgogICAgZGVmIF9fcmVwcl9fKHNlbGYpOgogICAgICAgIHJldHVybiAnPGNyeXB0Lk1FVEhPRF97MH0+Jy5mb3JtYXQoc2VsZi5uYW1lKQoKCiMgIGF2YWlsYWJsZSBzYWx0aW5nL2NyeXB0byBtZXRob2RzCk1FVEhPRF9TSEEyNTYgPSBfTWV0aG9kKCdTSEEyNTYnLCAnNScsIDE2LCA2MykKTUVUSE9EX1NIQTUxMiA9IF9NZXRob2QoJ1NIQTUxMicsICc2JywgMTYsIDEwNikKCm1ldGhvZHMgPSAoCiAgICBNRVRIT0RfU0hBNTEyLAogICAgTUVUSE9EX1NIQTI1NiwKKQoKCmRlZiBta3NhbHQobWV0aG9kPU5vbmUsIHJvdW5kcz1Ob25lKToKICAgICIiIkdlbmVyYXRlIGEgc2FsdCBmb3IgdGhlIHNwZWNpZmllZCBtZXRob2QuCiAgICBJZiBub3Qgc3BlY2lmaWVkLCB0aGUgc3Ryb25nZXN0IGF2YWlsYWJsZSBtZXRob2Qgd2lsbCBiZSB1c2VkLgogICAgIiIiCiAgICBpZiBtZXRob2QgaXMgTm9uZToKICAgICAgICBtZXRob2QgPSBtZXRob2RzWzBdCiAgICBzYWx0ID0gWyckezB9JCcuZm9ybWF0KG1ldGhvZC5pZGVudCkgaWYgbWV0aG9kLmlkZW50IGVsc2UgJyddCiAgICBpZiByb3VuZHM6CiAgICAgICAgc2FsdC5hcHBlbmQoJ3JvdW5kcz17MDpkfSQnLmZvcm1hdChyb3VuZHMpKQogICAgc2FsdC5hcHBlbmQoJycuam9pbihfc3IuY2hvaWNlKF9CQVNFNjRfQ0hBUkFDVEVSUykgZm9yIGNoYXIgaW4gcmFuZ2UobWV0aG9kLnNhbHRfY2hhcnMpKSkKICAgIHJldHVybiAnJy5qb2luKHNhbHQpCgoKZGVmIGNyeXB0KHdvcmQsIHNhbHQ9Tm9uZSwgcm91bmRzPV9ST1VORFNfREVGQVVMVCk6CiAgICAiIiJSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvbmUtd2F5IGhhc2ggb2YgYSBwYXNzd29yZCwgd2l0aCBhIHNhbHQKICAgIHByZXBlbmRlZC4KICAgIElmIGBgc2FsdGBgIGlzIG5vdCBzcGVjaWZpZWQgb3IgaXMgYGBOb25lYGAsIHRoZSBzdHJvbmdlc3QKICAgIGF2YWlsYWJsZSBtZXRob2Qgd2lsbCBiZSBzZWxlY3RlZCBhbmQgYSBzYWx0IGdlbmVyYXRlZC4gIE90aGVyd2lzZSwKICAgIGBgc2FsdGBgIG1heSBiZSBvbmUgb2YgdGhlIGBgY3J5cHQuTUVUSE9EXypgYCB2YWx1ZXMsIG9yIGEgc3RyaW5nIGFzCiAgICByZXR1cm5lZCBieSBgYGNyeXB0Lm1rc2FsdCgpYGAuCiAgICAiIiIKICAgIGlmIHNhbHQgaXMgTm9uZSBvciBpc2luc3RhbmNlKHNhbHQsIF9NZXRob2QpOgogICAgICAgIHNhbHQgPSBta3NhbHQoc2FsdCwgcm91bmRzKQoKICAgIGFsZ28sIHJvdW5kcywgc2FsdCA9IGV4dHJhY3RfY29tcG9uZW50c19mcm9tX3NhbHQoc2FsdCkKICAgIGlmIGFsZ28gPT0gNToKICAgICAgICBoYXNoZnVuYyA9IGhhc2hsaWIuc2hhMjU2CiAgICBlbGlmIGFsZ28gPT0gNjoKICAgICAgICBoYXNoZnVuYyA9IGhhc2hsaWIuc2hhNTEyCiAgICBlbHNlOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoJ1Vuc3VwcG9ydGVkIGFsZ29yaXRobSwgbXVzdCBiZSBlaXRoZXIgNSAoc2hhMjU2KSBvciA2IChzaGE1MTIpJykKCiAgICByZXR1cm4gc2hhMl9jcnlwdCh3b3JkLCBzYWx0LCBoYXNoZnVuYywgcm91bmRzKQoKCmRlZiBzaGEyX2NyeXB0KGtleSwgc2FsdCwgaGFzaGZ1bmMsIHJvdW5kcz1fUk9VTkRTX0RFRkFVTFQpOgogICAgIiIiCiAgICBUaGlzIGFsZ29yaXRobSBpcyBpbnNhbmUuIEhpc3RvcnkgY2FuIGJlIGZvdW5kIGF0CiAgICBodHRwczovL2UuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmEub3JnL3dpa2kvQ3J5cHRfJTI4QyUyOQogICAgIiIiCiAgICBrZXkgPSBrZXkuZW5jb2RlKCd1dGYtOCcpCiAgICBoID0gaGFzaGZ1bmMoKQogICAgYWx0X2ggPSBoYXNoZnVuYygpCiAgICBkaWdlc3Rfc2l6ZSA9IGguZGlnZXN0X3NpemUKICAgIGtleV9sZW4gPSBsZW4oa2V5KQoKICAgICMgRmlyc3QsIGZlZWQga2V5LCBzYWx0IGFuZCB0aGVuIGtleSBhZ2FpbiB0byB0aGUgYWx0IGhhc2gKICAgIGFsdF9oLnVwZGF0ZShrZXkpCiAgICBhbHRfaC51cGRhdGUoc2FsdC5lbmNvZGUoJ3V0Zi04JykpCiAgICBhbHRfaC51cGRhdGUoa2V5KQogICAgYWx0X3Jlc3VsdCA9IGFsdF9oLmRpZ2VzdCgpCgogICAgIyBGZWVkIGtleSBhbmQgc2FsdCB0byB0aGUgcHJpbWFyeSBoYXNoCiAgICBoLnVwZGF0ZShrZXkpCiAgICBoLnVwZGF0ZShzYWx0LmVuY29kZSgndXRmLTgnKSkKCiAgICAjIEZlZWQgYXMgbWFueSAobG9vcHBpbmcpIGJ5dGVzIGZyb20gYWx0IGRpZ2VzdCBhcyB0aGUgbGVuZ3RoIG9mIHRoZSBrZXkKICAgIGZvciBpIGluIHJhbmdlKGtleV9sZW4vL2RpZ2VzdF9zaXplKToKICAgICAgICBoLnVwZGF0ZShhbHRfcmVzdWx0KQogICAgaC51cGRhdGUoYWx0X3Jlc3VsdFs6KGtleV9sZW4gJSBkaWdlc3Rfc2l6ZSldKQoKICAgICMgVGFrZSB0aGUgYmluYXJ5IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBsZW5ndGggb2YgdGhlIGtleSBhbmQgZm9yIGV2ZXJ5CiAgICAjIDEgYWRkIHRoZSBhbHRlcm5hdGUgZGlnZXN0LCBmb3IgZXZlcnkgMCB0aGUga2V5CiAgICBiaXRzID0ga2V5X2xlbgogICAgd2hpbGUgYml0cyA+IDA6CiAgICAgICAgaWYgYml0cyAmIDEgPT0gMDoKICAgICAgICAgICAgaC51cGRhdGUoa2V5KQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGgudXBkYXRlKGFsdF9yZXN1bHQpCiAgICAgICAgYml0cyA+Pj0gMQoKICAgICMgU3RvcmUgdGhlIHJlc3VsdHMgZnJvbSB0aGUgcHJpbWFyeSBoYXNoCiAgICBhbHRfcmVzdWx0ID0gaC5kaWdlc3QoKQoKICAgIGggPSBoYXNoZnVuYygpCgogICAgIyBBZGQgcGFzc3dvcmQgZm9yIGVhY2ggY2hhcmFjdGVyIGluIHRoZSBwYXNzd29yZAogICAgZm9yIGkgaW4gcmFuZ2Uoa2V5X2xlbik6CiAgICAgICAgaC51cGRhdGUoa2V5KQoKICAgIHRlbXBfcmVzdWx0ID0gaC5kaWdlc3QoKQoKICAgICMgQ29tcHV0ZSBhIFAgYXJyYXkgb2YgdGhlIGJ5dGVzIGluIHRlbXAgcmVwZWF0ZWQgZm9yIHRoZSBsZW5ndGggb2YgdGhlIGtleQogICAgcF9ieXRlcyA9IHRlbXBfcmVzdWx0ICogKGtleV9sZW4gLy8gZGlnZXN0X3NpemUpCiAgICBwX2J5dGVzICs9IHRlbXBfcmVzdWx0Wzooa2V5X2xlbiAlIGRpZ2VzdF9zaXplKV0KCiAgICBhbHRfaCA9IGhhc2hmdW5jKCkKCiAgICAjIEFkZCB0aGUgc2FsdCAxNiArIGFyYml0cmFyeSBhbW91bnQgZGVjaWRlZCBieSBmaXJzdCBieXRlIGluIGFsdCBkaWdlc3QKICAgIGZvciBpIGluIHJhbmdlKDE2ICsgYnl0ZTJpbnQoYWx0X3Jlc3VsdFswXSkpOgogICAgICAgIGFsdF9oLnVwZGF0ZShzYWx0LmVuY29kZSgndXRmLTgnKSkKCiAgICB0ZW1wX3Jlc3VsdCA9IGFsdF9oLmRpZ2VzdCgpCgogICAgIyBDb21wdXRlIGEgUyBhcnJheSBvZiB0aGUgYnl0ZXMgaW4gdGVtcF9yZXN1bHQgcmVwZWF0ZWQgZm9yIHRoZSBsZW5ndGgKICAgICMgb2YgdGhlIHNhbHQKICAgIHNfYnl0ZXMgPSB0ZW1wX3Jlc3VsdCAqIChsZW4oc2FsdCkgLy8gZGlnZXN0X3NpemUpCiAgICBzX2J5dGVzICs9IHRlbXBfcmVzdWx0WzoobGVuKHNhbHQpICUgZGlnZXN0X3NpemUpXQoKICAgICMgRG8gdGhlIGFjdHVhbCBpdGVyYXRpb25zCiAgICBmb3IgaSBpbiByYW5nZShyb3VuZHMpOgogICAgICAgIGggPSBoYXNoZnVuYygpCgogICAgICAgICMgQWx0ZXJuYXRlIGFkZGluZyBlaXRoZXIgdGhlIFAgYXJyYXkgb3IgdGhlIGFsdCBkaWdlc3QKICAgICAgICBpZiBpICYgMSAhPSAwOgogICAgICAgICAgICBoLnVwZGF0ZShwX2J5dGVzKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGgudXBkYXRlKGFsdF9yZXN1bHQpCgogICAgICAgICMgSWYgdGhlIHJvdW5kIGlzIGRpdmlzaWJsZSBieSAzLCBhZGQgdGhlIFMgYXJyYXkKICAgICAgICBpZiBpICUgMyAhPSAwOgogICAgICAgICAgICBoLnVwZGF0ZShzX2J5dGVzKQoKICAgICAgICAjIElmIHRoZSByb3VuZCBpcyBkaXZpc2libGUgYnkgNywgYWRkIHRoZSBQIGFycmF5CiAgICAgICAgaWYgaSAlIDcgIT0gMDoKICAgICAgICAgICAgaC51cGRhdGUocF9ieXRlcykKCiAgICAgICAgIyBBbHRlcm5hdGUgYWRkaW5nIGVpdGhlciB0aGUgUCBhcnJheSBvciB0aGUgYWx0IGRpZ2VzdCwgb3Bwb3NpdGUKICAgICAgICAjIG9mIGZpcnN0IHN0ZXAKICAgICAgICBpZiBpICYgMSAhPSAwOgogICAgICAgICAgICBoLnVwZGF0ZShhbHRfcmVzdWx0KQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGgudXBkYXRlKHBfYnl0ZXMpCgogICAgICAgIGFsdF9yZXN1bHQgPSBoLmRpZ2VzdCgpCgogICAgIyBDb21wdXRlIHRoZSBiYXNlNjQtaXNoIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBoYXNoCiAgICByZXQgPSBbXQogICAgaWYgZGlnZXN0X3NpemUgPT0gNjQ6CiAgICAgICAgIyBTSEEtNTEyCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzBdLCBhbHRfcmVzdWx0WzIxXSwgYWx0X3Jlc3VsdFs0Ml0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsyMl0sIGFsdF9yZXN1bHRbNDNdLCBhbHRfcmVzdWx0WzFdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNDRdLCBhbHRfcmVzdWx0WzJdLCBhbHRfcmVzdWx0WzIzXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzNdLCBhbHRfcmVzdWx0WzI0XSwgYWx0X3Jlc3VsdFs0NV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsyNV0sIGFsdF9yZXN1bHRbNDZdLCBhbHRfcmVzdWx0WzRdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNDddLCBhbHRfcmVzdWx0WzVdLCBhbHRfcmVzdWx0WzI2XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzZdLCBhbHRfcmVzdWx0WzI3XSwgYWx0X3Jlc3VsdFs0OF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsyOF0sIGFsdF9yZXN1bHRbNDldLCBhbHRfcmVzdWx0WzddLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNTBdLCBhbHRfcmVzdWx0WzhdLCBhbHRfcmVzdWx0WzI5XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzldLCBhbHRfcmVzdWx0WzMwXSwgYWx0X3Jlc3VsdFs1MV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFszMV0sIGFsdF9yZXN1bHRbNTJdLCBhbHRfcmVzdWx0WzEwXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzUzXSwgYWx0X3Jlc3VsdFsxMV0sIGFsdF9yZXN1bHRbMzJdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMTJdLCBhbHRfcmVzdWx0WzMzXSwgYWx0X3Jlc3VsdFs1NF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFszNF0sIGFsdF9yZXN1bHRbNTVdLCBhbHRfcmVzdWx0WzEzXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzU2XSwgYWx0X3Jlc3VsdFsxNF0sIGFsdF9yZXN1bHRbMzVdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMTVdLCBhbHRfcmVzdWx0WzM2XSwgYWx0X3Jlc3VsdFs1N10sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFszN10sIGFsdF9yZXN1bHRbNThdLCBhbHRfcmVzdWx0WzE2XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzU5XSwgYWx0X3Jlc3VsdFsxN10sIGFsdF9yZXN1bHRbMzhdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMThdLCBhbHRfcmVzdWx0WzM5XSwgYWx0X3Jlc3VsdFs2MF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs0MF0sIGFsdF9yZXN1bHRbNjFdLCBhbHRfcmVzdWx0WzE5XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzYyXSwgYWx0X3Jlc3VsdFsyMF0sIGFsdF9yZXN1bHRbNDFdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGludDJieXRlKDApLCBpbnQyYnl0ZSgwKSwgYWx0X3Jlc3VsdFs2M10sIDIpKQogICAgZWxzZToKICAgICAgICAjIFNIQS0yNTYKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMF0sIGFsdF9yZXN1bHRbMTBdLCBhbHRfcmVzdWx0WzIwXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzIxXSwgYWx0X3Jlc3VsdFsxXSwgYWx0X3Jlc3VsdFsxMV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxMl0sIGFsdF9yZXN1bHRbMjJdLCBhbHRfcmVzdWx0WzJdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbM10sIGFsdF9yZXN1bHRbMTNdLCBhbHRfcmVzdWx0WzIzXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzI0XSwgYWx0X3Jlc3VsdFs0XSwgYWx0X3Jlc3VsdFsxNF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxNV0sIGFsdF9yZXN1bHRbMjVdLCBhbHRfcmVzdWx0WzVdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNl0sIGFsdF9yZXN1bHRbMTZdLCBhbHRfcmVzdWx0WzI2XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzI3XSwgYWx0X3Jlc3VsdFs3XSwgYWx0X3Jlc3VsdFsxN10sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxOF0sIGFsdF9yZXN1bHRbMjhdLCBhbHRfcmVzdWx0WzhdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbOV0sIGFsdF9yZXN1bHRbMTldLCBhbHRfcmVzdWx0WzI5XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChpbnQyYnl0ZSgwKSwgYWx0X3Jlc3VsdFszMV0sIGFsdF9yZXN1bHRbMzBdLCAzKSkKCiAgICBhbGdvID0gNiBpZiBkaWdlc3Rfc2l6ZSA9PSA2NCBlbHNlIDUKICAgIGlmIHJvdW5kcyA9PSBfUk9VTkRTX0RFRkFVTFQ6CiAgICAgICAgcmV0dXJuICckezB9JHsxfSR7Mn0nLmZvcm1hdChhbGdvLCBzYWx0LCAnJy5qb2luKHJldCkpCiAgICBlbHNlOgogICAgICAgIHJldHVybiAnJHswfSRyb3VuZHM9ezF9JHsyfSR7M30nLmZvcm1hdChhbGdvLCByb3VuZHMsIHNhbHQsICcnLmpvaW4ocmV0KSkKCgpkZWYgYnl0ZTJpbnQodmFsdWUpOgogICAgaWYgX1BZMjoKICAgICAgICByZXR1cm4gb3JkKHZhbHVlKQogICAgZWxzZToKICAgICAgICByZXR1cm4gdmFsdWUKCgpkZWYgaW50MmJ5dGUodmFsdWUpOgogICAgaWYgX1BZMjoKICAgICAgICByZXR1cm4gY2hyKHZhbHVlKQogICAgZWxzZToKICAgICAgICByZXR1cm4gdmFsdWUKCgpkZWYgZXh0cmFjdF9jb21wb25lbnRzX2Zyb21fc2FsdChzYWx0KToKICAgIHNhbHRfbWF0Y2ggPSBfU0FMVF9SRS5tYXRjaChzYWx0KQogICAgaWYgc2FsdF9tYXRjaDoKICAgICAgICBhbGdvLCByb3VuZHMsIHNhbHQgPSBzYWx0X21hdGNoLmdyb3VwcyhfUk9VTkRTX0RFRkFVTFQpCiAgICAgICAgYWxnbyA9IGludChhbGdvKQogICAgICAgIHJvdW5kcyA9IGludChyb3VuZHMpCiAgICBlbHNlOgogICAgICAgIGFsZ28gPSA2CiAgICAgICAgcm91bmRzID0gX1JPVU5EU19ERUZBVUxUCiAgICByZXR1cm4gX25hbWVkdHVwbGUoJ1NhbHQnLCAnYWxnbyByb3VuZHMgc2FsdCcpKGFsZ28sIHJvdW5kcywgc2FsdCkKCgpkZWYgYjY0X2Zyb21fMjRiaXQoYjIsIGIxLCBiMCwgbik6CiAgICBiMiA9IGJ5dGUyaW50KGIyKQogICAgYjEgPSBieXRlMmludChiMSkKICAgIGIwID0gYnl0ZTJpbnQoYjApCiAgICBpbmRleCA9IGIyIDw8IDE2IHwgYjEgPDwgOCB8IGIwCiAgICByZXQgPSBbXQogICAgZm9yIGkgaW4gcmFuZ2Uobik6CiAgICAgICAgcmV0LmFwcGVuZChfQkFTRTY0X0NIQVJBQ1RFUlNbaW5kZXggJiAweDNmXSkKICAgICAgICBpbmRleCA+Pj0gNgogICAgcmV0dXJuICcnLmpvaW4ocmV0KQoKCmRlZiBjbGkoYXJndj1Ob25lKToKICAgIHBhcnNlciA9IGFyZ3BhcnNlLkFyZ3VtZW50UGFyc2VyKGRlc2NyaXB0aW9uPSdDb21wdXRlIGEgcGFzc3dvcmQgaGFzaCBmb3IgJwogICAgICAgICdTSEEyNTYvU0hBNTEyIGluIGNyeXB0KDMpLWNvbXBhdGlibGUgZm9ybWF0LiBQYXNzd29yZCB3aWxsIGJlIHByb21wdGVkIGZvci4nKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgnLXInLCAnLS1yb3VuZHMnLCBkZWZhdWx0PV9ST1VORFNfREVGQVVMVCwgdHlwZT1pbnQsCiAgICAgICAgaGVscD0nSG93IG1hbnkgcm91bmRzIG9mIGhhc2hpbmcgdG8gcGVyZm9ybS4gTW9yZSByb3VuZHMgYXJlIHNsb3dlciwgbWFraW5nJwogICAgICAgICcgaXQgaGFyZGVyIHRvIHJldmVyc2UgYSBoYXNoIHRocm91Z2ggYnJ1dGUgZm9yY2UuIERlZmF1bHQ6ICUoZGVmYXVsdClzJykKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoJy1hJywgJy0tYWxnbycsIGNob2ljZXM9KCdzaGEyNTYnLCAnc2hhNTEyJyksIGRlZmF1bHQ9J3NoYTUxMicsCiAgICAgICAgaGVscD0nV2hpY2ggYWxnb3JpdGhtIHRvIHVzZS4gRGVmYXVsdDogJShkZWZhdWx0KXMnKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgnLXMnLCAnLS1zaW5nbGUtcHJvbXB0JywgYWN0aW9uPSdzdG9yZV90cnVlJywKICAgICAgICBoZWxwPSJEb24ndCBhc2sgdG8gcmVwZWF0IHRoZSBwYXNzd29yZCIpCgogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKGFyZ3YpCgogICAgaWYgbm90IDEwMDAgPCBhcmdzLnJvdW5kcyA8IDk5OTk5OTk5OToKICAgICAgICAjIGxpbWl0cyBmZXRjaGVkIGZyb20gY3J5cHQoMykgc291cmNlCiAgICAgICAgc3lzLnN0ZGVyci53cml0ZSgnUm91bmRzIG11c3QgYmUgYmV0d2VlbiAxMDAwIGFuZCA5OTk5OTk5OTkuXG4nKQogICAgICAgIHN5cy5leGl0KDEpCgogICAgaWYgc3lzLnN0ZGluLmlzYXR0eSgpOgogICAgICAgIGlmIGFyZ3Muc2luZ2xlX3Byb21wdDoKICAgICAgICAgICAgcGFzc3dvcmQgPSBnZXRwYXNzLmdldHBhc3MoJ0VudGVyIHBhc3N3b3JkOiAnKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHBhc3N3b3JkID0gZG91YmxlX3Byb21wdF9mb3JfcGxhaW50ZXh0X3Bhc3N3b3JkKCkKICAgIGVsc2U6CiAgICAgICAgcGFzc3dvcmQgPSBzeXMuc3RkaW4ucmVhZGxpbmUoKS5yc3RyaXAoJ1xuJykKICAgIG1ldGhvZCA9IE1FVEhPRF9TSEEyNTYgaWYgYXJncy5hbGdvID09ICdzaGEyNTYnIGVsc2UgTUVUSE9EX1NIQTUxMgogICAgcHJpbnQoY3J5cHQocGFzc3dvcmQsIG1ldGhvZCwgcm91bmRzPWFyZ3Mucm91bmRzKSwgZW5kPSdcbicgaWYgc3lzLnN0ZG91dC5pc2F0dHkoKSBlbHNlICcnKQoKCmRlZiBkb3VibGVfcHJvbXB0X2Zvcl9wbGFpbnRleHRfcGFzc3dvcmQoKToKICAgICIiIkdldCB0aGUgZGVzaXJlZCBwYXNzd29yZCBmcm9tIHRoZSB1c2VyIHRocm91Z2ggYSBkb3VibGUgcHJvbXB0LiIiIgogICAgcGFzc3dvcmQgPSAxCiAgICBwYXNzd29yZF9yZXBlYXQgPSAyCiAgICB3aGlsZSBwYXNzd29yZCAhPSBwYXNzd29yZF9yZXBlYXQ6CiAgICAgICAgcGFzc3dvcmQgPSBnZXRwYXNzLmdldHBhc3MoJ0VudGVyIHBhc3N3b3JkOiAnKQogICAgICAgIHBhc3N3b3JkX3JlcGVhdCA9IGdldHBhc3MuZ2V0cGFzcygnUmVwZWF0IHBhc3N3b3JkOiAnKQogICAgICAgIGlmIHBhc3N3b3JkICE9IHBhc3N3b3JkX3JlcGVhdDoKICAgICAgICAgICAgc3lzLnN0ZGVyci53cml0ZSgnUGFzc3dvcmRzIGRvIG5vdCBtYXRjaCwgdHJ5IGFnYWluLlxuJykKICAgIHJldHVybiBwYXNzd29yZAoKcHJpbnQoY3J5cHQoIk15U2VjcmV0UGFzc3dvcmQiLCAiJDUkTGdzUHVhZVIiKSkKICAgIA==