diff --git a/lib/pylibscrypt/__init__.py b/lib/pylibscrypt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..764f925ce0681950e07488f1843f06b9ac1ed951
--- /dev/null
+++ b/lib/pylibscrypt/__init__.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Scrypt for Python"""
+
+__version__ = '1.4.0-git'
+
+# First, try loading libscrypt
+_done = False
+try:
+    from .pylibscrypt import *
+except ImportError:
+    pass
+else:
+    _done = True
+
+# If that didn't work, try the scrypt module
+if not _done:
+    try:
+        from .pyscrypt import *
+    except ImportError:
+        pass
+    else:
+        _done = True
+
+# Next: libsodium
+if not _done:
+    try:
+        from .pylibsodium import *
+    except ImportError:
+        pass
+    else:
+        _done = True
+
+# Unless we are on pypy, we want to try libsodium_salsa as well
+if not _done:
+    import platform
+    if platform.python_implementation() != 'PyPy':
+        try:
+            from .pylibsodium_salsa import *
+        except ImportError:
+            pass
+        else:
+            _done = True
+
+# If that didn't work either, the inlined Python version
+if not _done:
+    from .pypyscrypt_inline import *
+
+__all__ = ['scrypt', 'scrypt_mcf', 'scrypt_mcf_check']
+
+
diff --git a/lib/pylibscrypt/__pycache__/__init__.cpython-34.pyc b/lib/pylibscrypt/__pycache__/__init__.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2a0a1fdebc7e9c5d9c915f308d52d6fb05536ad
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/__init__.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/common.cpython-34.pyc b/lib/pylibscrypt/__pycache__/common.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b069b1cdba0dd48a228008131151d7649bc75c5a
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/common.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/mcf.cpython-34.pyc b/lib/pylibscrypt/__pycache__/mcf.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0a4bbee9fa25919f142a01f2522c3403ecc345aa
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/mcf.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pbkdf2.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pbkdf2.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..262909f5bd77a50500b01079d8110bb6a0662e93
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pbkdf2.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pylibscrypt.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pylibscrypt.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..df0cf565d6fd9d4eb91e4cee1b23f01c29f0582c
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pylibscrypt.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pylibsodium.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pylibsodium.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..826de49d1d2925b78d0a0bd07b44bbaa1a9b383a
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pylibsodium.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pylibsodium_salsa.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pylibsodium_salsa.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fded72646b6f1543f4cc4ebf11fae7d3780657bc
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pylibsodium_salsa.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pypyscrypt_inline.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pypyscrypt_inline.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..97c3dd5027ae493e1042b7e1ed043010eb99b4a3
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pypyscrypt_inline.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/pyscrypt.cpython-34.pyc b/lib/pylibscrypt/__pycache__/pyscrypt.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0bc0e240c509da046749fa23e729c0db48d17019
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/pyscrypt.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/__pycache__/tests.cpython-34.pyc b/lib/pylibscrypt/__pycache__/tests.cpython-34.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c7edcc5d7371d01da9b17fc9369e61d12562c5f2
Binary files /dev/null and b/lib/pylibscrypt/__pycache__/tests.cpython-34.pyc differ
diff --git a/lib/pylibscrypt/bench.py b/lib/pylibscrypt/bench.py
new file mode 100644
index 0000000000000000000000000000000000000000..488dfceec32ba09d5dd1286c35d972f95c597fe9
--- /dev/null
+++ b/lib/pylibscrypt/bench.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Simple benchmark of python vs c scrypt"""
+
+import time
+
+from .common import *
+from .pylibscrypt import scrypt
+from .pypyscrypt_inline import scrypt as pyscrypt
+from .pylibsodium_salsa import scrypt as pcscrypt
+
+
+# Benchmark time in seconds
+tmin = 5
+Nmax = 20
+
+t1 = time.time()
+for i in xrange(1, Nmax+1):
+    pyscrypt(b'password', b'NaCl', N=2**i)
+    if time.time() - t1 > tmin:
+        Nmax = i
+        break
+t1 = time.time() - t1
+print('Using N = 2,4,..., 2**%d' % Nmax)
+print('Python scrypt took %.2fs' % t1)
+
+t2 = time.time()
+for i in xrange(1, Nmax+1):
+    pcscrypt(b'password', b'NaCl', N=2**i)
+t2 = time.time() - t2
+print('Py + C scrypt took %.2fs' % t2)
+
+t3 = time.time()
+for i in xrange(1, Nmax+1):
+    scrypt(b'password', b'NaCl', N=2**i)
+t3 = time.time() - t3
+print('C scrypt took      %.2fs' % t3)
+
+print('Python scrypt took %.2f times as long as C' % (t1 / t3))
+print('Py + C scrypt took %.2f times as long as C' % (t2 / t3))
+
diff --git a/lib/pylibscrypt/common.py b/lib/pylibscrypt/common.py
new file mode 100644
index 0000000000000000000000000000000000000000..241426970f1a97616971b6408e954d42769da01c
--- /dev/null
+++ b/lib/pylibscrypt/common.py
@@ -0,0 +1,61 @@
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Common variables and functions used by scrypt implementations"""
+
+import numbers
+
+
+SCRYPT_MCF_PREFIX_7 = b'$7$'
+SCRYPT_MCF_PREFIX_s1 = b'$s1$'
+SCRYPT_MCF_PREFIX_DEFAULT = b'$s1$'
+SCRYPT_MCF_PREFIX_ANY = None
+
+SCRYPT_N = 1<<14
+SCRYPT_r = 8
+SCRYPT_p = 1
+
+# The last one differs from libscrypt defaults, but matches the 'interactive'
+# work factor from the original paper. For long term storage where runtime of
+# key derivation is not a problem, you could use 16 as in libscrypt or better
+# yet increase N if memory is plentiful.
+
+xrange = xrange if 'xrange' in globals() else range
+
+def check_args(password, salt, N, r, p, olen=64):
+    if not isinstance(password, bytes):
+        raise TypeError('password must be a byte string')
+    if not isinstance(salt, bytes):
+        raise TypeError('salt must be a byte string')
+    if not isinstance(N, numbers.Integral):
+        raise TypeError('N must be an integer')
+    if not isinstance(r, numbers.Integral):
+        raise TypeError('r must be an integer')
+    if not isinstance(p, numbers.Integral):
+        raise TypeError('p must be an integer')
+    if not isinstance(olen, numbers.Integral):
+        raise TypeError('length must be an integer')
+    if N > 2**63:
+        raise ValueError('N cannot be larger than 2**63')
+    if (N & (N - 1)) or N < 2:
+        raise ValueError('N must be a power of two larger than 1')
+    if r <= 0:
+        raise ValueError('r must be positive')
+    if p <= 0:
+        raise ValueError('p must be positive')
+    if r * p >= 2**30:
+        raise ValueError('r * p must be less than 2 ** 30')
+    if olen <= 0:
+        raise ValueError('length must be positive')
+
diff --git a/lib/pylibscrypt/fuzz.py b/lib/pylibscrypt/fuzz.py
new file mode 100644
index 0000000000000000000000000000000000000000..91cd4e2b5498317af15e6a3f1dfdf24220178510
--- /dev/null
+++ b/lib/pylibscrypt/fuzz.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Fuzzes scrypt function input, comparing two implementations"""
+
+import itertools
+import random
+from random import randrange as rr
+import unittest
+
+
+class Skip(Exception):
+    pass
+
+
+class Fuzzer(object):
+    """Fuzzes function input"""
+    def __init__(self, f, args, g=None, pass_good=None, pass_bad=None):
+        self.f = f
+        self.g = g
+        self.args = args
+        self.pass_good = pass_good
+        self.pass_bad = pass_bad
+
+    @staticmethod
+    def get_random_int():
+        return int((1<<rr(66)) * 1.3)
+
+    @staticmethod
+    def get_random_bytes(lrange=None, skip=None):
+        if lrange is None:
+            v = bytearray(rr(2**rr(10)))
+        else:
+            v = bytearray(rr(*lrange))
+        for i in range(len(v)):
+            v[i] = rr(256)
+            while v[i] == skip:
+                v[i] = rr(256)
+        return bytes(v)
+
+    def get_good_args(self):
+        kwargs = {}
+        for a in self.args:
+            assert isinstance(a, dict)
+            if 'opt' in a and a['opt'] and random.randrange(2):
+                continue
+            if 'val' in a:
+                kwargs[a['name']] = a['val']
+            elif 'vals' in a:
+                kwargs[a['name']] = random.choice(a['vals'])
+            elif 'valf' in a:
+                kwargs[a['name']] = a['valf']()
+            elif 'type' in a and a['type'] == 'int':
+                kwargs[a['name']] = self.get_random_int()
+            elif 'type' in a and a['type'] == 'bytes':
+                kwargs[a['name']] = self.get_random_bytes()
+            else:
+                raise ValueError
+            if 'none' in a and not random.randrange(10):
+                kwargs[a['name']] = None
+            if 'skip' in a and a['skip'](kwargs[a['name']]):
+                if 'opt' in a and a['opt']:
+                    del kwargs[a['name']]
+                else:
+                    raise Skip
+        return kwargs
+
+    def get_bad_args(self, kwargs=None):
+        kwargs = kwargs or self.get_good_args()
+        a = random.choice(self.args)
+        if not 'opt' in a:
+            if not random.randrange(10):
+                del kwargs[a['name']]
+                return kwargs
+        if not 'type' in a:
+            return self.get_bad_args(kwargs)
+
+        if not random.randrange(10):
+            wrongtype = [
+                self.get_random_int(), self.get_random_bytes(), None,
+                1.1*self.get_random_int(), 1.0*self.get_random_int()
+            ]
+            if a['type'] == 'int':
+                del wrongtype[0]
+            elif a['type'] == 'bytes':
+                del wrongtype[1]
+            v = random.choice(wrongtype)
+            try:
+                if 'valf' in a:
+                    if a['valf'](v):
+                        return self.get_bad_args(kwargs)
+                if 'skip' in a and a['skip'](v):
+                    return self.get_bad_args(kwargs)
+            except TypeError:
+                pass # Surely bad enough
+            kwargs[a['name']] = v
+            return kwargs
+
+        if a['type'] == 'int':
+            v = self.get_random_int()
+            if 'valf' in a:
+                if a['valf'](v):
+                    return self.get_bad_args(kwargs)
+            if 'skip' in a and a['skip'](v):
+                return self.get_bad_args(kwargs)
+            kwargs[a['name']] = v
+            return kwargs
+
+        if a['type'] == 'bytes' and 'valf' in a:
+            v = self.get_random_bytes()
+            if not a['valf'](v):
+                kwargs[a['name']] = v
+                return kwargs
+
+        return self.get_bad_args(kwargs)
+
+    def fuzz_good_run(self, tc):
+        try:
+            kwargs = self.get_good_args()
+            r1 = self.f(**kwargs)
+            r2 = self.g(**kwargs) if self.g is not None else None
+            if self.g is not None:
+                r2 = self.g(**kwargs)
+        except Skip:
+            tc.skipTest('slow')
+        except Exception as e:
+            assert False, ('unexpected exception', kwargs, e)
+
+        try:
+            if self.pass_good:
+                tc.assertTrue(self.pass_good(r1, r2, kwargs),
+                              msg=('unexpected output', r1, r2, kwargs))
+            else:
+                if self.g is not None:
+                    assert r1 == r2, ('f and g mismatch', kwargs, r1, r2)
+                tc.assertTrue(r1)
+        except Exception as e:
+            print ('unexpected exception', kwargs, r1, r2, e)
+            raise
+
+    def fuzz_bad(self, f=None, kwargs=None):
+        f = f or self.f
+        kwargs = kwargs or self.get_bad_args()
+        return f(**kwargs)
+
+    def fuzz_bad_run(self, tc):
+        try:
+            kwargs = self.get_bad_args()
+        except Skip:
+            tc.skipTest('slow')
+        for f in ((self.f,) if not self.g else (self.f, self.g)):
+            try:
+                r = self.fuzz_bad(f, kwargs)
+                assert False, ('no exception', kwargs, r)
+            except Skip:
+                tc.skipTest('slow')
+            except AssertionError:
+                raise
+            except Exception:
+                pass
+
+    def testcase_good(self, tests=1, name='FuzzTestGood'):
+        testfs = {}
+        for i in range(tests):
+            testfs['test_fuzz_good_%d' % i] = lambda s: self.fuzz_good_run(s)
+        t = type(name, (unittest.TestCase,), testfs)
+        return t
+
+    def testcase_bad(self, tests=1, name='FuzzTestBad'):
+        testfs = {}
+        for i in range(tests):
+            testfs['test_fuzz_bad_%d' % i] = lambda s: self.fuzz_bad_run(s)
+        t = type(name, (unittest.TestCase,), testfs)
+        return t
+
+    def generate_tests(self, suite, count, name):
+        loader = unittest.defaultTestLoader
+        suite.addTest(
+            loader.loadTestsFromTestCase(
+                self.testcase_good(count, name + 'Good')
+            )
+        )
+        suite.addTest(
+            loader.loadTestsFromTestCase(
+                self.testcase_bad(count, name + 'Bad')
+            )
+        )
+
+
+
+if __name__ == "__main__":
+    import argparse
+
+    parser = argparse.ArgumentParser(description='Fuzz testing')
+    parser.add_argument('-c', '--count', type=int, default=100)
+    parser.add_argument('-f', '--failfast', action='store_true')
+    clargs = parser.parse_args()
+
+    modules = []
+    try:
+        from . import pylibscrypt
+        modules.append((pylibscrypt, 'pylibscrypt'))
+    except ImportError:
+        pass
+
+    try:
+        from . import pyscrypt
+        modules.append((pyscrypt, 'pyscrypt'))
+    except ImportError:
+        pass
+
+    try:
+        from . import pylibsodium_salsa
+        modules.append((pylibsodium_salsa, 'pylibsodium_salsa'))
+    except ImportError:
+        pass
+
+    try:
+        from . import pylibsodium
+        modules.append((pylibsodium, 'pylibsodium'))
+    except ImportError:
+        pass
+
+    try:
+        from . import pypyscrypt_inline as pypyscrypt
+        modules.append((pypyscrypt, 'pypyscrypt'))
+    except ImportError:
+        pass
+
+    scrypt_args = (
+        {'name':'password', 'type':'bytes'},
+        {'name':'salt', 'type':'bytes'},
+        {
+            'name':'N', 'type':'int', 'opt':False,
+            'valf':(lambda N=None: 2**rr(1,6) if N is None else
+                    1 < N < 2**64 and not (N & (N - 1))),
+            'skip':(lambda N: (N & (N - 1)) == 0 and N > 32 and N < 2**64)
+        },
+        {
+            'name':'r', 'type':'int', 'opt':True,
+            'valf':(lambda r=None: rr(1, 16) if r is None else 0<r<2**30),
+            'skip':(lambda r: r > 16 and r < 2**30)
+        },
+        {
+            'name':'p', 'type':'int', 'opt':True,
+            'valf':(lambda p=None: rr(1, 16) if p is None else 0<p<2**30),
+            'skip':(lambda p: p > 16 and p < 2**30)
+        },
+        {
+            'name':'olen', 'type':'int', 'opt':True,
+            'valf':(lambda l=None: rr(1, 1000) if l is None else l >= 0),
+            'skip':(lambda l: l < 0 or l > 1024)
+        },
+    )
+
+    scrypt_mcf_args = (
+        {
+            'name':'password', 'type':'bytes',
+            'valf':(lambda p=None: Fuzzer.get_random_bytes(skip=0) if p is None
+                    else not b'\0' in p)
+        },
+        {
+            'name':'salt', 'type':'bytes', 'opt':False, 'none':True,
+            'valf':(lambda s=None: Fuzzer.get_random_bytes((1,17)) if s is None
+                    else 1 <= len(s) <= 16)
+        },
+        {
+            'name':'N', 'type':'int', 'opt':False,
+            'valf':(lambda N=None: 2**rr(1,6) if N is None else
+                    1 < N < 2**64 and not (N & (N - 1))),
+            'skip':(lambda N: (N & (N - 1)) == 0 and N > 32 and N < 2**64)
+        },
+        {
+            'name':'r', 'type':'int', 'opt':True,
+            'valf':(lambda r=None: rr(1, 16) if r is None else 0<r<2**30),
+            'skip':(lambda r: r > 16 and r < 2**30)
+        },
+        {
+            'name':'p', 'type':'int', 'opt':True,
+            'valf':(lambda p=None: rr(1, 16) if p is None else 0<p<2**30),
+            'skip':(lambda p: p > 16 and p < 2**30)
+        },
+        {
+            'name':'prefix', 'type':'bytes', 'opt':True,
+            'vals':(b'$s1$', b'$7$'),
+            'skip':(lambda p: p is None)
+        }
+    )
+
+    random.shuffle(modules)
+    suite = unittest.TestSuite()
+    loader = unittest.defaultTestLoader
+    for m1, m2 in itertools.permutations(modules, 2):
+        Fuzzer(
+            m1[0].scrypt, scrypt_args, m2[0].scrypt,
+            pass_good=lambda r1, r2, a: (
+                isinstance(r1, bytes) and
+                (r2 is None or r1 == r2) and
+                (len(r1) == 64 if 'olen' not in a else len(r1) == a['olen'])
+            )
+        ).generate_tests(suite, clargs.count, m1[1])
+        Fuzzer(
+            m1[0].scrypt_mcf, scrypt_mcf_args, m2[0].scrypt_mcf,
+            pass_good=lambda r1, r2, a: (
+                m2[0].scrypt_mcf_check(r1, a['password']) and
+                (r2 is None or m1[0].scrypt_mcf_check(r2, a['password'])) and
+                (r2 is None or 'salt' not in a or a['salt'] is None or r1 == r2)
+            )
+        ).generate_tests(suite, clargs.count, m1[1])
+    unittest.TextTestRunner(failfast=clargs.failfast).run(suite)
+
diff --git a/lib/pylibscrypt/inline.py b/lib/pylibscrypt/inline.py
new file mode 100644
index 0000000000000000000000000000000000000000..54e8f5c06fb67a8852168968ed937d8875c36438
--- /dev/null
+++ b/lib/pylibscrypt/inline.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Inlines the salsa20 core lines into salsa20_8"""
+
+of = open('pylibscrypt/pypyscrypt_inline.py', 'w')
+assert of
+
+def indent(line):
+    i = 0
+    while i < len(line) and line[i] == ' ':
+        i += 1
+    return i
+
+with open('pylibscrypt/pypyscrypt.py', 'r') as f:
+    in_loop = False
+    loop_indent = 0
+    lc = 0
+    rl = []
+    skipping = False
+    for line in f:
+        lc += 1
+        i = indent(line)
+        if line[i:].startswith('def R('):
+            skipping = True
+        elif line[i:].startswith('def array_overwrite('):
+            skipping = True
+        elif skipping:
+            if line[i:].startswith('def'):
+                of.write(line)
+                skipping = False
+
+        elif line[i:].startswith('R('):
+            parts = line.split(';')
+            rl += parts
+            if len(rl) == 32:
+                # Interleave to reduce dependencies for pypy
+                rl1 = rl[:16]
+                rl2 = rl[16:]
+                rl1 = rl1[0::4] + rl1[1::4] + rl1[2::4] + rl1[3::4]
+                rl2 = rl2[0::4] + rl2[1::4] + rl2[2::4] + rl2[3::4]
+                rl = rl1 + rl2
+                for p, q in zip(rl[::2], rl[1::2]):
+                    pvals = p.split(',')[1:]
+                    pvals = [int(v.strip(' )\n')) for v in pvals]
+                    qvals = q.split(',')[1:]
+                    qvals = [int(v.strip(' )\n')) for v in qvals]
+                    of.write(' '*i)
+                    of.write('a = (x[%d]+x[%d]) & 0xffffffff\n' %
+                             (pvals[1], pvals[2]))
+                    of.write(' '*i)
+                    of.write('b = (x[%d]+x[%d]) & 0xffffffff\n' %
+                             (qvals[1], qvals[2]))
+                    of.write(' '*i)
+                    of.write('x[%d] ^= (a << %d) | (a >> %d)\n' %
+                             (pvals[0], pvals[3], 32 - pvals[3]))
+                    of.write(' '*i)
+                    of.write('x[%d] ^= (b << %d) | (b >> %d)\n' %
+                             (qvals[0], qvals[3], 32 - qvals[3]))
+
+        elif line[i:].startswith('array_overwrite('):
+            vals = line.split(',')
+            vals[0] = vals[0].split('(')[1]
+            vals[-1] = vals[-1].split(')')[0]
+            vals = [v.strip() for v in vals]
+            assert len(vals) == 5
+            of.write(' '*i)
+            of.write(vals[2] + '[' + vals[3] + ':(' + vals[3] + ')+(' +
+                     vals[4] + ')] = ' + vals[0] + '[' + vals[1] + ':(' +
+                     vals[1] + ')+(' + vals[4] + ')]\n')
+
+        else:
+            of.write(line)
+        if lc == 1:
+            of.write('\n# Automatically generated file, see inline.py\n')
+
diff --git a/lib/pylibscrypt/mcf.py b/lib/pylibscrypt/mcf.py
new file mode 100644
index 0000000000000000000000000000000000000000..493ac942d5e4358e72cb803ea31f5cd1c82f6a7a
--- /dev/null
+++ b/lib/pylibscrypt/mcf.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Modular Crypt Format support for scrypt
+
+Compatible with libscrypt scrypt_mcf_check also supports the $7$ format.
+
+libscrypt format:
+$s1$NNrrpp$salt$hash
+NN   - hex encoded N log2 (two hex digits)
+rr   - hex encoded r in 1-255
+pp   - hex encoded p in 1-255
+salt - base64 encoded salt 1-16 bytes decoded
+hash - base64 encoded 64-byte scrypt hash
+
+$7$ format:
+$7$Nrrrrrpppppsalt$hash
+N     - crypt base64 N log2
+rrrrr - crypt base64 r (little-endian 30 bits)
+ppppp - crypt base64 p (little-endian 30 bits)
+salt  - raw salt (0-43 bytes that should be limited to crypt base64)
+hash  - crypt base64 encoded 32-byte scrypt hash (43 bytes)
+
+(crypt base64 is base64 with the alphabet: ./0-9A-Za-z)
+
+When reading, we are more lax, allowing salts and hashes to be longer and
+incorrectly encoded, since the worst that can happen is that the password does
+not verify.
+"""
+
+
+import base64, binascii
+import os
+import struct
+
+from .common import *
+
+
+def _scrypt_mcf_encode_s1(N, r, p, salt, hash):
+    h64 = base64.b64encode(hash)
+    s64 = base64.b64encode(salt)
+
+    t = 1
+    while 2**t < N:
+        t += 1
+    params = p + (r << 8) + (t << 16)
+
+    return (
+        b'$s1' +
+        ('$%06x' % params).encode() +
+        b'$' + s64 +
+        b'$' + h64
+    )
+
+
+def _b64decode(b64):
+    for b in (b64, b64 + b'=', b64 + b'=='):
+        try:
+            return base64.b64decode(b)
+        except (TypeError, binascii.Error):
+            pass
+    raise ValueError('Incorrect base64 in MCF')
+
+
+def _scrypt_mcf_decode_s1(mcf):
+    s = mcf.split(b'$')
+    if not (mcf.startswith(b'$s1$') and len(s) == 5):
+        return None
+
+    params, s64, h64 = s[2:]
+    params = base64.b16decode(params, True)
+    salt = _b64decode(s64)
+    hash = _b64decode(h64)
+
+    if len(params) != 3:
+        raise ValueError('Unrecognized MCF parameters')
+    t, r, p = struct.unpack('3B', params)
+    N = 2 ** t
+    return N, r, p, salt, hash, len(hash)
+
+
+# Crypt base 64
+_cb64 = b'./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+_cb64a = bytearray(_cb64)
+_icb64 = (
+    [None] * 46 +
+    [
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, None, None, None, None, None,
+        None, None, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, None, None, None,
+        None, None, None, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+        50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
+    ] +
+    [None] * 133
+)
+
+
+def _cb64enc(arr):
+    arr = bytearray(arr)
+    out = bytearray()
+    val = bits = pos = 0
+    for b in arr:
+        val += b << bits
+        bits += 8
+    while bits >= 0:
+        out.append(_cb64a[val & 0x3f])
+        bits -= 6
+        val = val >> 6
+    return bytes(out)
+
+
+def _scrypt_mcf_encode_7(N, r, p, salt, hash):
+    t = 1
+    while 2**t < N:
+        t += 1
+    return (
+        b'$7$' +
+        # N
+        _cb64[t::64] +
+        # r
+        _cb64[r & 0x3f::64] + _cb64[(r >> 6) & 0x3f::64] +
+        _cb64[(r >> 12) & 0x3f::64] + _cb64[(r >> 18) & 0x3f::64] +
+        _cb64[(r >> 24) & 0x3f::64] +
+        # p
+        _cb64[p & 0x3f::64] + _cb64[(p >> 6) & 0x3f::64] +
+        _cb64[(p >> 12) & 0x3f::64] + _cb64[(p >> 18) & 0x3f::64] +
+        _cb64[(p >> 24) & 0x3f::64] +
+        # rest
+        salt +
+        b'$' + _cb64enc(hash)
+    )
+
+
+def _cb64dec(arr):
+    out = bytearray()
+    val = bits = pos = 0
+    for b in arr:
+        val += _icb64[b] << bits
+        bits += 6
+        if bits >= 8:
+            out.append(val & 0xff)
+            bits -= 8
+            val >>= 8
+    return out
+
+
+def _scrypt_mcf_decode_7(mcf):
+    s = mcf.split(b'$')
+    if not (mcf.startswith(b'$7$') and len(s) == 4):
+        return None
+
+    s64 = bytearray(s[2])
+    h64 = bytearray(s[3])
+    try:
+        N = 2 ** _icb64[s64[0]]
+        r = (_icb64[s64[1]] + (_icb64[s64[2]] << 6) + (_icb64[s64[3]] << 12) + 
+             (_icb64[s64[4]] << 18) + (_icb64[s64[5]] << 24))
+        p = (_icb64[s64[6]] + (_icb64[s64[7]] << 6) + (_icb64[s64[8]] << 12) + 
+             (_icb64[s64[9]] << 18) + (_icb64[s64[10]] << 24))
+        salt = bytes(s64[11:])
+        hash = bytes(_cb64dec(h64))
+    except (IndexError, TypeError):
+        raise ValueError('Unrecognized MCF format')
+
+    return N, r, p, salt, hash, len(hash)
+
+
+def _scrypt_mcf_7_is_standard(mcf):
+    params = _scrypt_mcf_decode_7(mcf)
+    if params is None:
+        return False
+    N, r, p, salt, hash, hlen = params
+    return len(salt) == 43 and hlen == 32
+
+
+def _scrypt_mcf_decode(mcf):
+    params = _scrypt_mcf_decode_s1(mcf)
+    if params is None:
+        params = _scrypt_mcf_decode_7(mcf)
+    if params is None:
+        raise ValueError('Unrecognized MCF hash')
+    return params
+
+
+def scrypt_mcf(scrypt, password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF given
+
+    Expects the signature:
+    scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64)
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    if salt is not None and not (1 <= len(salt) <= 16):
+        raise ValueError('salt must be 1-16 bytes')
+    if r > 255:
+        raise ValueError('scrypt_mcf r out of range [1,255]')
+    if p > 255:
+        raise ValueError('scrypt_mcf p out of range [1,255]')
+    if N > 2**31:
+        raise ValueError('scrypt_mcf N out of range [2,2**31]')
+    if b'\0' in password:
+        raise ValueError('scrypt_mcf password must not contain zero bytes')
+
+    if prefix == SCRYPT_MCF_PREFIX_s1:
+        if salt is None:
+            salt = os.urandom(16)
+        hash = scrypt(password, salt, N, r, p)
+        return _scrypt_mcf_encode_s1(N, r, p, salt, hash)
+    elif prefix == SCRYPT_MCF_PREFIX_7 or prefix == SCRYPT_MCF_PREFIX_ANY:
+        if salt is None:
+            salt = os.urandom(32)
+        salt = _cb64enc(salt)
+        hash = scrypt(password, salt, N, r, p, 32)
+        return _scrypt_mcf_encode_7(N, r, p, salt, hash)
+    else:
+        raise ValueError("Unrecognized MCF format")
+
+
+def scrypt_mcf_check(scrypt, mcf, password):
+    """Returns True if the password matches the given MCF hash
+
+    Supports both the libscrypt $s1$ format and the $7$ format.
+    """
+    if not isinstance(mcf, bytes):
+        raise TypeError
+    if not isinstance(password, bytes):
+        raise TypeError
+
+    N, r, p, salt, hash, hlen = _scrypt_mcf_decode(mcf)
+    h = scrypt(password, salt, N=N, r=r, p=p, olen=hlen)
+    return hash == h
+
diff --git a/lib/pylibscrypt/pbkdf2.py b/lib/pylibscrypt/pbkdf2.py
new file mode 100644
index 0000000000000000000000000000000000000000..bedd5d628e39e0c665c379bff2bf2b4b9c356065
--- /dev/null
+++ b/lib/pylibscrypt/pbkdf2.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""PBKDF2 in pure Python, compatible with Python3.4 hashlib.pbkdf2_hmac"""
+
+
+import hashlib
+import hmac
+import struct
+
+from .common import *
+
+
+def pbkdf2_hmac(name, password, salt, rounds, dklen=None):
+    """Returns the result of the Password-Based Key Derivation Function 2"""
+    h = hmac.new(key=password, digestmod=lambda d=b'': hashlib.new(name, d))
+    hs = h.copy()
+    hs.update(salt)
+
+    blocks = bytearray()
+    dklen = hs.digest_size if dklen is None else dklen
+    block_count, last_size = divmod(dklen, hs.digest_size)
+    block_count += last_size > 0
+
+    for block_number in xrange(1, block_count + 1):
+        hb = hs.copy()
+        hb.update(struct.pack('>L', block_number))
+        U = bytearray(hb.digest())
+
+        if rounds > 1:
+            Ui = U
+            for i in xrange(rounds - 1):
+                hi = h.copy()
+                hi.update(Ui)
+                Ui = bytearray(hi.digest())
+                for j in xrange(hs.digest_size):
+                    U[j] ^= Ui[j]
+
+        blocks.extend(U)
+
+    if last_size:
+        del blocks[dklen:]
+    return bytes(blocks)
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_pbkdf2_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pylibscrypt.py b/lib/pylibscrypt/pylibscrypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bbc3fb1b49290fc7e4ef312955cbf4ffa63cfc1
--- /dev/null
+++ b/lib/pylibscrypt/pylibscrypt.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Scrypt implementation that calls into system libscrypt"""
+
+
+import base64
+import ctypes, ctypes.util
+from ctypes import c_char_p, c_size_t, c_uint64, c_uint32
+import os
+
+from .common import *
+from . import mcf as mcf_mod
+
+
+_libscrypt_soname = ctypes.util.find_library('scrypt')
+if _libscrypt_soname is None:
+    raise ImportError('Unable to find libscrypt')
+
+try:
+    _libscrypt = ctypes.CDLL(_libscrypt_soname)
+    _libscrypt_scrypt = _libscrypt.libscrypt_scrypt
+    _libscrypt_mcf = _libscrypt.libscrypt_mcf
+    _libscrypt_check = _libscrypt.libscrypt_check
+except OSError:
+    raise ImportError('Unable to load libscrypt: ' + _libscrypt_soname)
+except AttributeError:
+    raise ImportError('Incompatible libscrypt: ' + _libscrypt_soname)
+
+_libscrypt_scrypt.argtypes = [
+    c_char_p,  # password
+    c_size_t,  # password length
+    c_char_p,  # salt
+    c_size_t,  # salt length
+    c_uint64,  # N
+    c_uint32,  # r
+    c_uint32,  # p
+    c_char_p,  # out
+    c_size_t,  # out length
+]
+
+_libscrypt_mcf.argtypes = [
+    c_uint64,  # N
+    c_uint32,  # r
+    c_uint32,  # p
+    c_char_p,  # salt
+    c_char_p,  # hash
+    c_char_p,  # out (125+ bytes)
+]
+
+_libscrypt_check.argtypes = [
+    c_char_p,  # mcf (modified)
+    c_char_p,  # hash
+]
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+    check_args(password, salt, N, r, p, olen)
+
+    out = ctypes.create_string_buffer(olen)
+    ret = _libscrypt_scrypt(password, len(password), salt, len(salt),
+                          N, r, p, out, len(out))
+    if ret:
+        raise ValueError
+
+    return out.raw
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    if (prefix != SCRYPT_MCF_PREFIX_s1 and prefix != SCRYPT_MCF_PREFIX_ANY):
+        return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+    if salt is None:
+        salt = os.urandom(16)
+    elif not (1 <= len(salt) <= 16):
+        raise ValueError('salt must be 1-16 bytes')
+    if N > 2**31:
+        raise ValueError('N > 2**31 not supported')
+    if b'\0' in password:
+        raise ValueError('scrypt_mcf password must not contain zero bytes')
+
+    hash = scrypt(password, salt, N, r, p)
+
+    h64 = base64.b64encode(hash)
+    s64 = base64.b64encode(salt)
+
+    out = ctypes.create_string_buffer(125)
+    ret = _libscrypt_mcf(N, r, p, s64, h64, out)
+    if not ret:
+        raise ValueError
+
+    out = out.raw.strip(b'\0')
+    # XXX: Hack to support old libscrypt (like in Ubuntu 14.04)
+    if len(out) == 123:
+        out = out + b'='
+
+    return out
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    if not isinstance(mcf, bytes):
+        raise TypeError
+    if not isinstance(password, bytes):
+        raise TypeError
+    if len(mcf) != 124 or b'\0' in password:
+        return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+    mcfbuf = ctypes.create_string_buffer(mcf)
+    ret = _libscrypt_check(mcfbuf, password)
+    if ret < 0:
+        return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+    return bool(ret)
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pylibsodium.py b/lib/pylibscrypt/pylibsodium.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf6949e81d6dbb837a8b827f0c7f781496132a89
--- /dev/null
+++ b/lib/pylibscrypt/pylibsodium.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Scrypt implementation that calls into system libsodium"""
+
+
+import base64
+import ctypes, ctypes.util
+from ctypes import c_char_p, c_size_t, c_uint64, c_uint32, c_void_p
+import hashlib, hmac
+import numbers
+import platform
+import struct
+import sys
+
+from . import mcf as mcf_mod
+from .common import *
+
+if platform.python_implementation() == 'PyPy':
+    from . import pypyscrypt_inline as scr_mod
+else:
+    from . import pylibsodium_salsa as scr_mod
+
+
+def _get_libsodium():
+    '''
+    Locate the nacl c libs to use
+    '''
+
+    __SONAMES = (13, 10, 5, 4)
+    # Import libsodium from system
+    sys_sodium = ctypes.util.find_library('sodium')
+    if sys_sodium is None:
+        sys_sodium = ctypes.util.find_library('libsodium')
+
+    if sys_sodium:
+        return ctypes.CDLL(sys_sodium)
+
+    # Import from local path
+    if sys.platform.startswith('win'):
+
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium')
+        except OSError:
+            pass
+        for soname_ver in __SONAMES:
+            try:
+                return ctypes.cdll.LoadLibrary(
+                    'libsodium-{0}'.format(soname_ver)
+                )
+            except OSError:
+                pass
+    elif sys.platform.startswith('darwin'):
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium.dylib')
+        except OSError:
+            pass
+    else:
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium.so')
+        except OSError:
+            pass
+
+        for soname_ver in __SONAMES:
+            try:
+                return ctypes.cdll.LoadLibrary(
+                    'libsodium.so.{0}'.format(soname_ver)
+                )
+            except OSError:
+                pass
+
+
+_lib = _get_libsodium()
+if _lib is None:
+    raise ImportError('Unable to load libsodium')
+
+try:
+    _scrypt_ll = _lib.crypto_pwhash_scryptsalsa208sha256_ll
+    _scrypt_ll.argtypes = [
+        c_void_p,  # passwd
+        c_size_t,  # passwdlen
+        c_void_p,  # salt
+        c_size_t,  # saltlen
+        c_uint64,  # N
+        c_uint32,  # r
+        c_uint32,  # p
+        c_void_p,  # buf
+        c_size_t,  # buflen
+    ]
+except AttributeError:
+    _scrypt_ll = None
+
+try:
+    _scrypt = _lib.crypto_pwhash_scryptsalsa208sha256
+    _scrypt_str = _lib.crypto_pwhash_scryptsalsa208sha256_str
+    _scrypt_str_chk = _lib.crypto_pwhash_scryptsalsa208sha256_str_verify
+    _scrypt_str_bytes = _lib.crypto_pwhash_scryptsalsa208sha256_strbytes()
+    _scrypt_salt = _lib.crypto_pwhash_scryptsalsa208sha256_saltbytes()
+    if _scrypt_str_bytes != 102 and not _scrypt_ll:
+        raise ImportError('Incompatible libsodium: ' + _lib_soname)
+except AttributeError:
+    try:
+        _scrypt = _lib.crypto_pwhash_scryptxsalsa208sha256
+        _scrypt_str = _lib.crypto_pwhash_scryptxsalsa208sha256_str
+        _scrypt_str_chk = _lib.crypto_pwhash_scryptxsalsa208sha256_str_verify
+        _scrypt_str_bytes = _lib.crypto_pwhash_scryptxsalsa208sha256_strbytes()
+        _scrypt_salt = _lib.crypto_pwhash_scryptxsalsa208sha256_saltbytes
+        _scrypt_salt = _scrypt_salt()
+        if _scrypt_str_bytes != 102 and not _scrypt_ll:
+            raise ImportError('Incompatible libsodium: ' + _lib_soname)
+    except AttributeError:
+        if not _scrypt_ll:
+            raise ImportError('Incompatible libsodium: ' + _lib_soname)
+
+_scrypt.argtypes = [
+    c_void_p,  # out
+    c_uint64,  # outlen
+    c_void_p,  # passwd
+    c_uint64,  # passwdlen
+    c_void_p,  # salt
+    c_uint64,  # opslimit
+    c_size_t,  # memlimit
+]
+
+_scrypt_str.argtypes = [
+    c_void_p,  # out (102 bytes)
+    c_void_p,  # passwd
+    c_uint64,  # passwdlen
+    c_uint64,  # opslimit
+    c_size_t,  # memlimit
+]
+
+_scrypt_str_chk.argtypes = [
+    c_char_p,  # str (102 bytes)
+    c_void_p,  # passwd
+    c_uint64,  # passwdlen
+]
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+    check_args(password, salt, N, r, p, olen)
+
+    if _scrypt_ll:
+        out = ctypes.create_string_buffer(olen)
+        if _scrypt_ll(password, len(password), salt, len(salt),
+                      N, r, p, out, olen):
+            raise ValueError
+        return out.raw
+
+    if len(salt) != _scrypt_salt or r != 8 or (p & (p - 1)) or (N*p <= 512):
+        return scr_mod.scrypt(password, salt, N, r, p, olen)
+
+    s = next(i for i in range(1, 64) if 2**i == N)
+    t = next(i for i in range(0, 30) if 2**i == p)
+    m = 2**(10 + s)
+    o = 2**(5 + t + s)
+    if s > 53 or t + s > 58:
+        raise ValueError
+    out = ctypes.create_string_buffer(olen)
+    if _scrypt(out, olen, password, len(password), salt, o, m) != 0:
+        raise ValueError
+    return out.raw
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    if N < 2 or (N & (N - 1)):
+        raise ValueError('scrypt N must be a power of 2 greater than 1')
+    if p > 255 or p < 1:
+        raise ValueError('scrypt_mcf p out of range [1,255]')
+    if N > 2**31:
+        raise ValueError('scrypt_mcf N out of range [2,2**31]')
+
+    if (salt is not None or r != 8 or (p & (p - 1)) or (N*p <= 512) or
+        prefix not in (SCRYPT_MCF_PREFIX_7, SCRYPT_MCF_PREFIX_s1,
+                       SCRYPT_MCF_PREFIX_ANY) or
+        _scrypt_ll):
+        return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+    s = next(i for i in range(1, 32) if 2**i == N)
+    t = next(i for i in range(0, 8) if 2**i == p)
+    m = 2**(10 + s)
+    o = 2**(5 + t + s)
+    mcf = ctypes.create_string_buffer(102)
+    if _scrypt_str(mcf, password, len(password), o, m) != 0:
+        return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+    if prefix in (SCRYPT_MCF_PREFIX_7, SCRYPT_MCF_PREFIX_ANY):
+        return mcf.raw.strip(b'\0')
+
+    _N, _r, _p, salt, hash, olen = mcf_mod._scrypt_mcf_decode_7(mcf.raw[:-1])
+    assert _N == N and _r == r and _p == p, (_N, _r, _p, N, r, p, o, m)
+    return mcf_mod._scrypt_mcf_encode_s1(N, r, p, salt, hash)
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    if mcf_mod._scrypt_mcf_7_is_standard(mcf) and not _scrypt_ll:
+        return _scrypt_str_chk(mcf, password, len(password)) == 0
+    return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    try:
+        from . import pylibscrypt
+        scr_mod = pylibscrypt
+    except ImportError:
+        pass
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pylibsodium_salsa.py b/lib/pylibscrypt/pylibsodium_salsa.py
new file mode 100644
index 0000000000000000000000000000000000000000..34057e632625f66c8301c098eaa37926a662da56
--- /dev/null
+++ b/lib/pylibscrypt/pylibsodium_salsa.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Richard Moore
+# Copyright (c) 2014 Jan Varho
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Scrypt implementation that calls into system libsodium"""
+
+
+import base64
+import ctypes, ctypes.util
+from ctypes import c_char_p, c_size_t, c_uint64, c_uint32, c_void_p
+import hashlib, hmac
+import numbers
+import struct
+import sys
+
+from . import mcf as mcf_mod
+from .common import *
+
+
+def _get_libsodium():
+    '''
+    Locate the nacl c libs to use
+    '''
+
+    __SONAMES = (13, 10, 5, 4)
+    # Import libsodium from system
+    sys_sodium = ctypes.util.find_library('sodium')
+    if sys_sodium is None:
+        sys_sodium = ctypes.util.find_library('libsodium')
+
+    if sys_sodium:
+        return ctypes.CDLL(sys_sodium)
+
+    # Import from local path
+    if sys.platform.startswith('win'):
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium')
+        except OSError:
+            pass
+        for soname_ver in __SONAMES:
+            try:
+                return ctypes.cdll.LoadLibrary(
+                    'libsodium-{0}'.format(soname_ver)
+                )
+            except OSError:
+                pass
+    elif sys.platform.startswith('darwin'):
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium.dylib')
+        except OSError:
+            pass
+    else:
+        try:
+            return ctypes.cdll.LoadLibrary('libsodium.so')
+        except OSError:
+            pass
+
+        for soname_ver in __SONAMES:
+            try:
+                return ctypes.cdll.LoadLibrary(
+                    'libsodium.so.{0}'.format(soname_ver)
+                )
+            except OSError:
+                pass
+
+
+_libsodium = _get_libsodium()
+if _libsodium is None:
+    raise ImportError('Unable to load libsodium')
+
+try:
+    _libsodium_salsa20_8 = _libsodium.crypto_core_salsa208
+except AttributeError:
+    raise ImportError('Incompatible libsodium: ')
+
+_libsodium_salsa20_8.argtypes = [
+    c_void_p,  # out (16*4 bytes)
+    c_void_p,  # in  (4*4 bytes)
+    c_void_p,  # k   (8*4 bytes)
+    c_void_p,  # c   (4*4 bytes)
+]
+
+
+# Python 3.4+ have PBKDF2 in hashlib, so use it...
+if 'pbkdf2_hmac' in dir(hashlib):
+    _pbkdf2 = hashlib.pbkdf2_hmac
+else:
+    # but fall back to Python implementation in < 3.4
+    from pbkdf2 import pbkdf2_hmac as _pbkdf2
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+    def array_overwrite(source, s_start, dest, d_start, length):
+        dest[d_start:d_start + length] = source[s_start:s_start + length]
+
+
+    def blockxor(source, s_start, dest, d_start, length):
+        for i in xrange(length):
+            dest[d_start + i] ^= source[s_start + i]
+
+
+    def integerify(B, r):
+        """A bijection from ({0, 1} ** k) to {0, ..., (2 ** k) - 1"""
+
+        Bi = (2 * r - 1) * 8
+        return B[Bi] & 0xffffffff
+
+
+    def salsa20_8(B, x):
+        """Salsa 20/8 using libsodium
+
+        NaCL/libsodium includes crypto_core_salsa208, but unfortunately it
+        expects the data in a different order, so we need to mix it up a bit.
+        """
+        hi = 0xffffffff00000000
+        lo = 0x00000000ffffffff
+        struct.pack_into('<9Q', x, 0,
+            (B[0] & lo) +  (B[2] & hi),  (B[5] & lo) + (B[7] & hi), # c
+            B[3], B[4],                                             # in
+            B[0], B[1], (B[2] & lo) + (B[5] & hi),                  # pad k pad
+            B[6], B[7],
+        )
+
+        c = ctypes.addressof(x)
+        i = c + 4*4
+        k = c + 9*4
+
+        _libsodium_salsa20_8(c, i, k, c)
+
+        B[:] = struct.unpack('<8Q8x', x)
+
+
+    def blockmix_salsa8(BY, Yi, r):
+        """Blockmix; Used by SMix"""
+
+        start = (2 * r - 1) * 8
+        X = BY[start:start+8]                              # BlockMix - 1
+        x = ctypes.create_string_buffer(8*9)
+
+        for i in xrange(2 * r):                            # BlockMix - 2
+            blockxor(BY, i * 8, X, 0, 8)                   # BlockMix - 3(inner)
+            salsa20_8(X, x)                                # BlockMix - 3(outer)
+            array_overwrite(X, 0, BY, Yi + (i * 8), 8)     # BlockMix - 4
+
+        for i in xrange(r):                                # BlockMix - 6
+            array_overwrite(BY, Yi + (i * 2) * 8, BY, i * 8, 8)
+            array_overwrite(BY, Yi + (i*2 + 1) * 8, BY, (i + r) * 8, 8)
+
+
+    def smix(B, Bi, r, N, V, X):
+        """SMix; a specific case of ROMix based on Salsa20/8"""
+
+        array_overwrite(B, Bi, X, 0, 16 * r)               # ROMix - 1
+
+        for i in xrange(N):                                # ROMix - 2
+            array_overwrite(X, 0, V, i * (16 * r), 16 * r) # ROMix - 3
+            blockmix_salsa8(X, 16 * r, r)                  # ROMix - 4
+
+        for i in xrange(N):                                # ROMix - 6
+            j = integerify(X, r) & (N - 1)                 # ROMix - 7
+            blockxor(V, j * (16 * r), X, 0, 16 * r)        # ROMix - 8(inner)
+            blockmix_salsa8(X, 16 * r, r)                  # ROMix - 9(outer)
+
+        array_overwrite(X, 0, B, Bi, 16 * r)               # ROMix - 10
+
+    check_args(password, salt, N, r, p, olen)
+
+    # Everything is lists of 64-bit uints for all but pbkdf2
+    try:
+        B  = _pbkdf2('sha256', password, salt, 1, p * 128 * r)
+        B  = list(struct.unpack('<%dQ' % (len(B) // 8), B))
+        XY = [0] * (32 * r)
+        V  = [0] * (16 * r * N)
+    except (MemoryError, OverflowError):
+        raise ValueError("scrypt parameters don't fit in memory")
+
+    for i in xrange(p):
+        smix(B, i * 16 * r, r, N, V, XY)
+
+    B = struct.pack('<%dQ' % len(B), *B)
+    return _pbkdf2('sha256', password, B, 1, olen)
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=b'$s1$'):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pypyscrypt.py b/lib/pylibscrypt/pypyscrypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b088e12f3784a11be5c0382e31968439b3c296b
--- /dev/null
+++ b/lib/pylibscrypt/pypyscrypt.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Richard Moore
+# Copyright (c) 2014 Jan Varho
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Python implementation of Scrypt password-based key derivation function"""
+
+# Scrypt definition:
+# http://www.tarsnap.com/scrypt/scrypt.pdf
+
+# It was originally written for a pure-Python Litecoin CPU miner:
+# https://github.com/ricmoo/nightminer
+# Imported to this project from:
+# https://github.com/ricmoo/pyscrypt
+# And owes thanks to:
+# https://github.com/wg/scrypt
+
+
+import hashlib, hmac
+import struct
+
+from . import mcf as mcf_mod
+from .common import *
+
+
+# Python 3.4+ have PBKDF2 in hashlib, so use it...
+if 'pbkdf2_hmac' in dir(hashlib):
+    _pbkdf2 = hashlib.pbkdf2_hmac
+else:
+    # but fall back to Python implementation in < 3.4
+    from pbkdf2 import pbkdf2_hmac as _pbkdf2
+
+
+def array_overwrite(source, s_start, dest, d_start, length):
+    dest[d_start:d_start + length] = source[s_start:s_start + length]
+
+
+def blockxor(source, s_start, dest, d_start, length):
+    for i in xrange(length):
+        dest[d_start + i] ^= source[s_start + i]
+
+
+def integerify(B, r):
+    """A bijection from ({0, 1} ** k) to {0, ..., (2 ** k) - 1"""
+
+    Bi = (2 * r - 1) * 16
+    return B[Bi]
+
+
+def R(X, destination, a1, a2, b):
+    """A single Salsa20 row operation"""
+
+    a = (X[a1] + X[a2]) & 0xffffffff
+    X[destination] ^= ((a << b) | (a >> (32 - b)))
+
+
+def salsa20_8(B, x, src, s_start, dest, d_start):
+    """Salsa20/8 http://en.wikipedia.org/wiki/Salsa20"""
+
+    # Merged blockxor for speed
+    for i in xrange(16):
+        x[i] = B[i] = B[i] ^ src[s_start + i]
+
+    # This is the actual Salsa 20/8: four identical double rounds
+    for i in xrange(4):
+        R(x, 4, 0,12, 7);R(x, 8, 4, 0, 9);R(x,12, 8, 4,13);R(x, 0,12, 8,18)
+        R(x, 9, 5, 1, 7);R(x,13, 9, 5, 9);R(x, 1,13, 9,13);R(x, 5, 1,13,18)
+        R(x,14,10, 6, 7);R(x, 2,14,10, 9);R(x, 6, 2,14,13);R(x,10, 6, 2,18)
+        R(x, 3,15,11, 7);R(x, 7, 3,15, 9);R(x,11, 7, 3,13);R(x,15,11, 7,18)
+        R(x, 1, 0, 3, 7);R(x, 2, 1, 0, 9);R(x, 3, 2, 1,13);R(x, 0, 3, 2,18)
+        R(x, 6, 5, 4, 7);R(x, 7, 6, 5, 9);R(x, 4, 7, 6,13);R(x, 5, 4, 7,18)
+        R(x,11,10, 9, 7);R(x, 8,11,10, 9);R(x, 9, 8,11,13);R(x,10, 9, 8,18)
+        R(x,12,15,14, 7);R(x,13,12,15, 9);R(x,14,13,12,13);R(x,15,14,13,18)
+
+    # While we are handling the data, write it to the correct dest.
+    # The latter half is still part of salsa20
+    for i in xrange(16):
+        dest[d_start + i] = B[i] = (x[i] + B[i]) & 0xffffffff
+
+
+def blockmix_salsa8(BY, Yi, r):
+    """Blockmix; Used by SMix"""
+
+    start = (2 * r - 1) * 16
+    X = BY[start:start+16]                             # BlockMix - 1
+    tmp = [0]*16
+
+    for i in xrange(2 * r):                            # BlockMix - 2
+        #blockxor(BY, i * 16, X, 0, 16)                # BlockMix - 3(inner)
+        salsa20_8(X, tmp, BY, i * 16, BY, Yi + i*16)   # BlockMix - 3(outer)
+        #array_overwrite(X, 0, BY, Yi + (i * 16), 16)  # BlockMix - 4
+
+    for i in xrange(r):                                # BlockMix - 6
+        array_overwrite(BY, Yi + (i * 2) * 16, BY, i * 16, 16)
+        array_overwrite(BY, Yi + (i*2 + 1) * 16, BY, (i + r) * 16, 16)
+
+
+def smix(B, Bi, r, N, V, X):
+    """SMix; a specific case of ROMix based on Salsa20/8"""
+
+    array_overwrite(B, Bi, X, 0, 32 * r)               # ROMix - 1
+
+    for i in xrange(N):                                # ROMix - 2
+        array_overwrite(X, 0, V, i * (32 * r), 32 * r) # ROMix - 3
+        blockmix_salsa8(X, 32 * r, r)                  # ROMix - 4
+
+    for i in xrange(N):                                # ROMix - 6
+        j = integerify(X, r) & (N - 1)                 # ROMix - 7
+        blockxor(V, j * (32 * r), X, 0, 32 * r)        # ROMix - 8(inner)
+        blockmix_salsa8(X, 32 * r, r)                  # ROMix - 9(outer)
+
+    array_overwrite(X, 0, B, Bi, 32 * r)               # ROMix - 10
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+
+    check_args(password, salt, N, r, p, olen)
+
+    # Everything is lists of 32-bit uints for all but pbkdf2
+    try:
+        B  = _pbkdf2('sha256', password, salt, 1, p * 128 * r)
+        B  = list(struct.unpack('<%dI' % (len(B) // 4), B))
+        XY = [0] * (64 * r)
+        V  = [0] * (32 * r * N)
+    except (MemoryError, OverflowError):
+        raise ValueError("scrypt parameters don't fit in memory")
+
+    for i in xrange(p):
+        smix(B, i * 32 * r, r, N, V, XY)
+
+    B = struct.pack('<%dI' % len(B), *B)
+    return _pbkdf2('sha256', password, B, 1, olen)
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+
+__all__ = ['scrypt', 'scrypt_mcf', 'scrypt_mcf_check']
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pypyscrypt_inline.py b/lib/pylibscrypt/pypyscrypt_inline.py
new file mode 100644
index 0000000000000000000000000000000000000000..75f497e9a181a54e9fe1be9b21f438c3e20872d2
--- /dev/null
+++ b/lib/pylibscrypt/pypyscrypt_inline.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+
+# Automatically generated file, see inline.py
+
+# Copyright (c) 2014 Richard Moore
+# Copyright (c) 2014 Jan Varho
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Python implementation of Scrypt password-based key derivation function"""
+
+# Scrypt definition:
+# http://www.tarsnap.com/scrypt/scrypt.pdf
+
+# It was originally written for a pure-Python Litecoin CPU miner:
+# https://github.com/ricmoo/nightminer
+# Imported to this project from:
+# https://github.com/ricmoo/pyscrypt
+# And owes thanks to:
+# https://github.com/wg/scrypt
+
+
+import hashlib, hmac
+import struct
+
+from . import mcf as mcf_mod
+from .common import *
+
+
+# Python 3.4+ have PBKDF2 in hashlib, so use it...
+if 'pbkdf2_hmac' in dir(hashlib):
+    _pbkdf2 = hashlib.pbkdf2_hmac
+else:
+    # but fall back to Python implementation in < 3.4
+    from pbkdf2 import pbkdf2_hmac as _pbkdf2
+
+
+def blockxor(source, s_start, dest, d_start, length):
+    for i in xrange(length):
+        dest[d_start + i] ^= source[s_start + i]
+
+
+def integerify(B, r):
+    """A bijection from ({0, 1} ** k) to {0, ..., (2 ** k) - 1"""
+
+    Bi = (2 * r - 1) * 16
+    return B[Bi]
+
+
+def salsa20_8(B, x, src, s_start, dest, d_start):
+    """Salsa20/8 http://en.wikipedia.org/wiki/Salsa20"""
+
+    # Merged blockxor for speed
+    for i in xrange(16):
+        x[i] = B[i] = B[i] ^ src[s_start + i]
+
+    # This is the actual Salsa 20/8: four identical double rounds
+    for i in xrange(4):
+        a = (x[0]+x[12]) & 0xffffffff
+        b = (x[5]+x[1]) & 0xffffffff
+        x[4] ^= (a << 7) | (a >> 25)
+        x[9] ^= (b << 7) | (b >> 25)
+        a = (x[10]+x[6]) & 0xffffffff
+        b = (x[15]+x[11]) & 0xffffffff
+        x[14] ^= (a << 7) | (a >> 25)
+        x[3] ^= (b << 7) | (b >> 25)
+        a = (x[4]+x[0]) & 0xffffffff
+        b = (x[9]+x[5]) & 0xffffffff
+        x[8] ^= (a << 9) | (a >> 23)
+        x[13] ^= (b << 9) | (b >> 23)
+        a = (x[14]+x[10]) & 0xffffffff
+        b = (x[3]+x[15]) & 0xffffffff
+        x[2] ^= (a << 9) | (a >> 23)
+        x[7] ^= (b << 9) | (b >> 23)
+        a = (x[8]+x[4]) & 0xffffffff
+        b = (x[13]+x[9]) & 0xffffffff
+        x[12] ^= (a << 13) | (a >> 19)
+        x[1] ^= (b << 13) | (b >> 19)
+        a = (x[2]+x[14]) & 0xffffffff
+        b = (x[7]+x[3]) & 0xffffffff
+        x[6] ^= (a << 13) | (a >> 19)
+        x[11] ^= (b << 13) | (b >> 19)
+        a = (x[12]+x[8]) & 0xffffffff
+        b = (x[1]+x[13]) & 0xffffffff
+        x[0] ^= (a << 18) | (a >> 14)
+        x[5] ^= (b << 18) | (b >> 14)
+        a = (x[6]+x[2]) & 0xffffffff
+        b = (x[11]+x[7]) & 0xffffffff
+        x[10] ^= (a << 18) | (a >> 14)
+        x[15] ^= (b << 18) | (b >> 14)
+        a = (x[0]+x[3]) & 0xffffffff
+        b = (x[5]+x[4]) & 0xffffffff
+        x[1] ^= (a << 7) | (a >> 25)
+        x[6] ^= (b << 7) | (b >> 25)
+        a = (x[10]+x[9]) & 0xffffffff
+        b = (x[15]+x[14]) & 0xffffffff
+        x[11] ^= (a << 7) | (a >> 25)
+        x[12] ^= (b << 7) | (b >> 25)
+        a = (x[1]+x[0]) & 0xffffffff
+        b = (x[6]+x[5]) & 0xffffffff
+        x[2] ^= (a << 9) | (a >> 23)
+        x[7] ^= (b << 9) | (b >> 23)
+        a = (x[11]+x[10]) & 0xffffffff
+        b = (x[12]+x[15]) & 0xffffffff
+        x[8] ^= (a << 9) | (a >> 23)
+        x[13] ^= (b << 9) | (b >> 23)
+        a = (x[2]+x[1]) & 0xffffffff
+        b = (x[7]+x[6]) & 0xffffffff
+        x[3] ^= (a << 13) | (a >> 19)
+        x[4] ^= (b << 13) | (b >> 19)
+        a = (x[8]+x[11]) & 0xffffffff
+        b = (x[13]+x[12]) & 0xffffffff
+        x[9] ^= (a << 13) | (a >> 19)
+        x[14] ^= (b << 13) | (b >> 19)
+        a = (x[3]+x[2]) & 0xffffffff
+        b = (x[4]+x[7]) & 0xffffffff
+        x[0] ^= (a << 18) | (a >> 14)
+        x[5] ^= (b << 18) | (b >> 14)
+        a = (x[9]+x[8]) & 0xffffffff
+        b = (x[14]+x[13]) & 0xffffffff
+        x[10] ^= (a << 18) | (a >> 14)
+        x[15] ^= (b << 18) | (b >> 14)
+
+    # While we are handling the data, write it to the correct dest.
+    # The latter half is still part of salsa20
+    for i in xrange(16):
+        dest[d_start + i] = B[i] = (x[i] + B[i]) & 0xffffffff
+
+
+def blockmix_salsa8(BY, Yi, r):
+    """Blockmix; Used by SMix"""
+
+    start = (2 * r - 1) * 16
+    X = BY[start:start+16]                             # BlockMix - 1
+    tmp = [0]*16
+
+    for i in xrange(2 * r):                            # BlockMix - 2
+        #blockxor(BY, i * 16, X, 0, 16)                # BlockMix - 3(inner)
+        salsa20_8(X, tmp, BY, i * 16, BY, Yi + i*16)   # BlockMix - 3(outer)
+        #array_overwrite(X, 0, BY, Yi + (i * 16), 16)  # BlockMix - 4
+
+    for i in xrange(r):                                # BlockMix - 6
+        BY[i * 16:(i * 16)+(16)] = BY[Yi + (i * 2) * 16:(Yi + (i * 2) * 16)+(16)]
+        BY[(i + r) * 16:((i + r) * 16)+(16)] = BY[Yi + (i*2 + 1) * 16:(Yi + (i*2 + 1) * 16)+(16)]
+
+
+def smix(B, Bi, r, N, V, X):
+    """SMix; a specific case of ROMix based on Salsa20/8"""
+
+    X[0:(0)+(32 * r)] = B[Bi:(Bi)+(32 * r)]
+
+    for i in xrange(N):                                # ROMix - 2
+        V[i * (32 * r):(i * (32 * r))+(32 * r)] = X[0:(0)+(32 * r)]
+        blockmix_salsa8(X, 32 * r, r)                  # ROMix - 4
+
+    for i in xrange(N):                                # ROMix - 6
+        j = integerify(X, r) & (N - 1)                 # ROMix - 7
+        blockxor(V, j * (32 * r), X, 0, 32 * r)        # ROMix - 8(inner)
+        blockmix_salsa8(X, 32 * r, r)                  # ROMix - 9(outer)
+
+    B[Bi:(Bi)+(32 * r)] = X[0:(0)+(32 * r)]
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+
+    check_args(password, salt, N, r, p, olen)
+
+    # Everything is lists of 32-bit uints for all but pbkdf2
+    try:
+        B  = _pbkdf2('sha256', password, salt, 1, p * 128 * r)
+        B  = list(struct.unpack('<%dI' % (len(B) // 4), B))
+        XY = [0] * (64 * r)
+        V  = [0] * (32 * r * N)
+    except (MemoryError, OverflowError):
+        raise ValueError("scrypt parameters don't fit in memory")
+
+    for i in xrange(p):
+        smix(B, i * 32 * r, r, N, V, XY)
+
+    B = struct.pack('<%dI' % len(B), *B)
+    return _pbkdf2('sha256', password, B, 1, olen)
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+
+__all__ = ['scrypt', 'scrypt_mcf', 'scrypt_mcf_check']
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/pyscrypt.py b/lib/pylibscrypt/pyscrypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..5cfe1bbda7c2eb1bb93e7e9eaa0950f9578e9e4e
--- /dev/null
+++ b/lib/pylibscrypt/pyscrypt.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Scrypt implementation that calls into the 'scrypt' python module"""
+
+
+import numbers
+
+from scrypt import hash as _scrypt
+
+from . import mcf as mcf_mod
+from .common import *
+
+
+# scrypt < 0.6 doesn't support hash length
+try:
+    _scrypt(b'password', b'NaCl', N=2, r=1, p=1, buflen=42)
+except TypeError:
+    raise ImportError('scrypt module version unsupported, 0.6+ required')
+
+
+def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
+    """Returns a key derived using the scrypt key-derivarion function
+
+    N must be a power of two larger than 1 but no larger than 2 ** 63 (insane)
+    r and p must be positive numbers such that r * p < 2 ** 30
+
+    The default values are:
+    N -- 2**14 (~16k)
+    r -- 8
+    p -- 1
+
+    Memory usage is proportional to N*r. Defaults require about 16 MiB.
+    Time taken is proportional to N*p. Defaults take <100ms of a recent x86.
+
+    The last one differs from libscrypt defaults, but matches the 'interactive'
+    work factor from the original paper. For long term storage where runtime of
+    key derivation is not a problem, you could use 16 as in libscrypt or better
+    yet increase N if memory is plentiful.
+    """
+    check_args(password, salt, N, r, p, olen)
+
+    try:
+        return _scrypt(password=password, salt=salt, N=N, r=r, p=p, buflen=olen)
+    except:
+        raise ValueError
+
+
+def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
+               prefix=SCRYPT_MCF_PREFIX_DEFAULT):
+    """Derives a Modular Crypt Format hash using the scrypt KDF
+
+    Parameter space is smaller than for scrypt():
+    N must be a power of two larger than 1 but no larger than 2 ** 31
+    r and p must be positive numbers between 1 and 255
+    Salt must be a byte string 1-16 bytes long.
+
+    If no salt is given, a random salt of 128+ bits is used. (Recommended.)
+    """
+    return mcf_mod.scrypt_mcf(scrypt, password, salt, N, r, p, prefix)
+
+
+def scrypt_mcf_check(mcf, password):
+    """Returns True if the password matches the given MCF hash"""
+    return mcf_mod.scrypt_mcf_check(scrypt, mcf, password)
+
+
+if __name__ == "__main__":
+    import sys
+    from . import tests
+    tests.run_scrypt_suite(sys.modules[__name__])
+
diff --git a/lib/pylibscrypt/tests.py b/lib/pylibscrypt/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..95e1b1869d4cb6d80b1c46b8db65f385a42c2f62
--- /dev/null
+++ b/lib/pylibscrypt/tests.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014, Jan Varho
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Tests scrypt and PBKDF2 implementations"""
+
+
+import base64
+import hashlib
+import unittest
+
+
+class ScryptTests(unittest.TestCase):
+    """Tests an scrypt implementation from module"""
+    set_up_lambda = None
+    tear_down_lambda = None
+
+    def setUp(self):
+        if not self.module:
+            self.skipTest('module not tested')
+        if self.set_up_lambda:
+            self.set_up_lambda()
+
+    def tearDown(self):
+        if self.tear_down_lambda:
+            self.tear_down_lambda()
+
+    def _test_vector(self, vector):
+        pw, s, N, r, p, h, m = vector
+        self.assertEqual(
+            self.module.scrypt(pw, s, N, r, p),
+            base64.b16decode(h, True)
+        )
+        if m is not None:
+            self.assertEqual(
+                self.module.scrypt_mcf(pw, s, N, r, p),
+                m
+            )
+            self.assertTrue(self.module.scrypt_mcf_check(m, pw))
+            self.assertFalse(self.module.scrypt_mcf_check(m, b'x' + pw))
+
+    def test_vector0(self):
+        self._test_vector((
+            b'', b'', 16, 1, 1,
+            b'77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442'
+            b'fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906',
+            None
+        ))
+
+    def test_vector1(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        self._test_vector((
+            b'password', b'NaCl', 1024, 8, 16,
+            b'fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162'
+            b'2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640',
+            b'$s1$0a0810$TmFDbA==$/bq+HJ00cgB4VucZDQHp/nxq18vII3gw53N2Y0s3MWIu'
+            b'rzDZLiKjiG/xCSedmDDaxyevuUqD7m2DYMvfoswGQA=='
+        ))
+
+    def test_vector2(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        self._test_vector((
+            b'pleaseletmein', b'SodiumChloride', 16384, 8, 1,
+            b'7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2'
+            b'd5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887',
+            b'$s1$0e0801$U29kaXVtQ2hsb3JpZGU=$cCO9yzr9c0hGHAbNgf046/2o+7qQT44+'
+            b'qbVD9lRdofLVQylVYT8Pz2LUlwUkKpr55h6F3A1lHkDfzwF7RVdYhw=='
+        ))
+
+    def test_vector3(self):
+        self._test_vector((
+            b'password', b'NaCl', 2, 8, 1,
+            b'e5ed8edc019edfef2d3ced0896faf9eec6921dcc68125ce81c10d53474ce'
+            b'1be545979159700d324e77c68d34c553636a8429c4f3c99b9566466877f9'
+            b'dca2b92b',
+            b'$s1$010801$TmFDbA==$5e2O3AGe3+8tPO0Ilvr57saSHcxoElzoHBDVNHTO'
+            b'G+VFl5FZcA0yTnfGjTTFU2NqhCnE88mblWZGaHf53KK5Kw=='
+        ))
+
+    def test_vector4(self):
+        self._test_vector((
+            b'pleaseletmein', b'SodiumChloride', 4, 1, 1,
+            b'BB1D77016C543A99FE632C9C43C60180FD05E0CAC8B29374DBD1854569CB'
+            b'534F487240CFC069D6A59A35F2FA5C7428B21D9BE9F84315446D5371119E'
+            b'016FEDF7',
+            b'$s1$020101$U29kaXVtQ2hsb3JpZGU=$ux13AWxUOpn+YyycQ8YBgP0F4MrI'
+            b'spN029GFRWnLU09IckDPwGnWpZo18vpcdCiyHZvp+EMVRG1TcRGeAW/t9w=='
+        ))
+
+    def test_vector5(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        self._test_vector((
+            b'pleaseletmein', b'X'*32, 2**10, 8, 1,
+            b'cd81f46bd79125651e017a1bf5a28295f68d4b68d397815514bfdc2f3684'
+            b'f034ae2a5df332a48e915f7567306df2d401387b70d8f02f83bd6f4c69ff'
+            b'89d2663c',
+            None
+        ))
+
+    def test_vector6(self):
+        self._test_vector((
+            b'pa\0ss', b'salt'*4, 32, 2, 2,
+            b'76c5260f1dc6339512ae87143d799089f5b508c823c870a3d55f641efa84'
+            b'63a813221050c93a44255ac8027804c49a87c1ecc9911356b9fc17e06eda'
+            b'85f23ff5',
+            None
+        ))
+
+    def test_vector7(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        self._test_vector((
+            b'pleaseletmein', b'X'*32, 2**10, 8, 2,
+            b'1693cc02b680b18b3d0a874d459d23a5f63ff4f9de0fb5917ef899226af6'
+            b'bd33d3a3dfe569d3b6f4f762f0cb64f5f406d485aca501a54645d7389fe6'
+            b'e28e261e',
+            None
+        ))
+
+    def test_bytes_enforced(self):
+        self.assertRaises(TypeError, self.module.scrypt, u'pass', b'salt')
+        self.assertRaises(TypeError, self.module.scrypt, 42, b'salt')
+        self.assertRaises(TypeError, self.module.scrypt, b'pass', None)
+        self.assertRaises(TypeError, self.module.scrypt_mcf, u'mcf', b'pass')
+        self.assertRaises(TypeError, self.module.scrypt_mcf, object, b'pass')
+
+    def test_salt_length_mcf(self):
+        pw = b'pass'
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, b'')
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, b'a'*17)
+
+    def test_salt_generation(self):
+        pw, N = b'pass', 2
+        m1 = self.module.scrypt_mcf(pw, N=N)
+        m2 = self.module.scrypt_mcf(pw, N=N)
+        self.assertNotEqual(m1, m2)
+        self.assertTrue(self.module.scrypt_mcf_check(m1, pw))
+        self.assertTrue(self.module.scrypt_mcf_check(m2, pw))
+
+    def test_invalid_N(self):
+        pw, s = b'password', b'salt'*8
+        self.assertRaises(TypeError, self.module.scrypt, pw, s, 7.5)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, -1)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 1)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 42)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 2**66)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 2**66+2)
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, None, 1)
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, None, 2**32)
+
+    def test_huge_N(self):
+        pw, s = b'password', b'salt'*8
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 2**50)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, 2**60)
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw,
+                          N=2**31, prefix=b'$7$')
+
+    def test_invalid_r(self):
+        pw, s, N = b'password', b'salt', 2
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, 0)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, -1)
+        self.assertRaises(TypeError, self.module.scrypt, pw, s, N, 7.5)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, 2**31)
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, s, N, 256)
+
+    def test_invalid_p(self):
+        pw, s, N = b'password', b'salt', 2
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, 1, 0)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, 1, -2**31)
+        self.assertRaises(TypeError, self.module.scrypt, pw, s, N, 1, 7.5)
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, 2**35)
+        self.assertRaises(ValueError, self.module.scrypt_mcf, pw, s, N, 1, 256)
+
+    def test_olen(self):
+        pw, s, N = b'password', b'salt', 2
+        self.assertEquals(len(self.module.scrypt(pw, s, N, olen=42)), 42)
+        self.assertEquals(len(self.module.scrypt(pw, s, N, olen=100)), 100)
+        self.assertRaises(TypeError, self.module.scrypt, pw, s, N, olen=b'7')
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, olen=-1)
+
+    def test_invalid_olen(self):
+        pw, s, N = b'password', b'salt', 2**10
+        self.assertRaises(TypeError, self.module.scrypt, pw, s, N, olen=b'7')
+        self.assertRaises(ValueError, self.module.scrypt, pw, s, N, olen=-1)
+
+    def test_mcf(self):
+        pw = b'password'
+        self.assertRaises(ValueError, self.module.scrypt_mcf_check, b'', pw)
+        self.assertRaises(ValueError, self.module.scrypt_mcf_check,
+                          b'$s1$ffffffff$aaaa$bbbb', pw)
+        self.assertRaises(TypeError, self.module.scrypt_mcf_check, u'mcf', pw)
+        self.assertRaises(TypeError, self.module.scrypt_mcf_check, b'mcf', 42)
+
+    def test_mcf_padding(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        pw = b'pleaseletmein'
+        m1 = (
+            b'$s1$020101$U29kaXVtQ2hsb3JpZGU$ux13AWxUOpn+YyycQ8YBgP0F4MrI'
+            b'spN029GFRWnLU09IckDPwGnWpZo18vpcdCiyHZvp+EMVRG1TcRGeAW/t9w=='
+        )
+        m2 = (
+            b'$s1$020101$U29kaXVtQ2hsb3JpZGU=$ux13AWxUOpn+YyycQ8YBgP0F4MrI'
+            b'spN029GFRWnLU09IckDPwGnWpZo18vpcdCiyHZvp+EMVRG1TcRGeAW/t9w='
+        )
+        m3 = (
+            b'$s1$020101$U29kaXVtQ2hsb3JpZGU=$ux13AWxUOpn+YyycQ8YBgP0F4MrI'
+            b'spN029GFRWnLU09IckDPwGnWpZo18vpcdCiyHZvp+EMVRG1TcRGeAW/t9'
+        )
+        self.assertTrue(self.module.scrypt_mcf_check(m1, pw))
+        self.assertTrue(self.module.scrypt_mcf_check(m2, pw))
+        self.assertRaises(ValueError, self.module.scrypt_mcf_check, m3, pw)
+
+    def test_mcf_nonstandard(self):
+        pw = b'pass'
+        m1 = ( # empty salt
+            b'$s1$010801$$WA1vBj+HFlIk7pG/OPS5bY4NKHBGeGIxEY99farnu2C9uOHxKe'
+            b'LWP3sCXRvP98F7lVi2JNT/Bmte38iodf81VEYB0Nu3pBw9JqTwiCAqMwL+2kqB'
+        )
+        m2 = ( # 31 byte hash
+            b'$7$16..../....l/htqjrI38qNowkQZL8RxFVxS8JV9PPJr1+A/WTQWiU'
+            b'$wOcPY0vsHHshxa0u87FDhmTo42WZr0JbSHY2w2Zkyr1'
+        )
+        m3 = ( # 44 byte salt, 31 byte hash
+            b'$7$12..../....aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+            b'$14hkhieutTQcbq.iU1FDZzYz1vW8NPYowy4WERDM70'
+        )
+        self.assertTrue(self.module.scrypt_mcf_check(m1, pw))
+        self.assertTrue(self.module.scrypt_mcf_check(m2, pw))
+        self.assertTrue(self.module.scrypt_mcf_check(m3, pw))
+
+    def test_mcf_7(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        p, m = b'pleaseletmein', (
+            b'$7$C6..../....SodiumChloride'
+            b'$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D'
+        )
+        self.assertTrue(self.module.scrypt_mcf_check(m, p))
+        self.assertFalse(self.module.scrypt_mcf_check(m, b'X'+p))
+        self.assertRaises(ValueError, self.module.scrypt_mcf_check,
+            b'$7$$', p
+        )
+        self.assertRaises(ValueError, self.module.scrypt_mcf_check,
+            b'$7$$$', p
+        )
+
+    def test_mcf_7_2(self):
+        if self.fast:
+            self.skipTest('slow testcase')
+        p = b'pleaseletmein'
+        m1 = self.module.scrypt_mcf(p, None, 2**10, 8, 2, b'$7$')
+        self.assertTrue(m1.startswith(b'$7$'))
+        self.assertTrue(self.module.scrypt_mcf_check(m1, p))
+        m2 = self.module.scrypt_mcf(p, None, 2**10, 8, 2, b'$s1$')
+        self.assertTrue(m2.startswith(b'$s1$'))
+        self.assertTrue(self.module.scrypt_mcf_check(m1, p))
+
+    def test_mcf_7_fast(self):
+        p, m1 = b'pleaseletmein', (
+            b'$7$06..../....SodiumChloride'
+            b'$ENlyo6fGw4PCcDBOFepfSZjFUnVatHzCcW55.ZGz3B0'
+        )
+        self.assertTrue(self.module.scrypt_mcf_check(m1, p))
+        m2 = self.module.scrypt_mcf(p, b'NaCl', 4, 8, 1, b'$7$')
+        self.assertTrue(self.module.scrypt_mcf_check(m2, p))
+
+    def test_mcf_unknown(self):
+        p = b'pleaseletmein'
+        self.assertRaises(ValueError, self.module.scrypt_mcf, p, prefix=b'$$')
+
+    def test_mcf_null(self):
+        p1, p2, p3 = b'please', b'please\0letmein', b'pleaseletmein'
+        self.assertRaises(ValueError, self.module.scrypt_mcf, p2, N=4)
+        m = (
+            b'$s1$020801$m8/OZVv4hi8rHFVTvOH3tQ==$jwi4vgiCjyqrZKOaksMFks5A'
+            b'M9ZRcrVPhAwqT1iRMTqXYrwkTngwjR2rwbAet9cSGdFfSverOEVLiLuUzG4k'
+            b'Hg=='
+        )
+        self.assertTrue(self.module.scrypt_mcf_check(m, p2))
+        self.assertFalse(self.module.scrypt_mcf_check(m, p1))
+        self.assertFalse(self.module.scrypt_mcf_check(m, p3))
+
+
+def load_scrypt_suite(name, module, fast=True):
+    loader = unittest.defaultTestLoader
+    tests = type(name, (ScryptTests,), {'module': module, 'fast': fast})
+    return unittest.defaultTestLoader.loadTestsFromTestCase(tests)
+
+
+def run_scrypt_suite(module, fast=False):
+    suite = unittest.TestSuite()
+    suite.addTest(load_scrypt_suite('scryptTests', module, fast))
+    unittest.TextTestRunner().run(suite)
+
+
+class PBKDF2Tests(unittest.TestCase):
+    """Tests a PBKDF2 implementation from module"""
+    def setUp(self):
+        if not self.module:
+            self.skipTest('module not tested')
+
+    def _test_vector(self, vector):
+        n, p, s, c, l, h = vector
+        h = base64.b16decode(h, True)
+        self.assertEquals(self.module.pbkdf2_hmac(n, p, s, c, l), h)
+
+    def test_vector1(self):
+        self._test_vector(('sha1', b'password', b'salt', 1, 20,
+                           b'0c60c80f961f0e71f3a9b524af6012062fe037a6'))
+
+    def test_vector2(self):
+        self._test_vector(('sha1', b'pass\0word', b'sa\0lt', 4096, 16,
+                           b'56fa6aa75548099dcc37d7f03425e0c3'))
+
+    def test_vector3(self):
+        self._test_vector(('sha256', b'password', b'NaCl', 7, 42,
+                           b'8cb94b8721e20e643be099f3c31d332456b4c26f55'
+                           b'b6403950267dc2b3c0806bda709a3f2d7f6107db73'))
+
+    def test_long_key(self):
+        self.module.pbkdf2_hmac('sha256', b'pass'*100, b'NaCl', 2, 20)
+
+
+def load_pbkdf2_suite(name, module):
+    loader = unittest.defaultTestLoader
+    tests = type(name, (PBKDF2Tests,), {'module': module})
+    return unittest.defaultTestLoader.loadTestsFromTestCase(tests)
+
+
+def run_pbkdf2_suite(module, fast=False):
+    suite = unittest.TestSuite()
+    suite.addTest(load_pbkdf2_suite('scryptTests', module))
+    unittest.TextTestRunner().run(suite)
+
+
+if __name__ == "__main__":
+    suite = unittest.TestSuite()
+    try:
+        from . import pylibscrypt
+        suite.addTest(load_scrypt_suite('pylibscryptTests', pylibscrypt, True))
+    except ImportError:
+        suite.addTest(load_scrypt_suite('pylibscryptTests', None, True))
+
+    try:
+        from . import pyscrypt
+        suite.addTest(load_scrypt_suite('pyscryptTests', pyscrypt, True))
+    except ImportError:
+        suite.addTest(load_scrypt_suite('pyscryptTests', None, True))
+
+    try:
+        from . import pylibsodium
+        suite.addTest(load_scrypt_suite('pylibsodiumTests',
+                                        pylibsodium, True))
+        from . import pylibscrypt
+        loader = unittest.defaultTestLoader
+        def set_up_ll(self):
+            if not self.module._scrypt_ll:
+                self.skipTest('no ll')
+            self.tmp_ll = self.module._scrypt_ll
+            self.tmp_scr = self.module.scr_mod
+            self.module._scrypt_ll = None
+            self.module.scr_mod = pylibscrypt
+        def tear_down_ll(self):
+            self.module._scrypt_ll = self.tmp_ll
+            self.module.scr_mod = self.tmp_scr
+        tmp = type(
+            'pylibsodiumFallbackTests', (ScryptTests,),
+            {
+                'module': pylibsodium, 'fast': False,
+                'set_up_lambda': set_up_ll,
+                'tear_down_lambda': tear_down_ll,
+            }
+        )
+        suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(tmp))
+    except ImportError:
+        suite.addTest(load_scrypt_suite('pylibsodiumTests', None, True))
+
+    try:
+        from . import pylibsodium_salsa
+        suite.addTest(load_scrypt_suite('pylibsodium_salsaTests',
+                                        pylibsodium_salsa, True))
+    except ImportError:
+        suite.addTest(load_scrypt_suite('pylibsodium_salsaTests', None, True))
+
+    try:
+        from . import pypyscrypt_inline as pypyscrypt
+        suite.addTest(load_scrypt_suite('pypyscryptTests', pypyscrypt, True))
+    except ImportError:
+        suite.addTest(load_scrypt_suite('pypyscryptTests', None, True))
+
+    try:
+        from . import pbkdf2
+        suite.addTest(load_pbkdf2_suite('pbkdf2', pbkdf2))
+    except ImportError:
+        suite.addTest(load_pbkdf2_suite('pbkdf2', None))
+
+    if 'pbkdf2_hmac' in dir(hashlib):
+        suite.addTest(load_pbkdf2_suite('hashlib_pbkdf2', hashlib))
+    else:
+        suite.addTest(load_pbkdf2_suite('hashlib_pbkdf2', None))
+
+    unittest.TextTestRunner().run(suite)
+
diff --git a/setup.py b/setup.py
index 8317c343545aea95172ab19342bf10c6a7d60390..b4a0739e34df62b0d3314cee1688fdbd3a05fa71 100644
--- a/setup.py
+++ b/setup.py
@@ -12,9 +12,9 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib')))
 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'src')))
 
 print(sys.path)
-includes = ["sip", "re", "json", "logging", "hashlib", "os", "urllib", "ucoinpy", "requests"]
+includes = ["sip", "re", "json", "logging", "hashlib", "os", "urllib", "ucoinpy", "pylibscrypt", "requests"]
 excludes = []
-packages = ["libnacl", "pylibscrypt"]
+packages = ["libnacl"]
 
 includefiles = []
 if sys.platform == "win32":