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()
print(ord(alt_result[0]),ord(alt_result[1]), ord(alt_result[2]),ord(alt_result[3]),ord(alt_result[4]))
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
f = temp_result * 2
print(ord(f[0]), ord(f[32]))
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
print(ord(alt_result[0]),ord(alt_result[1]),ord(alt_result[2]), ord(alt_result[3]))
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+Jy5mb3JtYXQoc2VsZi5uYW1lKQoKCiMgIGF2YWlsYWJsZSBzYWx0aW5nL2NyeXB0byBtZXRob2RzCk1FVEhPRF9TSEEyNTYgPSBfTWV0aG9kKCdTSEEyNTYnLCAnNScsIDE2LCA2MykKTUVUSE9EX1NIQTUxMiA9IF9NZXRob2QoJ1NIQTUxMicsICc2JywgMTYsIDEwNikKCm1ldGhvZHMgPSAoCiAgICBNRVRIT0RfU0hBNTEyLAogICAgTUVUSE9EX1NIQTI1NiwKKQoKCmRlZiBta3NhbHQobWV0aG9kPU5vbmUsIHJvdW5kcz1Ob25lKToKICAgICIiIkdlbmVyYXRlIGEgc2FsdCBmb3IgdGhlIHNwZWNpZmllZCBtZXRob2QuCiAgICBJZiBub3Qgc3BlY2lmaWVkLCB0aGUgc3Ryb25nZXN0IGF2YWlsYWJsZSBtZXRob2Qgd2lsbCBiZSB1c2VkLgogICAgIiIiCiAgICBpZiBtZXRob2QgaXMgTm9uZToKICAgICAgICBtZXRob2QgPSBtZXRob2RzWzBdCiAgICBzYWx0ID0gWyckezB9JCcuZm9ybWF0KG1ldGhvZC5pZGVudCkgaWYgbWV0aG9kLmlkZW50IGVsc2UgJyddCiAgICBpZiByb3VuZHM6CiAgICAgICAgc2FsdC5hcHBlbmQoJ3JvdW5kcz17MDpkfSQnLmZvcm1hdChyb3VuZHMpKQogICAgc2FsdC5hcHBlbmQoJycuam9pbihfc3IuY2hvaWNlKF9CQVNFNjRfQ0hBUkFDVEVSUykgZm9yIGNoYXIgaW4gcmFuZ2UobWV0aG9kLnNhbHRfY2hhcnMpKSkKICAgIHJldHVybiAnJy5qb2luKHNhbHQpCgoKZGVmIGNyeXB0KHdvcmQsIHNhbHQ9Tm9uZSwgcm91bmRzPV9ST1VORFNfREVGQVVMVCk6CiAgICAiIiJSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvbmUtd2F5IGhhc2ggb2YgYSBwYXNzd29yZCwgd2l0aCBhIHNhbHQKICAgIHByZXBlbmRlZC4KICAgIElmIGBgc2FsdGBgIGlzIG5vdCBzcGVjaWZpZWQgb3IgaXMgYGBOb25lYGAsIHRoZSBzdHJvbmdlc3QKICAgIGF2YWlsYWJsZSBtZXRob2Qgd2lsbCBiZSBzZWxlY3RlZCBhbmQgYSBzYWx0IGdlbmVyYXRlZC4gIE90aGVyd2lzZSwKICAgIGBgc2FsdGBgIG1heSBiZSBvbmUgb2YgdGhlIGBgY3J5cHQuTUVUSE9EXypgYCB2YWx1ZXMsIG9yIGEgc3RyaW5nIGFzCiAgICByZXR1cm5lZCBieSBgYGNyeXB0Lm1rc2FsdCgpYGAuCiAgICAiIiIKICAgIGlmIHNhbHQgaXMgTm9uZSBvciBpc2luc3RhbmNlKHNhbHQsIF9NZXRob2QpOgogICAgICAgIHNhbHQgPSBta3NhbHQoc2FsdCwgcm91bmRzKQoKICAgIGFsZ28sIHJvdW5kcywgc2FsdCA9IGV4dHJhY3RfY29tcG9uZW50c19mcm9tX3NhbHQoc2FsdCkKICAgIGlmIGFsZ28gPT0gNToKICAgICAgICBoYXNoZnVuYyA9IGhhc2hsaWIuc2hhMjU2CiAgICBlbGlmIGFsZ28gPT0gNjoKICAgICAgICBoYXNoZnVuYyA9IGhhc2hsaWIuc2hhNTEyCiAgICBlbHNlOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoJ1Vuc3VwcG9ydGVkIGFsZ29yaXRobSwgbXVzdCBiZSBlaXRoZXIgNSAoc2hhMjU2KSBvciA2IChzaGE1MTIpJykKCiAgICByZXR1cm4gc2hhMl9jcnlwdCh3b3JkLCBzYWx0LCBoYXNoZnVuYywgcm91bmRzKQoKCmRlZiBzaGEyX2NyeXB0KGtleSwgc2FsdCwgaGFzaGZ1bmMsIHJvdW5kcz1fUk9VTkRTX0RFRkFVTFQpOgogICAgIiIiCiAgICBUaGlzIGFsZ29yaXRobSBpcyBpbnNhbmUuIEhpc3RvcnkgY2FuIGJlIGZvdW5kIGF0CiAgICBodHRwczovL2UuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmEub3JnL3dpa2kvQ3J5cHRfJTI4QyUyOQogICAgIiIiCiAgICBrZXkgPSBrZXkuZW5jb2RlKCd1dGYtOCcpCiAgICBoID0gaGFzaGZ1bmMoKQogICAgYWx0X2ggPSBoYXNoZnVuYygpCiAgICBkaWdlc3Rfc2l6ZSA9IGguZGlnZXN0X3NpemUKICAgIGtleV9sZW4gPSBsZW4oa2V5KQogICAgCiAgICAgICAgIyBGaXJzdCwgZmVlZCBrZXksIHNhbHQgYW5kIHRoZW4ga2V5IGFnYWluIHRvIHRoZSBhbHQgaGFzaAogICAgYWx0X2gudXBkYXRlKGtleSkKICAgIGFsdF9oLnVwZGF0ZShzYWx0LmVuY29kZSgndXRmLTgnKSkKICAgIGFsdF9oLnVwZGF0ZShrZXkpCiAgICBhbHRfcmVzdWx0ID0gYWx0X2guZGlnZXN0KCkKICAgIAoKCgogICAgIyBGZWVkIGtleSBhbmQgc2FsdCB0byB0aGUgcHJpbWFyeSBoYXNoCiAgICBoLnVwZGF0ZShrZXkpCiAgICBoLnVwZGF0ZShzYWx0LmVuY29kZSgndXRmLTgnKSkKCiAgICAjIEZlZWQgYXMgbWFueSAobG9vcHBpbmcpIGJ5dGVzIGZyb20gYWx0IGRpZ2VzdCBhcyB0aGUgbGVuZ3RoIG9mIHRoZSBrZXkKICAgIGZvciBpIGluIHJhbmdlKGtleV9sZW4vL2RpZ2VzdF9zaXplKToKICAgICAgICBoLnVwZGF0ZShhbHRfcmVzdWx0KQogICAgaC51cGRhdGUoYWx0X3Jlc3VsdFs6KGtleV9sZW4gJSBkaWdlc3Rfc2l6ZSldKQogICAgIyBUYWtlIHRoZSBiaW5hcnkgcmVwcmVzZW50YXRpb24gb2YgdGhlIGxlbmd0aCBvZiB0aGUga2V5IGFuZCBmb3IgZXZlcnkKICAgICMgMSBhZGQgdGhlIGFsdGVybmF0ZSBkaWdlc3QsIGZvciBldmVyeSAwIHRoZSBrZXkKICAgIGJpdHMgPSBrZXlfbGVuCiAgICB3aGlsZSBiaXRzID4gMDoKICAgICAgICBpZiBiaXRzICYgMSA9PSAwOgogICAgICAgICAgICBoLnVwZGF0ZShrZXkpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgaC51cGRhdGUoYWx0X3Jlc3VsdCkKICAgICAgICBiaXRzID4+PSAxCgogICAgIyBTdG9yZSB0aGUgcmVzdWx0cyBmcm9tIHRoZSBwcmltYXJ5IGhhc2gKICAgIGFsdF9yZXN1bHQgPSBoLmRpZ2VzdCgpCiAgICAKICAgIHByaW50KG9yZChhbHRfcmVzdWx0WzBdKSxvcmQoYWx0X3Jlc3VsdFsxXSksIG9yZChhbHRfcmVzdWx0WzJdKSxvcmQoYWx0X3Jlc3VsdFszXSksb3JkKGFsdF9yZXN1bHRbNF0pKQogICAgaCA9IGhhc2hmdW5jKCkKCiAgICAjIEFkZCBwYXNzd29yZCBmb3IgZWFjaCBjaGFyYWN0ZXIgaW4gdGhlIHBhc3N3b3JkCiAgICBmb3IgaSBpbiByYW5nZShrZXlfbGVuKToKICAgICAgICBoLnVwZGF0ZShrZXkpCgogICAgdGVtcF9yZXN1bHQgPSBoLmRpZ2VzdCgpCiAgICAKCiAgICAjIENvbXB1dGUgYSBQIGFycmF5IG9mIHRoZSBieXRlcyBpbiB0ZW1wIHJlcGVhdGVkIGZvciB0aGUgbGVuZ3RoIG9mIHRoZSBrZXkKICAgIGYgPSB0ZW1wX3Jlc3VsdCAqIDIKICAgIHByaW50KG9yZChmWzBdKSwgb3JkKGZbMzJdKSkKICAgIHBfYnl0ZXMgPSB0ZW1wX3Jlc3VsdCAqIChrZXlfbGVuIC8vIGRpZ2VzdF9zaXplKQogICAgcF9ieXRlcyArPSB0ZW1wX3Jlc3VsdFs6KGtleV9sZW4gJSBkaWdlc3Rfc2l6ZSldCgogICAgYWx0X2ggPSBoYXNoZnVuYygpCgogICAgIyBBZGQgdGhlIHNhbHQgMTYgKyBhcmJpdHJhcnkgYW1vdW50IGRlY2lkZWQgYnkgZmlyc3QgYnl0ZSBpbiBhbHQgZGlnZXN0CiAgICBmb3IgaSBpbiByYW5nZSgxNiArIGJ5dGUyaW50KGFsdF9yZXN1bHRbMF0pKToKICAgICAgICBhbHRfaC51cGRhdGUoc2FsdC5lbmNvZGUoJ3V0Zi04JykpCgogICAgdGVtcF9yZXN1bHQgPSBhbHRfaC5kaWdlc3QoKQoKICAgICMgQ29tcHV0ZSBhIFMgYXJyYXkgb2YgdGhlIGJ5dGVzIGluIHRlbXBfcmVzdWx0IHJlcGVhdGVkIGZvciB0aGUgbGVuZ3RoCiAgICAjIG9mIHRoZSBzYWx0CiAgICBzX2J5dGVzID0gdGVtcF9yZXN1bHQgKiAobGVuKHNhbHQpIC8vIGRpZ2VzdF9zaXplKQogICAgc19ieXRlcyArPSB0ZW1wX3Jlc3VsdFs6KGxlbihzYWx0KSAlIGRpZ2VzdF9zaXplKV0KCiAgICAjIERvIHRoZSBhY3R1YWwgaXRlcmF0aW9ucwogICAgZm9yIGkgaW4gcmFuZ2Uocm91bmRzKToKICAgICAgICBoID0gaGFzaGZ1bmMoKQoKICAgICAgICAjIEFsdGVybmF0ZSBhZGRpbmcgZWl0aGVyIHRoZSBQIGFycmF5IG9yIHRoZSBhbHQgZGlnZXN0CiAgICAgICAgaWYgaSAmIDEgIT0gMDoKICAgICAgICAgICAgaC51cGRhdGUocF9ieXRlcykKICAgICAgICBlbHNlOgogICAgICAgICAgICBoLnVwZGF0ZShhbHRfcmVzdWx0KQoKICAgICAgICAjIElmIHRoZSByb3VuZCBpcyBkaXZpc2libGUgYnkgMywgYWRkIHRoZSBTIGFycmF5CiAgICAgICAgaWYgaSAlIDMgIT0gMDoKICAgICAgICAgICAgaC51cGRhdGUoc19ieXRlcykKCiAgICAgICAgIyBJZiB0aGUgcm91bmQgaXMgZGl2aXNpYmxlIGJ5IDcsIGFkZCB0aGUgUCBhcnJheQogICAgICAgIGlmIGkgJSA3ICE9IDA6CiAgICAgICAgICAgIGgudXBkYXRlKHBfYnl0ZXMpCgogICAgICAgICMgQWx0ZXJuYXRlIGFkZGluZyBlaXRoZXIgdGhlIFAgYXJyYXkgb3IgdGhlIGFsdCBkaWdlc3QsIG9wcG9zaXRlCiAgICAgICAgIyBvZiBmaXJzdCBzdGVwCiAgICAgICAgaWYgaSAmIDEgIT0gMDoKICAgICAgICAgICAgaC51cGRhdGUoYWx0X3Jlc3VsdCkKICAgICAgICBlbHNlOgogICAgICAgICAgICBoLnVwZGF0ZShwX2J5dGVzKQoKICAgICAgICBhbHRfcmVzdWx0ID0gaC5kaWdlc3QoKQoKICAgICMgQ29tcHV0ZSB0aGUgYmFzZTY0LWlzaCByZXByZXNlbnRhdGlvbiBvZiB0aGUgaGFzaAogICAgcmV0ID0gW10KICAgIGlmIGRpZ2VzdF9zaXplID09IDY0OgogICAgICAgICMgU0hBLTUxMgogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFswXSwgYWx0X3Jlc3VsdFsyMV0sIGFsdF9yZXN1bHRbNDJdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMjJdLCBhbHRfcmVzdWx0WzQzXSwgYWx0X3Jlc3VsdFsxXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzQ0XSwgYWx0X3Jlc3VsdFsyXSwgYWx0X3Jlc3VsdFsyM10sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFszXSwgYWx0X3Jlc3VsdFsyNF0sIGFsdF9yZXN1bHRbNDVdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMjVdLCBhbHRfcmVzdWx0WzQ2XSwgYWx0X3Jlc3VsdFs0XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzQ3XSwgYWx0X3Jlc3VsdFs1XSwgYWx0X3Jlc3VsdFsyNl0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs2XSwgYWx0X3Jlc3VsdFsyN10sIGFsdF9yZXN1bHRbNDhdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMjhdLCBhbHRfcmVzdWx0WzQ5XSwgYWx0X3Jlc3VsdFs3XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzUwXSwgYWx0X3Jlc3VsdFs4XSwgYWx0X3Jlc3VsdFsyOV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs5XSwgYWx0X3Jlc3VsdFszMF0sIGFsdF9yZXN1bHRbNTFdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMzFdLCBhbHRfcmVzdWx0WzUyXSwgYWx0X3Jlc3VsdFsxMF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs1M10sIGFsdF9yZXN1bHRbMTFdLCBhbHRfcmVzdWx0WzMyXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzEyXSwgYWx0X3Jlc3VsdFszM10sIGFsdF9yZXN1bHRbNTRdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMzRdLCBhbHRfcmVzdWx0WzU1XSwgYWx0X3Jlc3VsdFsxM10sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs1Nl0sIGFsdF9yZXN1bHRbMTRdLCBhbHRfcmVzdWx0WzM1XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzE1XSwgYWx0X3Jlc3VsdFszNl0sIGFsdF9yZXN1bHRbNTddLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMzddLCBhbHRfcmVzdWx0WzU4XSwgYWx0X3Jlc3VsdFsxNl0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs1OV0sIGFsdF9yZXN1bHRbMTddLCBhbHRfcmVzdWx0WzM4XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzE4XSwgYWx0X3Jlc3VsdFszOV0sIGFsdF9yZXN1bHRbNjBdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNDBdLCBhbHRfcmVzdWx0WzYxXSwgYWx0X3Jlc3VsdFsxOV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFs2Ml0sIGFsdF9yZXN1bHRbMjBdLCBhbHRfcmVzdWx0WzQxXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChpbnQyYnl0ZSgwKSwgaW50MmJ5dGUoMCksIGFsdF9yZXN1bHRbNjNdLCAyKSkKICAgIGVsc2U6CiAgICAgICAgIyBTSEEtMjU2CiAgICAgICAgcHJpbnQob3JkKGFsdF9yZXN1bHRbMF0pLG9yZChhbHRfcmVzdWx0WzFdKSxvcmQoYWx0X3Jlc3VsdFsyXSksIG9yZChhbHRfcmVzdWx0WzNdKSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbMF0sIGFsdF9yZXN1bHRbMTBdLCBhbHRfcmVzdWx0WzIwXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzIxXSwgYWx0X3Jlc3VsdFsxXSwgYWx0X3Jlc3VsdFsxMV0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxMl0sIGFsdF9yZXN1bHRbMjJdLCBhbHRfcmVzdWx0WzJdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbM10sIGFsdF9yZXN1bHRbMTNdLCBhbHRfcmVzdWx0WzIzXSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzI0XSwgYWx0X3Jlc3VsdFs0XSwgYWx0X3Jlc3VsdFsxNF0sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxNV0sIGFsdF9yZXN1bHRbMjVdLCBhbHRfcmVzdWx0WzVdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbNl0sIGFsdF9yZXN1bHRbMTZdLCBhbHRfcmVzdWx0WzI2XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChhbHRfcmVzdWx0WzI3XSwgYWx0X3Jlc3VsdFs3XSwgYWx0X3Jlc3VsdFsxN10sIDQpKQogICAgICAgIHJldC5hcHBlbmQoYjY0X2Zyb21fMjRiaXQoYWx0X3Jlc3VsdFsxOF0sIGFsdF9yZXN1bHRbMjhdLCBhbHRfcmVzdWx0WzhdLCA0KSkKICAgICAgICByZXQuYXBwZW5kKGI2NF9mcm9tXzI0Yml0KGFsdF9yZXN1bHRbOV0sIGFsdF9yZXN1bHRbMTldLCBhbHRfcmVzdWx0WzI5XSwgNCkpCiAgICAgICAgcmV0LmFwcGVuZChiNjRfZnJvbV8yNGJpdChpbnQyYnl0ZSgwKSwgYWx0X3Jlc3VsdFszMV0sIGFsdF9yZXN1bHRbMzBdLCAzKSkKCiAgICBhbGdvID0gNiBpZiBkaWdlc3Rfc2l6ZSA9PSA2NCBlbHNlIDUKICAgIGlmIHJvdW5kcyA9PSBfUk9VTkRTX0RFRkFVTFQ6CiAgICAgICAgcmV0dXJuICckezB9JHsxfSR7Mn0nLmZvcm1hdChhbGdvLCBzYWx0LCAnJy5qb2luKHJldCkpCiAgICBlbHNlOgogICAgICAgIHJldHVybiAnJHswfSRyb3VuZHM9ezF9JHsyfSR7M30nLmZvcm1hdChhbGdvLCByb3VuZHMsIHNhbHQsICcnLmpvaW4ocmV0KSkKCgpkZWYgYnl0ZTJpbnQodmFsdWUpOgogICAgaWYgX1BZMjoKICAgICAgICByZXR1cm4gb3JkKHZhbHVlKQogICAgZWxzZToKICAgICAgICByZXR1cm4gdmFsdWUKCgpkZWYgaW50MmJ5dGUodmFsdWUpOgogICAgaWYgX1BZMjoKICAgICAgICByZXR1cm4gY2hyKHZhbHVlKQogICAgZWxzZToKICAgICAgICByZXR1cm4gdmFsdWUKCgpkZWYgZXh0cmFjdF9jb21wb25lbnRzX2Zyb21fc2FsdChzYWx0KToKICAgIHNhbHRfbWF0Y2ggPSBfU0FMVF9SRS5tYXRjaChzYWx0KQogICAgaWYgc2FsdF9tYXRjaDoKICAgICAgICBhbGdvLCByb3VuZHMsIHNhbHQgPSBzYWx0X21hdGNoLmdyb3VwcyhfUk9VTkRTX0RFRkFVTFQpCiAgICAgICAgYWxnbyA9IGludChhbGdvKQogICAgICAgIHJvdW5kcyA9IGludChyb3VuZHMpCiAgICBlbHNlOgogICAgICAgIGFsZ28gPSA2CiAgICAgICAgcm91bmRzID0gX1JPVU5EU19ERUZBVUxUCiAgICByZXR1cm4gX25hbWVkdHVwbGUoJ1NhbHQnLCAnYWxnbyByb3VuZHMgc2FsdCcpKGFsZ28sIHJvdW5kcywgc2FsdCkKCgpkZWYgYjY0X2Zyb21fMjRiaXQoYjIsIGIxLCBiMCwgbik6CiAgICBiMiA9IGJ5dGUyaW50KGIyKQogICAgYjEgPSBieXRlMmludChiMSkKICAgIGIwID0gYnl0ZTJpbnQoYjApCiAgICBpbmRleCA9IGIyIDw8IDE2IHwgYjEgPDwgOCB8IGIwCiAgICByZXQgPSBbXQogICAgZm9yIGkgaW4gcmFuZ2Uobik6CiAgICAgICAgcmV0LmFwcGVuZChfQkFTRTY0X0NIQVJBQ1RFUlNbaW5kZXggJiAweDNmXSkKICAgICAgICBpbmRleCA+Pj0gNgogICAgcmV0dXJuICcnLmpvaW4ocmV0KQoKCmRlZiBjbGkoYXJndj1Ob25lKToKICAgIHBhcnNlciA9IGFyZ3BhcnNlLkFyZ3VtZW50UGFyc2VyKGRlc2NyaXB0aW9uPSdDb21wdXRlIGEgcGFzc3dvcmQgaGFzaCBmb3IgJwogICAgICAgICdTSEEyNTYvU0hBNTEyIGluIGNyeXB0KDMpLWNvbXBhdGlibGUgZm9ybWF0LiBQYXNzd29yZCB3aWxsIGJlIHByb21wdGVkIGZvci4nKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgnLXInLCAnLS1yb3VuZHMnLCBkZWZhdWx0PV9ST1VORFNfREVGQVVMVCwgdHlwZT1pbnQsCiAgICAgICAgaGVscD0nSG93IG1hbnkgcm91bmRzIG9mIGhhc2hpbmcgdG8gcGVyZm9ybS4gTW9yZSByb3VuZHMgYXJlIHNsb3dlciwgbWFraW5nJwogICAgICAgICcgaXQgaGFyZGVyIHRvIHJldmVyc2UgYSBoYXNoIHRocm91Z2ggYnJ1dGUgZm9yY2UuIERlZmF1bHQ6ICUoZGVmYXVsdClzJykKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoJy1hJywgJy0tYWxnbycsIGNob2ljZXM9KCdzaGEyNTYnLCAnc2hhNTEyJyksIGRlZmF1bHQ9J3NoYTUxMicsCiAgICAgICAgaGVscD0nV2hpY2ggYWxnb3JpdGhtIHRvIHVzZS4gRGVmYXVsdDogJShkZWZhdWx0KXMnKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgnLXMnLCAnLS1zaW5nbGUtcHJvbXB0JywgYWN0aW9uPSdzdG9yZV90cnVlJywKICAgICAgICBoZWxwPSJEb24ndCBhc2sgdG8gcmVwZWF0IHRoZSBwYXNzd29yZCIpCgogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKGFyZ3YpCgogICAgaWYgbm90IDEwMDAgPCBhcmdzLnJvdW5kcyA8IDk5OTk5OTk5OToKICAgICAgICAjIGxpbWl0cyBmZXRjaGVkIGZyb20gY3J5cHQoMykgc291cmNlCiAgICAgICAgc3lzLnN0ZGVyci53cml0ZSgnUm91bmRzIG11c3QgYmUgYmV0d2VlbiAxMDAwIGFuZCA5OTk5OTk5OTkuXG4nKQogICAgICAgIHN5cy5leGl0KDEpCgogICAgaWYgc3lzLnN0ZGluLmlzYXR0eSgpOgogICAgICAgIGlmIGFyZ3Muc2luZ2xlX3Byb21wdDoKICAgICAgICAgICAgcGFzc3dvcmQgPSBnZXRwYXNzLmdldHBhc3MoJ0VudGVyIHBhc3N3b3JkOiAnKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHBhc3N3b3JkID0gZG91YmxlX3Byb21wdF9mb3JfcGxhaW50ZXh0X3Bhc3N3b3JkKCkKICAgIGVsc2U6CiAgICAgICAgcGFzc3dvcmQgPSBzeXMuc3RkaW4ucmVhZGxpbmUoKS5yc3RyaXAoJ1xuJykKICAgIG1ldGhvZCA9IE1FVEhPRF9TSEEyNTYgaWYgYXJncy5hbGdvID09ICdzaGEyNTYnIGVsc2UgTUVUSE9EX1NIQTUxMgogICAgcHJpbnQoY3J5cHQocGFzc3dvcmQsIG1ldGhvZCwgcm91bmRzPWFyZ3Mucm91bmRzKSwgZW5kPSdcbicgaWYgc3lzLnN0ZG91dC5pc2F0dHkoKSBlbHNlICcnKQoKCmRlZiBkb3VibGVfcHJvbXB0X2Zvcl9wbGFpbnRleHRfcGFzc3dvcmQoKToKICAgICIiIkdldCB0aGUgZGVzaXJlZCBwYXNzd29yZCBmcm9tIHRoZSB1c2VyIHRocm91Z2ggYSBkb3VibGUgcHJvbXB0LiIiIgogICAgcGFzc3dvcmQgPSAxCiAgICBwYXNzd29yZF9yZXBlYXQgPSAyCiAgICB3aGlsZSBwYXNzd29yZCAhPSBwYXNzd29yZF9yZXBlYXQ6CiAgICAgICAgcGFzc3dvcmQgPSBnZXRwYXNzLmdldHBhc3MoJ0VudGVyIHBhc3N3b3JkOiAnKQogICAgICAgIHBhc3N3b3JkX3JlcGVhdCA9IGdldHBhc3MuZ2V0cGFzcygnUmVwZWF0IHBhc3N3b3JkOiAnKQogICAgICAgIGlmIHBhc3N3b3JkICE9IHBhc3N3b3JkX3JlcGVhdDoKICAgICAgICAgICAgc3lzLnN0ZGVyci53cml0ZSgnUGFzc3dvcmRzIGRvIG5vdCBtYXRjaCwgdHJ5IGFnYWluLlxuJykKICAgIHJldHVybiBwYXNzd29yZAoKcHJpbnQoY3J5cHQoIk15U2VjcmV0UGFzc3dvcmQiLCAiJDUkTGdzUHVhZVIiKSkKICAgIA==