Commit 831f6441 authored by inso's avatar inso

Adding files for continous integration

parent f4d371d2
language: python
python:
# We don't actually use the Travis Python, but this keeps it organized.
- "3.4"
before_install:
# Update
- pwd
- sudo apt-get update -qq
- sudo apt-get install libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev -qq
- sudo apt-get install libgl1-mesa-dri libegl1-mesa libpcre3-dev
# Install and configure conda
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda config --add channels inso/channel/cutecoin
- conda config --add channels pyzo
- conda update -q conda
# Useful for debugging any issues with conda
- conda info -a
install:
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION cx_freeze pyqt5 libpng=1.5.13 libsodium=1.0.3
- source activate test-environment
- ldd $HOME/miniconda/envs/test-environment/lib/qt5/plugins/platforms/*.so
- pip install pylibscrypt
- pip install libnacl
- pip install requests
- pip install base58
- python gen_resources.py
- python gen_translations.py
- python setup.py build
before_script:
# screen must be 24bpp otherwise pyqt5 crashes
# see: https://github.com/pytest-dev/pytest-qt/issues/35
- export XVFBARGS="-screen 0 1280x1024x24"
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 3
script:
- export QT_QPA_PLATFORM_PLUGIN_PATH=$HOME/miniconda/envs/test-environment/lib/qt5/plugins/platforms;
- export QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb
- python run_tests.py
[![Code Health](https://landscape.io/github/ucoin-io/cutecoin/dev/landscape.svg?style=flat)](https://landscape.io/github/ucoin-io/cutecoin/dev)
Landscape | [![Code Health](https://landscape.io/github/ucoin-io/cutecoin/dev/landscape.svg?style=flat)](https://landscape.io/github/ucoin-io/cutecoin/dev)
![cutecoin logo](https://raw.github.com/ucoin-io/cutecoin/master/cutecoin.png)
Travis | [![Build Status](https://travis-ci.org/ucoin-io/cutecoin.svg?branch=travis)](https://travis-ci.org/ucoin-io/cutecoin)
Appveyor | [![Build status](https://ci.appveyor.com/api/projects/status/0wmo0rk5mds5t3lr/branch/dev)](https://ci.appveyor.com/project/Insoleet/cutecoin/branch/dev)
![cutecoin logo](https://raw.github.com/ucoin-io/cutecoin/master/cutecoin.png)
cutecoin
========
......
environment:
global:
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
# /E:ON and /V:ON options are not enabled in the batch script intepreter
# See: http://stackoverflow.com/a/13751649/163740
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\appveyor\\run_with_env.cmd"
matrix:
- PYTHON: "C:\\Python34_64"
PYTHON_VERSION: "3.4"
PYTHON_ARCH: "64"
CONDA_PY: "34"
CONDA_NPY: "18"
platform: x64
- PYTHON: "C:\\Python34_32"
PYTHON_VERSION: "3.4"
PYTHON_ARCH: "32"
CONDA_PY: "34"
CONDA_NPY: "18"
platform: x86
install:
# this installs the appropriate Miniconda (Py2/Py3, 32/64 bit),
# as well as pip, conda-build, and the binstar CLI
- powershell .\\ci\\appveyor\\install.ps1
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "SET QT_QPA_PLATFORM_PLUGIN_PATH=%PYTHON%\\envs\\test-environment\\Scripts\\plugins"
- choco install -y vcredist2013
- "%CMD_IN_ENV% conda config --set always_yes yes --set changeps1 no"
- "%CMD_IN_ENV% conda config --add channels inso/channel/cutecoin"
- "%CMD_IN_ENV% conda config --add channels pyzo"
- "%CMD_IN_ENV% conda create -q -n test-environment python=%PYTHON_VERSION% cx_freeze pyqt5 libsodium=1.0.3"
build_script:
- ".\\ci\\appveyor\\build.cmd"
test_script:
- ".\\ci\\appveyor\\tests.cmd"
- echo %errorlevel%
artifacts:
- path: build
name: Cutecoin-win$(PYTHON_ARCH)
deploy:
release: Cutecoin $(APPVEYOR_REPO_TAG_NAME)
provider: GitHub
auth_token:
secure: wbzlh6nx1zY1J1avlB0C3hKGm1abFNHBdM60u/U09i5Nam//D6kazvnv5ZBKdR89 # your encrypted token from GitHub
artifact: Cutecoin-win$(PYTHON_ARCH) # upload to releases
draft: true
prerelease: true
on:
branch: appveyor # release from master branch only
appveyor_repo_tag: true # deploy on tag push only
@ECHO ON
call activate test-environment
echo "%PATH%"
echo "%QT_QPA_PLATFORM_PLUGIN_PATH%"
python -V
call pyuic5 --version
pyrcc5 -version
lrelease -version
pip install pylibscrypt
pip install libnacl
pip install requests
pip install base58
python gen_resources.py
if %errorlevel% neq 0 exit /b 1s
python gen_translations.py
if %errorlevel% neq 0 exit /b 1
python setup.py build
if %errorlevel% neq 0 exit /b 1
\ No newline at end of file
# Sample script to install Miniconda under Windows
# Authors: Olivier Grisel, Jonathan Helmus and Kyle Kastner, Robert McGibbon
# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
$MINICONDA_URL = "http://repo.continuum.io/miniconda/"
function DownloadMiniconda ($python_version, $platform_suffix) {
$webclient = New-Object System.Net.WebClient
if ($python_version -match "3.4") {
$filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe"
} else {
$filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe"
}
$url = $MINICONDA_URL + $filename
$basedir = $pwd.Path + "\"
$filepath = $basedir + $filename
if (Test-Path $filename) {
Write-Host "Reusing" $filepath
return $filepath
}
# Download and retry up to 3 times in case of network transient errors.
Write-Host "Downloading" $filename "from" $url
$retry_attempts = 2
for($i=0; $i -lt $retry_attempts; $i++){
try {
$webclient.DownloadFile($url, $filepath)
break
}
Catch [Exception]{
Start-Sleep 1
}
}
if (Test-Path $filepath) {
Write-Host "File saved at" $filepath
} else {
# Retry once to get the error message if any at the last try
$webclient.DownloadFile($url, $filepath)
}
return $filepath
}
function InstallMiniconda ($python_version, $architecture, $python_home) {
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
if (Test-Path $python_home) {
Write-Host $python_home "already exists, skipping."
return $false
}
if ($architecture -match "32") {
$platform_suffix = "x86"
} else {
$platform_suffix = "x86_64"
}
$filepath = DownloadMiniconda $python_version $platform_suffix
Write-Host "Installing" $filepath "to" $python_home
$install_log = $python_home + ".log"
$args = "/S /D=$python_home"
Write-Host $filepath $args
Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru
if (Test-Path $python_home) {
Write-Host "Python $python_version ($architecture) installation complete"
} else {
Write-Host "Failed to install Python in $python_home"
Get-Content -Path $install_log
Exit 1
}
}
function InstallCondaPackages ($python_home, $spec) {
$conda_path = $python_home + "\Scripts\conda.exe"
$args = "install --yes " + $spec
Write-Host ("conda " + $args)
Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
}
function UpdateConda ($python_home) {
$conda_path = $python_home + "\Scripts\conda.exe"
Write-Host "Updating conda..."
$args = "update --yes conda"
Write-Host $conda_path $args
Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
}
function main () {
InstallMiniconda $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
UpdateConda $env:PYTHON
InstallCondaPackages $env:PYTHON "conda-build=1.4.0 pip jinja2 binstar"
}
main
:: To build extensions for 64 bit Python 3, we need to configure environment
:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1)
::
:: To build extensions for 64 bit Python 2, we need to configure environment
:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
::
:: 32 bit builds do not require specific environment configurations.
::
:: Note: this script needs to be run with the /E:ON and /V:ON flags for the
:: cmd interpreter, at least for (SDK v7.0)
::
:: More details at:
:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
:: http://stackoverflow.com/a/13751649/163740
::
:: Author: Olivier Grisel
:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
@ECHO OFF
SET COMMAND_TO_RUN=%*
SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%"
IF %MAJOR_PYTHON_VERSION% == "2" (
SET WINDOWS_SDK_VERSION="v7.0"
) ELSE IF %MAJOR_PYTHON_VERSION% == "3" (
SET WINDOWS_SDK_VERSION="v7.1"
) ELSE (
ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
EXIT 1
)
IF "%PYTHON_ARCH%"=="64" (
ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture
SET DISTUTILS_USE_SDK=1
SET MSSdk=1
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION%
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release
ECHO Executing: %COMMAND_TO_RUN%
call %COMMAND_TO_RUN% || exit 1
) ELSE (
ECHO Using default MSVC build environment for 32 bit architecture
ECHO Executing: %COMMAND_TO_RUN%
call %COMMAND_TO_RUN% || exit 1
)
@ECHO ON
call activate test-environment
echo "%PATH%"
echo "%QT_QPA_PLATFORM_PLUGIN_PATH%"
python -V
call pyuic5 --version
pyrcc5 -version
lrelease -version
python run_tests.py
if %errorlevel% neq 0 exit /b 1
\ No newline at end of file
import sys, os, multiprocessing, subprocess, time
import sys, os, multiprocessing, subprocess, time, shutil
gen_resources = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
ts = os.path.abspath(os.path.join(os.path.dirname(__file__), 'res', 'i18n', 'ts'))
......@@ -27,7 +27,7 @@ def prepare_qm():
for (ts_file, qm_file) in translations:
# avoid conflict with qt4 lrelease by running qtchooser directly
if sys.platform.startswith('win'):
if sys.platform.startswith('win') or shutil.which("qtchooser") == None:
subprocess.call(["lrelease", ts_file, "-qm", qm_file])
else:
subprocess.call(["qtchooser", "-run-tool=lrelease", "-qt=5", ts_file, "-qm", qm_file])
......
# 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']
#!/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))
# 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')
This diff is collapsed.
#!/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')
#!/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,