Skip to content

Commit

Permalink
Merge pull request #44 from jvarho/v2
Browse files Browse the repository at this point in the history
Drop support for old python versions and clean up
  • Loading branch information
jvarho committed Feb 7, 2021
2 parents 9329432 + b9de5ba commit fba02b0
Show file tree
Hide file tree
Showing 19 changed files with 86 additions and 116 deletions.
3 changes: 2 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ exclude_lines =
except AttributeError:
except TypeError

omit = /build/*,/usr/*,pylibscrypt/libsodium_load.py,pylibscrypt/test_properties.py,pylibscrypt/tests.py,test_fallback.py
include = pylibscrypt/*
omit = pylibscrypt/libsodium_load.py,pylibscrypt/test_properties.py,pylibscrypt/tests.py

3 changes: 2 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Release History
???

Major release:
- Drops support for python 3.3 and <2.7.8
- Drops support for python 2.x and 3.3
- Drops legacy backends


1.8.0
Expand Down
3 changes: 1 addition & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2014-2019, Jan Varho
Copyright (c) 2014-2021, 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
Expand All @@ -11,4 +11,3 @@ 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.

10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ all: inline
inline: pylibscrypt/pypyscrypt_inline.py

pylibscrypt/pypyscrypt_inline.py: pylibscrypt/inline.py pylibscrypt/pypyscrypt.py
env python -m pylibscrypt.inline
env python3 -m pylibscrypt.inline


clean:
Expand All @@ -19,23 +19,23 @@ distclean: clean


test: inline
env python -m pylibscrypt.tests
env python3 -m pylibscrypt.tests


fuzz: inline
env python -m pylibscrypt.fuzz
env python3 -m pylibscrypt.fuzz


coverage: inline
./run_coverage.sh


bench: inline
env python -m pylibscrypt.bench
env python3 -m pylibscrypt.bench


pypi-upload:
env python setup.py sdist upload -r https://upload.pypi.org/legacy/
env python3 setup.py sdist upload -r https://upload.pypi.org/legacy/


docker-build:
Expand Down
7 changes: 4 additions & 3 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ everything that I'd like, so here's One More[1].

Features
--
* Uses hashlib.scrypt on Python 3.6 and OpenSSL 1.1.
* Uses hashlib.scrypt on Python 3.6+ and OpenSSL 1.1+.
* Uses system libscrypt[2] as the next choice.
* If neither is available, tries the scrypt Python module[3] or libsodium[4].
* Offers a pure Python scrypt implementation for when there is no C scrypt.
Expand All @@ -23,12 +23,13 @@ speed of C scrypt. With CPython it is about 250x slower.

Requirements
--
* Python 2.7.8+ or 3.4+. Equivalent versions of PyPy should also work.
* Python 3.4+. Equivalent versions of PyPy should also work.
* For Python 2.7.8+ support install the latest version 1.x instead.
* If you want speed, you should use one of:
- Python 3.6+ with OpenSSL 1.1+
- libscrypt 1.8+ (older may work)
- py-scrypt 0.6+ (pip install scrypt)
- libsodium 1.0+
- Python 3.6+ with OpenSSL 1.1+


Usage
Expand Down
10 changes: 5 additions & 5 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#!/usr/bin/env python3

# Copyright (c) 2014-2015, Jan Varho
# Copyright (c) 2014-2021, 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
Expand All @@ -22,9 +22,9 @@
print(b16encode(scrypt(b'Hello World', b'salt')))

# Generate an MCF hash with random salt
mcf = scrypt_mcf(b'Hello World')
mcf = scrypt_mcf('Hello World')

# Test it
print(scrypt_mcf_check(mcf, b'Hello World'))
print(scrypt_mcf_check(mcf, b'HelloPyWorld'))
print(scrypt_mcf_check(mcf, 'Hello World'))
print(scrypt_mcf_check(mcf, 'HelloPyWorld'))

6 changes: 3 additions & 3 deletions pylibscrypt/bench.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014-2015, Jan Varho
# Copyright (c) 2014-2021, 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
Expand All @@ -25,7 +25,7 @@
Nmax = 20

t1 = time.time()
for i in xrange(1, Nmax+1):
for i in range(1, Nmax+1):
pyscrypt(b'password', b'NaCl', N=2**i)
if time.time() - t1 > tmin:
Nmax = i
Expand All @@ -35,7 +35,7 @@
print('Python scrypt took %.2fs' % t1)

t3 = time.time()
for i in xrange(1, Nmax+1):
for i in range(1, Nmax+1):
scrypt(b'password', b'NaCl', N=2**i)
t3 = time.time() - t3
print('C scrypt took %.2fs' % t3)
Expand Down
12 changes: 1 addition & 11 deletions pylibscrypt/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014-2018, Jan Varho
# Copyright (c) 2014-2021, 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
Expand Down Expand Up @@ -31,16 +31,6 @@
# key derivation is not a problem, you could use 16 as in libscrypt or better
# yet increase N if memory is plentiful.

try:
xrange = xrange
except:
xrange = range

try:
unicode = unicode
except:
unicode = str


def check_args(password, salt, N, r, p, olen=64):
if not isinstance(password, bytes):
Expand Down
14 changes: 7 additions & 7 deletions pylibscrypt/mcf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014-2018, Jan Varho
# Copyright (c) 2014-2021, 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
Expand Down Expand Up @@ -47,7 +47,7 @@

from .common import (
SCRYPT_N, SCRYPT_r, SCRYPT_p, SCRYPT_MCF_PREFIX_7, SCRYPT_MCF_PREFIX_s1,
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY, unicode)
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY)


def _scrypt_mcf_encode_s1(N, r, p, salt, hash):
Expand Down Expand Up @@ -112,7 +112,7 @@ def _scrypt_mcf_decode_s1(mcf):
def _cb64enc(arr):
arr = bytearray(arr)
out = bytearray()
val = bits = pos = 0
val = bits = 0
for b in arr:
val += b << bits
bits += 8
Expand Down Expand Up @@ -147,7 +147,7 @@ def _scrypt_mcf_encode_7(N, r, p, salt, hash):

def _cb64dec(arr):
out = bytearray()
val = bits = pos = 0
val = bits = 0
for b in arr:
val += _icb64[b] << bits
bits += 6
Expand Down Expand Up @@ -183,7 +183,7 @@ 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
_N, _r, _p, salt, _hash, hlen = params
return len(salt) == 43 and hlen == 32


Expand All @@ -205,7 +205,7 @@ def scrypt_mcf(scrypt, password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
If no salt is given, a random salt of 128+ bits is used. (Recommended.)
"""
if isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down Expand Up @@ -244,7 +244,7 @@ def scrypt_mcf_check(scrypt, mcf, password):
"""
if not isinstance(mcf, bytes):
raise TypeError('MCF must be a byte string')
if isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down
8 changes: 4 additions & 4 deletions pylibscrypt/pylibscrypt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014-2018, Jan Varho
# Copyright (c) 2014-2021, 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
Expand All @@ -23,7 +23,7 @@

from .common import (
SCRYPT_N, SCRYPT_r, SCRYPT_p, SCRYPT_MCF_PREFIX_s1,
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY, check_args, unicode)
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY, check_args)
from . import mcf as mcf_mod


Expand Down Expand Up @@ -111,7 +111,7 @@ def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
"""
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 isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down Expand Up @@ -146,7 +146,7 @@ def scrypt_mcf_check(mcf, password):
"""Returns True if the password matches the given MCF hash"""
if not isinstance(mcf, bytes):
raise TypeError('MCF must be a byte string')
if isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down
12 changes: 5 additions & 7 deletions pylibscrypt/pylibsodium.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014-2018, Jan Varho
# Copyright (c) 2014-2021, 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
Expand All @@ -17,14 +17,12 @@

import ctypes
from ctypes import c_char_p, c_size_t, c_uint64, c_uint32, c_void_p
import platform
from warnings import catch_warnings, filterwarnings

from . import mcf as mcf_mod
from . import libsodium_load
from .common import (
SCRYPT_N, SCRYPT_r, SCRYPT_p, SCRYPT_MCF_PREFIX_7, SCRYPT_MCF_PREFIX_s1,
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY, check_args, unicode)
SCRYPT_MCF_PREFIX_DEFAULT, SCRYPT_MCF_PREFIX_ANY, check_args)
from . import pypyscrypt_inline as scr_mod


Expand Down Expand Up @@ -149,7 +147,7 @@ def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
If no salt is given, a random salt of 128+ bits is used. (Recommended.)
"""
if isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down Expand Up @@ -177,14 +175,14 @@ def scrypt_mcf(password, salt=None, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p,
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])
_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 isinstance(password, unicode):
if isinstance(password, str):
password = password.encode('utf8')
elif not isinstance(password, bytes):
raise TypeError('password must be a unicode or byte string')
Expand Down
23 changes: 11 additions & 12 deletions pylibscrypt/pypyscrypt.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Copyright (c) 2014 Richard Moore
# Copyright (c) 2014-2019 Jan Varho
# Copyright (c) 2014-2021 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
Expand Down Expand Up @@ -37,16 +37,15 @@

from . import mcf as mcf_mod
from .common import (
SCRYPT_N, SCRYPT_r, SCRYPT_p, SCRYPT_MCF_PREFIX_DEFAULT, xrange,
check_args)
SCRYPT_N, SCRYPT_r, SCRYPT_p, SCRYPT_MCF_PREFIX_DEFAULT, check_args)


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):
for i in range(length):
dest[d_start + i] ^= source[s_start + i]


Expand All @@ -68,11 +67,11 @@ 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):
for i in range(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):
for i in range(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)
Expand All @@ -84,7 +83,7 @@ def salsa20_8(B, x, src, s_start, dest, d_start):

# 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):
for i in range(16):
dest[d_start + i] = B[i] = (x[i] + B[i]) & 0xffffffff


Expand All @@ -95,12 +94,12 @@ def blockmix_salsa8(BY, Yi, r):
X = BY[start:start+16] # BlockMix - 1
tmp = [0]*16

for i in xrange(2 * r): # BlockMix - 2
for i in range(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
for i in range(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)

Expand All @@ -110,11 +109,11 @@ def smix(B, Bi, r, N, V, X):

array_overwrite(B, Bi, X, 0, 32 * r) # ROMix - 1

for i in xrange(N): # ROMix - 2
for i in range(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
for i in range(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)
Expand Down Expand Up @@ -153,7 +152,7 @@ def scrypt(password, salt, N=SCRYPT_N, r=SCRYPT_r, p=SCRYPT_p, olen=64):
except (MemoryError, OverflowError):
raise ValueError("scrypt parameters don't fit in memory")

for i in xrange(p):
for i in range(p):
smix(B, i * 32 * r, r, N, V, XY)

B = struct.pack('<%dI' % len(B), *B)
Expand Down
Loading

0 comments on commit fba02b0

Please sign in to comment.