diff --git a/venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py b/venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py new file mode 100644 index 0000000..c71873a --- /dev/null +++ b/venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py @@ -0,0 +1,281 @@ +""" +passlib.utils.scrypt -- scrypt hash frontend and help utilities + +XXX: add this module to public docs? +""" +#========================================================================== +# imports +#========================================================================== +from __future__ import absolute_import +# core +import logging; log = logging.getLogger(__name__) +from warnings import warn +# pkg +from passlib import exc +from passlib.utils import to_bytes +from passlib.utils.compat import PYPY +# local +__all__ =[ + "validate", + "scrypt", +] + +#========================================================================== +# config validation +#========================================================================== + +#: internal global constant for setting stdlib scrypt's maxmem (int bytes). +#: set to -1 to auto-calculate (see _load_stdlib_backend() below) +#: set to 0 for openssl default (32mb according to python docs) +#: TODO: standardize this across backends, and expose support via scrypt hash config; +#: currently not very configurable, and only applies to stdlib backend. +SCRYPT_MAXMEM = -1 + +#: max output length in bytes +MAX_KEYLEN = ((1 << 32) - 1) * 32 + +#: max ``r * p`` limit +MAX_RP = (1 << 30) - 1 + +# TODO: unittests for this function +def validate(n, r, p): + """ + helper which validates a set of scrypt config parameters. + scrypt will take ``O(n * r * p)`` time and ``O(n * r)`` memory. + limitations are that ``n = 2**``, ``n < 2**(16*r)``, ``r * p < 2 ** 30``. + + :param n: scrypt rounds + :param r: scrypt block size + :param p: scrypt parallel factor + """ + if r < 1: + raise ValueError("r must be > 0: r=%r" % r) + + if p < 1: + raise ValueError("p must be > 0: p=%r" % p) + + if r * p > MAX_RP: + # pbkdf2-hmac-sha256 limitation - it will be requested to generate ``p*(2*r)*64`` bytes, + # but pbkdf2 can do max of (2**31-1) blocks, and sha-256 has 32 byte block size... + # so ``(2**31-1)*32 >= p*r*128`` -> ``r*p < 2**30`` + raise ValueError("r * p must be < 2**30: r=%r, p=%r" % (r,p)) + + if n < 2 or n & (n - 1): + raise ValueError("n must be > 1, and a power of 2: n=%r" % n) + + return True + + +UINT32_SIZE = 4 + + +def estimate_maxmem(n, r, p, fudge=1.05): + """ + calculate memory required for parameter combination. + assumes parameters have already been validated. + + .. warning:: + this is derived from OpenSSL's scrypt maxmem formula; + and may not be correct for other implementations + (additional buffers, different parallelism tradeoffs, etc). + """ + # XXX: expand to provide upper bound for diff backends, or max across all of them? + # NOTE: openssl's scrypt() enforces it's maxmem parameter based on calc located at + # , ending in line containing "Blen + Vlen > maxmem" + # using the following formula: + # Blen = p * 128 * r + # Vlen = 32 * r * (N + 2) * sizeof(uint32_t) + # total_bytes = Blen + Vlen + maxmem = r * (128 * p + 32 * (n + 2) * UINT32_SIZE) + # add fudge factor so we don't have off-by-one mismatch w/ openssl + maxmem = int(maxmem * fudge) + return maxmem + + +# TODO: configuration picker (may need psutil for full effect) + +#========================================================================== +# hash frontend +#========================================================================== + +#: backend function used by scrypt(), filled in by _set_backend() +_scrypt = None + +#: name of backend currently in use, exposed for informational purposes. +backend = None + +def scrypt(secret, salt, n, r, p=1, keylen=32): + """run SCrypt key derivation function using specified parameters. + + :arg secret: + passphrase string (unicode is encoded to bytes using utf-8). + + :arg salt: + salt string (unicode is encoded to bytes using utf-8). + + :arg n: + integer 'N' parameter + + :arg r: + integer 'r' parameter + + :arg p: + integer 'p' parameter + + :arg keylen: + number of bytes of key to generate. + defaults to 32 (the internal block size). + + :returns: + a *keylen*-sized bytes instance + + SCrypt imposes a number of constraints on it's input parameters: + + * ``r * p < 2**30`` -- due to a limitation of PBKDF2-HMAC-SHA256. + * ``keylen < (2**32 - 1) * 32`` -- due to a limitation of PBKDF2-HMAC-SHA256. + * ``n`` must a be a power of 2, and > 1 -- internal limitation of scrypt() implementation + + :raises ValueError: if the provided parameters are invalid (see constraints above). + + .. warning:: + + Unless the third-party ``scrypt ``_ package + is installed, passlib will use a builtin pure-python implementation of scrypt, + which is *considerably* slower (and thus requires a much lower / less secure + ``n`` value in order to be usuable). Installing the :mod:`!scrypt` package + is strongly recommended. + """ + validate(n, r, p) + secret = to_bytes(secret, param="secret") + salt = to_bytes(salt, param="salt") + if keylen < 1: + raise ValueError("keylen must be at least 1") + if keylen > MAX_KEYLEN: + raise ValueError("keylen too large, must be <= %d" % MAX_KEYLEN) + return _scrypt(secret, salt, n, r, p, keylen) + + +def _load_builtin_backend(): + """ + Load pure-python scrypt implementation built into passlib. + """ + slowdown = 10 if PYPY else 100 + warn("Using builtin scrypt backend, which is %dx slower than is required " + "for adequate security. Installing scrypt support (via 'pip install scrypt') " + "is strongly recommended" % slowdown, exc.PasslibSecurityWarning) + from ._builtin import ScryptEngine + return ScryptEngine.execute + + +def _load_cffi_backend(): + """ + Try to import the ctypes-based scrypt hash function provided by the + ``scrypt ``_ package. + """ + try: + from scrypt import hash + return hash + except ImportError: + pass + # not available, but check to see if package present but outdated / not installed right + try: + import scrypt + except ImportError as err: + if "scrypt" not in str(err): + # e.g. if cffi isn't set up right + # user should try importing scrypt explicitly to diagnose problem. + warn("'scrypt' package failed to import correctly (possible installation issue?)", + exc.PasslibWarning) + # else: package just isn't installed + else: + warn("'scrypt' package is too old (lacks ``hash()`` method)", exc.PasslibWarning) + return None + + +def _load_stdlib_backend(): + """ + Attempt to load stdlib scrypt() implement and return wrapper. + Returns None if not found. + """ + try: + # new in python 3.6, if compiled with openssl >= 1.1 + from hashlib import scrypt as stdlib_scrypt + except ImportError: + return None + + def stdlib_scrypt_wrapper(secret, salt, n, r, p, keylen): + # work out appropriate "maxmem" parameter + # + # TODO: would like to enforce a single "maxmem" policy across all backends; + # and maybe expose this via scrypt hasher config. + # + # for now, since parameters should all be coming from internally-controlled sources + # (password hashes), using policy of "whatever memory the parameters needs". + # furthermore, since stdlib scrypt is only place that needs this, + # currently calculating exactly what maxmem needs to make things work for stdlib call. + # as hack, this can be overriden via SCRYPT_MAXMEM above, + # would like to formalize all of this. + maxmem = SCRYPT_MAXMEM + if maxmem < 0: + maxmem = estimate_maxmem(n, r, p) + return stdlib_scrypt(password=secret, salt=salt, n=n, r=r, p=p, dklen=keylen, + maxmem=maxmem) + + return stdlib_scrypt_wrapper + + +#: list of potential backends +backend_values = ("stdlib", "scrypt", "builtin") + +#: dict mapping backend name -> loader +_backend_loaders = dict( + stdlib=_load_stdlib_backend, + scrypt=_load_cffi_backend, # XXX: rename backend constant to "cffi"? + builtin=_load_builtin_backend, +) + + +def _set_backend(name, dryrun=False): + """ + set backend for scrypt(). if name not specified, loads first available. + + :raises ~passlib.exc.MissingBackendError: if backend can't be found + + .. note:: mainly intended to be called by unittests, and scrypt hash handler + """ + if name == "any": + return + elif name == "default": + for name in backend_values: + try: + return _set_backend(name, dryrun=dryrun) + except exc.MissingBackendError: + continue + raise exc.MissingBackendError("no scrypt backends available") + else: + loader = _backend_loaders.get(name) + if not loader: + raise ValueError("unknown scrypt backend: %r" % (name,)) + hash = loader() + if not hash: + raise exc.MissingBackendError("scrypt backend %r not available" % name) + if dryrun: + return + global _scrypt, backend + backend = name + _scrypt = hash + +# initialize backend +_set_backend("default") + + +def _has_backend(name): + try: + _set_backend(name, dryrun=True) + return True + except exc.MissingBackendError: + return False + +#========================================================================== +# eof +#========================================================================== diff --git a/venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py b/venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py new file mode 100644 index 0000000..e9bb305 --- /dev/null +++ b/venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py @@ -0,0 +1,244 @@ +"""passlib.utils.scrypt._builtin -- scrypt() kdf in pure-python""" +#========================================================================== +# imports +#========================================================================== +# core +import operator +import struct +# pkg +from passlib.utils.compat import izip +from passlib.crypto.digest import pbkdf2_hmac +from passlib.crypto.scrypt._salsa import salsa20 +# local +__all__ =[ + "ScryptEngine", +] + +#========================================================================== +# scrypt engine +#========================================================================== +class ScryptEngine(object): + """ + helper class used to run scrypt kdf, see scrypt() for frontend + + .. warning:: + this class does NO validation of the input ranges or types. + + it's not intended to be used directly, + but only as a backend for :func:`passlib.utils.scrypt.scrypt()`. + """ + #================================================================= + # instance attrs + #================================================================= + + # primary scrypt config parameters + n = 0 + r = 0 + p = 0 + + # derived values & objects + smix_bytes = 0 + iv_bytes = 0 + bmix_len = 0 + bmix_half_len = 0 + bmix_struct = None + integerify = None + + #================================================================= + # frontend + #================================================================= + @classmethod + def execute(cls, secret, salt, n, r, p, keylen): + """create engine & run scrypt() hash calculation""" + return cls(n, r, p).run(secret, salt, keylen) + + #================================================================= + # init + #================================================================= + def __init__(self, n, r, p): + # store config + self.n = n + self.r = r + self.p = p + self.smix_bytes = r << 7 # num bytes in smix input - 2*r*16*4 + self.iv_bytes = self.smix_bytes * p + self.bmix_len = bmix_len = r << 5 # length of bmix block list - 32*r integers + self.bmix_half_len = r << 4 + assert struct.calcsize("I") == 4 + self.bmix_struct = struct.Struct("<" + str(bmix_len) + "I") + + # use optimized bmix for certain cases + if r == 1: + self.bmix = self._bmix_1 + + # pick best integerify function - integerify(bmix_block) should + # take last 64 bytes of block and return a little-endian integer. + # since it's immediately converted % n, we only have to extract + # the first 32 bytes if n < 2**32 - which due to the current + # internal representation, is already unpacked as a 32-bit int. + if n <= 0xFFFFffff: + integerify = operator.itemgetter(-16) + else: + assert n <= 0xFFFFffffFFFFffff + ig1 = operator.itemgetter(-16) + ig2 = operator.itemgetter(-17) + def integerify(X): + return ig1(X) | (ig2(X)<<32) + self.integerify = integerify + + #================================================================= + # frontend + #================================================================= + def run(self, secret, salt, keylen): + """ + run scrypt kdf for specified secret, salt, and keylen + + .. note:: + + * time cost is ``O(n * r * p)`` + * mem cost is ``O(n * r)`` + """ + # stretch salt into initial byte array via pbkdf2 + iv_bytes = self.iv_bytes + input = pbkdf2_hmac("sha256", secret, salt, rounds=1, keylen=iv_bytes) + + # split initial byte array into 'p' mflen-sized chunks, + # and run each chunk through smix() to generate output chunk. + smix = self.smix + if self.p == 1: + output = smix(input) + else: + # XXX: *could* use threading here, if really high p values encountered, + # but would tradeoff for more memory usage. + smix_bytes = self.smix_bytes + output = b''.join( + smix(input[offset:offset+smix_bytes]) + for offset in range(0, iv_bytes, smix_bytes) + ) + + # stretch final byte array into output via pbkdf2 + return pbkdf2_hmac("sha256", secret, output, rounds=1, keylen=keylen) + + #================================================================= + # smix() helper + #================================================================= + def smix(self, input): + """run SCrypt smix function on a single input block + + :arg input: + byte string containing input data. + interpreted as 32*r little endian 4 byte integers. + + :returns: + byte string containing output data + derived by mixing input using n & r parameters. + + .. note:: time & mem cost are both ``O(n * r)`` + """ + # gather locals + bmix = self.bmix + bmix_struct = self.bmix_struct + integerify = self.integerify + n = self.n + + # parse input into 32*r integers ('X' in scrypt source) + # mem cost -- O(r) + buffer = list(bmix_struct.unpack(input)) + + # starting with initial buffer contents, derive V s.t. + # V[0]=initial_buffer ... V[i] = bmix(V[i-1], V[i-1]) ... V[n-1] = bmix(V[n-2], V[n-2]) + # final buffer contents should equal bmix(V[n-1], V[n-1]) + # + # time cost -- O(n * r) -- n loops, bmix is O(r) + # mem cost -- O(n * r) -- V is n-element array of r-element tuples + # NOTE: could do time / memory tradeoff to shrink size of V + def vgen(): + i = 0 + while i < n: + last = tuple(buffer) + yield last + bmix(last, buffer) + i += 1 + V = list(vgen()) + + # generate result from X & V. + # + # time cost -- O(n * r) -- loops n times, calls bmix() which has O(r) time cost + # mem cost -- O(1) -- allocates nothing, calls bmix() which has O(1) mem cost + get_v_elem = V.__getitem__ + n_mask = n - 1 + i = 0 + while i < n: + j = integerify(buffer) & n_mask + result = tuple(a ^ b for a, b in izip(buffer, get_v_elem(j))) + bmix(result, buffer) + i += 1 + + # # NOTE: we could easily support arbitrary values of ``n``, not just powers of 2, + # # but very few implementations have that ability, so not enabling it for now... + # if not n_is_log_2: + # while i < n: + # j = integerify(buffer) % n + # tmp = tuple(a^b for a,b in izip(buffer, get_v_elem(j))) + # bmix(tmp,buffer) + # i += 1 + + # repack tmp + return bmix_struct.pack(*buffer) + + #================================================================= + # bmix() helper + #================================================================= + def bmix(self, source, target): + """ + block mixing function used by smix() + uses salsa20/8 core to mix block contents. + + :arg source: + source to read from. + should be list of 32*r 4-byte integers + (2*r salsa20 blocks). + + :arg target: + target to write to. + should be list with same size as source. + the existing value of this buffer is ignored. + + .. warning:: + + this operates *in place* on target, + so source & target should NOT be same list. + + .. note:: + + * time cost is ``O(r)`` -- loops 16*r times, salsa20() has ``O(1)`` cost. + + * memory cost is ``O(1)`` -- salsa20() uses 16 x uint4, + all other operations done in-place. + """ + ## assert source is not target + # Y[-1] = B[2r-1], Y[i] = hash( Y[i-1] xor B[i]) + # B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + half = self.bmix_half_len # 16*r out of 32*r - start of Y_1 + tmp = source[-16:] # 'X' in scrypt source + siter = iter(source) + j = 0 + while j < half: + jn = j+16 + target[j:jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter)) + target[half+j:half+jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter)) + j = jn + + def _bmix_1(self, source, target): + """special bmix() method optimized for ``r=1`` case""" + B = source[16:] + target[:16] = tmp = salsa20(a ^ b for a, b in izip(B, iter(source))) + target[16:] = salsa20(a ^ b for a, b in izip(tmp, B)) + + #================================================================= + # eoc + #================================================================= + +#========================================================================== +# eof +#========================================================================== diff --git a/venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py b/venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py new file mode 100644 index 0000000..55ddfae --- /dev/null +++ b/venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py @@ -0,0 +1,154 @@ +"""passlib.utils.scrypt._gen_files - meta script that generates _salsa.py""" +#========================================================================== +# imports +#========================================================================== +# core +import os +# pkg +# local +#========================================================================== +# constants +#========================================================================== + +_SALSA_OPS = [ + # row = (target idx, source idx 1, source idx 2, rotate) + # interpreted as salsa operation over uint32... + # target = (source1+source2)<> (32 - (b)))) + ##x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + ##x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + ( 4, 0, 12, 7), + ( 8, 4, 0, 9), + ( 12, 8, 4, 13), + ( 0, 12, 8, 18), + + ##x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + ##x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + ( 9, 5, 1, 7), + ( 13, 9, 5, 9), + ( 1, 13, 9, 13), + ( 5, 1, 13, 18), + + ##x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + ##x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + ( 14, 10, 6, 7), + ( 2, 14, 10, 9), + ( 6, 2, 14, 13), + ( 10, 6, 2, 18), + + ##x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + ##x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + ( 3, 15, 11, 7), + ( 7, 3, 15, 9), + ( 11, 7, 3, 13), + ( 15, 11, 7, 18), + + ##/* Operate on rows. */ + ##x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + ##x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + ( 1, 0, 3, 7), + ( 2, 1, 0, 9), + ( 3, 2, 1, 13), + ( 0, 3, 2, 18), + + ##x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + ##x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + ( 6, 5, 4, 7), + ( 7, 6, 5, 9), + ( 4, 7, 6, 13), + ( 5, 4, 7, 18), + + ##x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + ##x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + ( 11, 10, 9, 7), + ( 8, 11, 10, 9), + ( 9, 8, 11, 13), + ( 10, 9, 8, 18), + + ##x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + ##x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); + ( 12, 15, 14, 7), + ( 13, 12, 15, 9), + ( 14, 13, 12, 13), + ( 15, 14, 13, 18), +] + +def main(): + target = os.path.join(os.path.dirname(__file__), "_salsa.py") + fh = file(target, "w") + write = fh.write + + VNAMES = ["v%d" % i for i in range(16)] + + PAD = " " * 4 + PAD2 = " " * 8 + PAD3 = " " * 12 + TLIST = ", ".join("b%d" % i for i in range(16)) + VLIST = ", ".join(VNAMES) + kwds = dict( + VLIST=VLIST, + TLIST=TLIST, + ) + + write('''\ +"""passlib.utils.scrypt._salsa - salsa 20/8 core, autogenerated by _gen_salsa.py""" +#================================================================= +# salsa function +#================================================================= + +def salsa20(input): + \"""apply the salsa20/8 core to the provided input + + :args input: input list containing 16 32-bit integers + :returns: result list containing 16 32-bit integers + \""" + + %(TLIST)s = input + %(VLIST)s = \\ + %(TLIST)s + + i = 0 + while i < 4: +''' % kwds) + + for idx, (target, source1, source2, rotate) in enumerate(_SALSA_OPS): + write('''\ + # salsa op %(idx)d: [%(it)d] ^= ([%(is1)d]+[%(is2)d])<<<%(rot1)d + t = (%(src1)s + %(src2)s) & 0xffffffff + %(dst)s ^= ((t & 0x%(rmask)08x) << %(rot1)d) | (t >> %(rot2)d) + +''' % dict( + idx=idx, is1 = source1, is2=source2, it=target, + src1=VNAMES[source1], + src2=VNAMES[source2], + dst=VNAMES[target], + rmask=(1<<(32-rotate))-1, + rot1=rotate, + rot2=32-rotate, + )) + + write('''\ + i += 1 + +''') + + for idx in range(16): + write(PAD + "b%d = (b%d + v%d) & 0xffffffff\n" % (idx,idx,idx)) + + write('''\ + + return %(TLIST)s + +#================================================================= +# eof +#================================================================= +''' % kwds) + +if __name__ == "__main__": + main() + +#========================================================================== +# eof +#========================================================================== diff --git a/venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py b/venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py new file mode 100644 index 0000000..9112732 --- /dev/null +++ b/venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py @@ -0,0 +1,170 @@ +"""passlib.utils.scrypt._salsa - salsa 20/8 core, autogenerated by _gen_salsa.py""" +#================================================================= +# salsa function +#================================================================= + +def salsa20(input): + """apply the salsa20/8 core to the provided input + + :args input: input list containing 16 32-bit integers + :returns: result list containing 16 32-bit integers + """ + + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 = input + v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 = \ + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 + + i = 0 + while i < 4: + # salsa op 0: [4] ^= ([0]+[12])<<<7 + t = (v0 + v12) & 0xffffffff + v4 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 1: [8] ^= ([4]+[0])<<<9 + t = (v4 + v0) & 0xffffffff + v8 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 2: [12] ^= ([8]+[4])<<<13 + t = (v8 + v4) & 0xffffffff + v12 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 3: [0] ^= ([12]+[8])<<<18 + t = (v12 + v8) & 0xffffffff + v0 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 4: [9] ^= ([5]+[1])<<<7 + t = (v5 + v1) & 0xffffffff + v9 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 5: [13] ^= ([9]+[5])<<<9 + t = (v9 + v5) & 0xffffffff + v13 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 6: [1] ^= ([13]+[9])<<<13 + t = (v13 + v9) & 0xffffffff + v1 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 7: [5] ^= ([1]+[13])<<<18 + t = (v1 + v13) & 0xffffffff + v5 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 8: [14] ^= ([10]+[6])<<<7 + t = (v10 + v6) & 0xffffffff + v14 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 9: [2] ^= ([14]+[10])<<<9 + t = (v14 + v10) & 0xffffffff + v2 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 10: [6] ^= ([2]+[14])<<<13 + t = (v2 + v14) & 0xffffffff + v6 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 11: [10] ^= ([6]+[2])<<<18 + t = (v6 + v2) & 0xffffffff + v10 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 12: [3] ^= ([15]+[11])<<<7 + t = (v15 + v11) & 0xffffffff + v3 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 13: [7] ^= ([3]+[15])<<<9 + t = (v3 + v15) & 0xffffffff + v7 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 14: [11] ^= ([7]+[3])<<<13 + t = (v7 + v3) & 0xffffffff + v11 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 15: [15] ^= ([11]+[7])<<<18 + t = (v11 + v7) & 0xffffffff + v15 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 16: [1] ^= ([0]+[3])<<<7 + t = (v0 + v3) & 0xffffffff + v1 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 17: [2] ^= ([1]+[0])<<<9 + t = (v1 + v0) & 0xffffffff + v2 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 18: [3] ^= ([2]+[1])<<<13 + t = (v2 + v1) & 0xffffffff + v3 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 19: [0] ^= ([3]+[2])<<<18 + t = (v3 + v2) & 0xffffffff + v0 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 20: [6] ^= ([5]+[4])<<<7 + t = (v5 + v4) & 0xffffffff + v6 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 21: [7] ^= ([6]+[5])<<<9 + t = (v6 + v5) & 0xffffffff + v7 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 22: [4] ^= ([7]+[6])<<<13 + t = (v7 + v6) & 0xffffffff + v4 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 23: [5] ^= ([4]+[7])<<<18 + t = (v4 + v7) & 0xffffffff + v5 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 24: [11] ^= ([10]+[9])<<<7 + t = (v10 + v9) & 0xffffffff + v11 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 25: [8] ^= ([11]+[10])<<<9 + t = (v11 + v10) & 0xffffffff + v8 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 26: [9] ^= ([8]+[11])<<<13 + t = (v8 + v11) & 0xffffffff + v9 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 27: [10] ^= ([9]+[8])<<<18 + t = (v9 + v8) & 0xffffffff + v10 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + # salsa op 28: [12] ^= ([15]+[14])<<<7 + t = (v15 + v14) & 0xffffffff + v12 ^= ((t & 0x01ffffff) << 7) | (t >> 25) + + # salsa op 29: [13] ^= ([12]+[15])<<<9 + t = (v12 + v15) & 0xffffffff + v13 ^= ((t & 0x007fffff) << 9) | (t >> 23) + + # salsa op 30: [14] ^= ([13]+[12])<<<13 + t = (v13 + v12) & 0xffffffff + v14 ^= ((t & 0x0007ffff) << 13) | (t >> 19) + + # salsa op 31: [15] ^= ([14]+[13])<<<18 + t = (v14 + v13) & 0xffffffff + v15 ^= ((t & 0x00003fff) << 18) | (t >> 14) + + i += 1 + + b0 = (b0 + v0) & 0xffffffff + b1 = (b1 + v1) & 0xffffffff + b2 = (b2 + v2) & 0xffffffff + b3 = (b3 + v3) & 0xffffffff + b4 = (b4 + v4) & 0xffffffff + b5 = (b5 + v5) & 0xffffffff + b6 = (b6 + v6) & 0xffffffff + b7 = (b7 + v7) & 0xffffffff + b8 = (b8 + v8) & 0xffffffff + b9 = (b9 + v9) & 0xffffffff + b10 = (b10 + v10) & 0xffffffff + b11 = (b11 + v11) & 0xffffffff + b12 = (b12 + v12) & 0xffffffff + b13 = (b13 + v13) & 0xffffffff + b14 = (b14 + v14) & 0xffffffff + b15 = (b15 + v15) & 0xffffffff + + return b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 + +#================================================================= +# eof +#=================================================================