1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-26 09:21:28 +00:00

Merge pull request #70 from PQClean/windows-tests

Windows tests
This commit is contained in:
Douglas Stebila 2019-03-07 08:09:16 -05:00 committed by GitHub
commit 8f347af262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 195 additions and 127 deletions

View File

@ -10,6 +10,5 @@
--add-braces --add-braces
--convert-tabs --convert-tabs
--mode=c --mode=c
--lineend=linux
# disable backup files # disable backup files
--suffix=none --suffix=none

View File

@ -10,7 +10,7 @@ version: 2
- run: - run:
name: Run the tests in a container name: Run the tests in a container
command: | command: |
docker run --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " docker run -e CI=true --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c "
uname -a && uname -a &&
export CC=${CC} && export CC=${CC} &&
cd test && python3 -m nose --rednose --verbose" cd test && python3 -m nose --rednose --verbose"

View File

@ -1,5 +1,5 @@
--- ---
Checks: '*,-llvm-header-guard,-hicpp-*,-readability-function-size' Checks: '*,-llvm-header-guard,-hicpp-*,-readability-function-size,-google-readability-todo'
WarningsAsErrors: '*' WarningsAsErrors: '*'
HeaderFilterRegex: '.*' HeaderFilterRegex: '.*'
AnalyzeTemporaryDtors: false AnalyzeTemporaryDtors: false

View File

@ -5,8 +5,23 @@ image: Visual Studio 2017
build: build:
verbosity: minimal verbosity: minimal
environment:
matrix:
- BITS: 64
- BITS: 32
init: init:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars%BITS%.bat"
# Download AStyle 3.1: first enable strong crypto in Invoke-WebRequest
- ps: Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319" -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
- ps: Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319" -Name "SchUseStrongCrypto" -Value '1' -Type DWord
# Add Python to PATH
- set PATH="C:\\Python37";"C:\\Python37\Scripts";%PATH%
build_script: build_script:
- scripts_windows\build_all.bat - python -m pip install -r requirements.txt
- cd test
# Download Astyle to local folder because putting it in PATH doesn't work
- ps: Invoke-WebRequest -OutFile "astyle.exe" "https://rded.nl/pqclean/AStyle.exe"
# Run tests
- python -m nose -v --rednose

View File

@ -33,6 +33,7 @@ THE SOFTWARE.
#if defined(_WIN32) #if defined(_WIN32)
/* Windows */ /* Windows */
// NOLINTNEXTLINE(llvm-include-order): Include order required by Windows
#include <windows.h> #include <windows.h>
#include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */ #include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
#endif /* defined(_WIN32) */ #endif /* defined(_WIN32) */

View File

@ -1,16 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: # This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake # nmake /f Makefile.Microsoft_nmake
LIB=libkyber768_clean.lib LIBRARY=libkyber768_clean.lib
OBJECTS=cbd.obj indcpa.obj kem.obj ntt.obj poly.obj polyvec.obj precomp.obj reduce.obj verify.obj OBJECTS=cbd.obj indcpa.obj kem.obj ntt.obj poly.obj polyvec.obj precomp.obj reduce.obj verify.obj
CFLAGS=/I ..\..\..\common /W4 /WX CFLAGS=/nologo /I ..\..\..\common /W4 /WX
all: $(LIB) all: $(LIBRARY)
$(LIB): $(OBJECTS) # Make sure objects are recompiled if headers change.
LIB.EXE /OUT:$@ $** $(OBJECTS): *.h
$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**
clean: clean:
DEL $(OBJECTS) -DEL $(OBJECTS)
DEL $(LIB) -DEL $(LIBRARY)

View File

@ -1,16 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: # This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake # nmake /f Makefile.Microsoft_nmake
LIB=libdilithium-iii_clean.lib LIBRARY=libdilithium-iii_clean.lib
OBJECTS=ntt.obj packing.obj poly.obj polyvec.obj reduce.obj rounding.obj sign.obj OBJECTS=ntt.obj packing.obj poly.obj polyvec.obj reduce.obj rounding.obj sign.obj
CFLAGS=/I ..\..\..\common /W1 /WX # FIXME: ideally would use /W4 instead of /W1, but too many failures in Dilithium right now CFLAGS=/nologo /I ..\..\..\common /W1 /WX # FIXME: ideally would use /W4 instead of /W1, but too many failures in Dilithium right now
all: $(LIB) all: $(LIBRARY)
$(LIB): $(OBJECTS) # Make sure objects are recompiled if headers change.
LIB.EXE /OUT:$@ $** $(OBJECTS): *.h
$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**
clean: clean:
DEL $(OBJECTS) -DEL $(OBJECTS)
DEL $(LIB) -DEL $(LIBRARY)

View File

@ -1,4 +1,5 @@
#ifndef API_H #ifndef API_H
#include <stdint.h>
#define API_H #define API_H
#include <stddef.h> #include <stddef.h>

View File

@ -1,44 +0,0 @@
@ECHO OFF
SETLOCAL EnableDelayedExpansion
SET EL=0
REM CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
REM IF ERRORLEVEL 1 SET EL=1
:: Build the library files for individual implementations
FOR /D %%K IN (crypto_kem\* crypto_sign\*) DO (
FOR /D %%L IN (%%K\*) DO (
CD %%L
nmake /f Makefile.Microsoft_nmake clean
IF ERRORLEVEL 1 SET EL=2
nmake /f Makefile.Microsoft_nmake
IF ERRORLEVEL 1 SET EL=3
CD ..\..\..
)
)
:: Build and run the functional tests and test vector programs for each implementation
FOR %%T IN (kem sign) DO (
CD crypto_%%T
FOR /D %%K IN (*) DO (
SET schemeuppercase=%%K
FOR %%B IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET "schemeuppercase=!schemeuppercase:%%B=%%B!"
SET "schemeuppercase=!schemeuppercase:-=!"
CD %%K
FOR /D %%L IN (*) DO (
CD ..\..\test
nmake /f Makefile.Microsoft_nmake /E TYPE=%%T SCHEME=%%K SCHEME_UPPERCASE=!schemeuppercase! IMPLEMENTATION=%%L clean
IF ERRORLEVEL 1 SET EL=4
nmake /f Makefile.Microsoft_nmake /E TYPE=%%T SCHEME=%%K SCHEME_UPPERCASE=!schemeuppercase! IMPLEMENTATION=%%L
IF ERRORLEVEL 1 SET EL=5
CD ..
bin\functest_%%K_%%L
IF ERRORLEVEL 1 SET EL=6
CD crypto_%%T\%%K
)
CD ..
)
CD ..
)
EXIT /b %EL%

View File

@ -1,7 +1,7 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: # This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake # nmake /f Makefile.Microsoft_nmake
# override as desired # override as desired, use /E
# vim: set ts=4 sw=4 et: # vim: set ts=4 sw=4 et:
TYPE=kem TYPE=kem
SCHEME=kyber768 SCHEME=kyber768
@ -17,7 +17,7 @@ COMMON_OBJECTS_NOPATH=fips202.obj sha2.obj
DEST_DIR=..\bin DEST_DIR=..\bin
CFLAGS=/I $(COMMON_DIR) /W4 /WX CFLAGS=/nologo /I $(COMMON_DIR) /W4 /WX
all: $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).EXE $(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).EXE all: $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).EXE $(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).EXE
@ -31,17 +31,23 @@ clean-scheme:
nmake /f Makefile.Microsoft_nmake clean nmake /f Makefile.Microsoft_nmake clean
cd ..\..\..\test cd ..\..\..\test
$(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).EXE: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\randombytes.obj functest: $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).exe
testvectors: $(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).exe
$(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\randombytes.obj
-MKDIR $(DEST_DIR) -MKDIR $(DEST_DIR)
-DEL functest.obj
$(CC) /c crypto_$(TYPE)\functest.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) $(CC) /c crypto_$(TYPE)\functest.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE)
LINK.EXE /OUT:$@ functest.obj $(COMMON_OBJECTS_NOPATH) randombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib Advapi32.lib LINK.EXE /OUT:$@ functest.obj $(COMMON_OBJECTS_NOPATH) randombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib Advapi32.lib
$(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).EXE: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\notrandombytes.obj $(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\notrandombytes.obj
-MKDIR $(DEST_DIR) -MKDIR $(DEST_DIR)
-DEL testvectors.obj
$(CC) /c crypto_$(TYPE)\testvectors.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) $(CC) /c crypto_$(TYPE)\testvectors.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE)
LINK.EXE /OUT:$@ testvectors.obj $(COMMON_OBJECTS_NOPATH) notrandombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib LINK.EXE /OUT:$@ testvectors.obj $(COMMON_OBJECTS_NOPATH) notrandombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib
clean: clean:
-DEL functest.obj testvectors.obj -DEL functest.obj testvectors.obj
-DEL $(COMMON_OBJECTS_NOPATH) randombytes.obj notrandombytes.obj -DEL $(COMMON_OBJECTS_NOPATH) randombytes.obj notrandombytes.obj
-DEL $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).EXE -DEL $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).exe

View File

@ -98,9 +98,9 @@ static int test_sign(void) {
// twice // twice
if ((returncode = if ((returncode =
crypto_sign_open(sm + 8, &mlen, sm + 8, smlen, pk + 8)) != 0) { crypto_sign_open(sm + 8, &mlen, sm + 8, smlen, pk + 8)) != 0) {
printf("ERROR Signature did not verify correctly!\n"); fprintf(stderr, "ERROR Signature did not verify correctly!\n");
if (returncode > 0) { if (returncode > 0) {
puts("ERROR return code should be < 0 on failure"); fprintf(stderr, "ERROR return code should be < 0 on failure");
} }
return 1; return 1;
} }
@ -109,7 +109,7 @@ static int test_sign(void) {
check_canary(sk) || check_canary(sk + CRYPTO_SECRETKEYBYTES + 8) || check_canary(sk) || check_canary(sk + CRYPTO_SECRETKEYBYTES + 8) ||
check_canary(sm) || check_canary(sm + MLEN + CRYPTO_BYTES + 8) || check_canary(sm) || check_canary(sm + MLEN + CRYPTO_BYTES + 8) ||
check_canary(m) || check_canary(m + MLEN + 8)) { check_canary(m) || check_canary(m + MLEN + 8)) {
printf("ERROR canary overwritten\n"); fprintf(stderr, "ERROR canary overwritten\n");
return 1; return 1;
} }
} }
@ -143,9 +143,9 @@ static int test_wrong_pk(void) {
// twice // twice
returncode = crypto_sign_open(sm, &mlen, sm, smlen, pk2); returncode = crypto_sign_open(sm, &mlen, sm, smlen, pk2);
if (!returncode) { if (!returncode) {
printf("ERROR Signature did verify correctly under wrong public key!\n"); fprintf(stderr, "ERROR Signature did verify correctly under wrong public key!\n");
if (returncode > 0) { if (returncode > 0) {
puts("ERROR return code should be < 0"); fprintf(stderr, "ERROR return code should be < 0");
} }
return 1; return 1;
} }

View File

@ -32,6 +32,7 @@ int main(void) {
uint8_t mi[MAXMLEN]; uint8_t mi[MAXMLEN];
uint8_t sm[MAXMLEN + CRYPTO_BYTES]; uint8_t sm[MAXMLEN + CRYPTO_BYTES];
size_t smlen; size_t smlen;
size_t mlen; size_t mlen;

View File

@ -1,5 +1,9 @@
import functools
import os import os
import subprocess import subprocess
import unittest
import shutil
import sys
def run_subprocess(command, working_dir='.', env=None, expected_returncode=0): def run_subprocess(command, working_dir='.', env=None, expected_returncode=0):
@ -15,6 +19,7 @@ def run_subprocess(command, working_dir='.', env=None, expected_returncode=0):
# Note we need to capture stdout/stderr from the subprocess, # Note we need to capture stdout/stderr from the subprocess,
# then print it, which nose/unittest will then capture and # then print it, which nose/unittest will then capture and
# buffer appropriately # buffer appropriately
print(working_dir + " > " + " ".join(command))
result = subprocess.run( result = subprocess.run(
command, command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -22,26 +27,74 @@ def run_subprocess(command, working_dir='.', env=None, expected_returncode=0):
cwd=working_dir, cwd=working_dir,
env=env, env=env,
) )
print(working_dir + " > " + " ".join(command))
print(result.stdout.decode('utf-8')) print(result.stdout.decode('utf-8'))
assert(result.returncode == expected_returncode) assert result.returncode == expected_returncode, \
"Got unexpected return code {}".format(result.returncode)
return result.stdout.decode('utf-8') return result.stdout.decode('utf-8')
def make(*args, working_dir='.', env=None, **kwargs): def make(*args, working_dir='.', env=None, expected_returncode=0, **kwargs):
""" """
Runs a make target in the specified working directory Runs a make target in the specified working directory
Usage: Usage:
make('clean', 'targetb', SCHEME='bla') make('clean', 'targetb', SCHEME='bla')
""" """
make_command = 'make' if os.name == 'nt':
make_command = ['nmake', '/f', 'Makefile.Microsoft_nmake',
'/NOLOGO', '/E']
# we need SCHEME_UPPERCASE and IMPLEMENTATION_UPPERCASE with nmake
for envvar in ['IMPLEMENTATION', 'SCHEME']:
if envvar in kwargs:
kwargs['{}_UPPERCASE'.format(envvar)] = (
kwargs[envvar].upper().replace('-', ''))
else:
make_command = ['make']
return run_subprocess( return run_subprocess(
[ [
make_command, *make_command,
*args,
*['{}={}'.format(k, v) for k, v in kwargs.items()], *['{}={}'.format(k, v) for k, v in kwargs.items()],
*args,
], ],
working_dir=working_dir, working_dir=working_dir,
env=env, env=env,
expected_returncode=expected_returncode,
) )
def skip_windows(message="This test is not supported on Windows"):
def wrapper(f):
@functools.wraps(f)
def skip_windows(*args, **kwargs):
raise unittest.SkipTest(message)
if os.name == 'nt':
return skip_windows
else:
return f
return wrapper
def ensure_available(executable):
"""
Checks if a command is available.
If a command MUST be available, because we are in a CI environment,
raises an AssertionError.
In the docker containers, on Travis and on Windows, CI=true is set.
"""
path = shutil.which(executable)
if path:
print("Found", path)
return path
# Installing clang-tidy on LLVM will be too much of a mess.
if ((executable == 'clang-tidy' and sys.platform == 'darwin')
or 'CI' not in os.environ):
raise unittest.SkipTest(
"{} is not available on PATH. Install it to run this test.{}"
.format(executable, "" if not os.name == 'nt'
else "On Windows, make sure to add it to PATH")
)
raise AssertionError("{} not available on CI".format(executable))

View File

@ -1,4 +1,5 @@
import os import os
import glob
import yaml import yaml
@ -73,8 +74,20 @@ class Implementation:
return os.path.join(self.scheme.path(), self.name) return os.path.join(self.scheme.path(), self.name)
def libname(self) -> str: def libname(self) -> str:
if os.name == 'nt':
return "lib{}_{}.lib".format(self.scheme.name, self.name)
return "lib{}_{}.a".format(self.scheme.name, self.name) return "lib{}_{}.a".format(self.scheme.name, self.name)
def cfiles(self) -> [str]:
return glob.glob(os.path.join(self.path(), '*.c'))
def hfiles(self) -> [str]:
return glob.glob(os.path.join(self.path(), '*.h'))
def ofiles(self) -> [str]:
return glob.glob(os.path.join(self.path(),
'*.o' if os.name != 'nt' else '*.obj'))
@staticmethod @staticmethod
def by_name(scheme_name, implementation_name): def by_name(scheme_name, implementation_name):
scheme = Scheme.by_name(scheme_name) scheme = Scheme.by_name(scheme_name)

View File

@ -9,8 +9,6 @@ import unittest
def test_dynamic_memory(): def test_dynamic_memory():
if sys.platform not in ['linux', 'darwin']:
raise unittest.SkipTest()
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
# Keep this loop outside, to allow multiple assertions # Keep this loop outside, to allow multiple assertions
@ -18,6 +16,7 @@ def test_dynamic_memory():
yield (check_dynamic_memory, implementation, function) yield (check_dynamic_memory, implementation, function)
@helpers.skip_windows()
def check_dynamic_memory(implementation, function): def check_dynamic_memory(implementation, function):
# 'make' will take care of not rebuilding existing library files # 'make' will take care of not rebuilding existing library files
helpers.make(working_dir=implementation.path()) helpers.make(working_dir=implementation.path())

View File

@ -1,8 +1,5 @@
import os
from glob import glob
import pqclean import pqclean
from helpers import run_subprocess from helpers import run_subprocess, ensure_available
def test_formatting(): def test_formatting():
@ -12,8 +9,9 @@ def test_formatting():
def check_format(implementation: pqclean.Implementation): def check_format(implementation: pqclean.Implementation):
cfiles = glob(os.path.join(implementation.path(), '*.c')) ensure_available('astyle')
hfiles = glob(os.path.join(implementation.path(), '*.h')) cfiles = implementation.cfiles()
hfiles = implementation.hfiles()
run_subprocess(['astyle', run_subprocess(['astyle',
'--dry-run', '--dry-run',
'--options=../.astylerc', '--options=../.astylerc',

View File

@ -24,25 +24,32 @@ def test_functest_sanitizers():
def check_functest(implementation): def check_functest(implementation):
helpers.make(TYPE=implementation.scheme.type, helpers.make('functest',
TYPE=implementation.scheme.type,
SCHEME=implementation.scheme.name, SCHEME=implementation.scheme.name,
IMPLEMENTATION=implementation.name, IMPLEMENTATION=implementation.name,
working_dir=os.path.join('..', 'test')) working_dir=os.path.join('..', 'test'))
helpers.run_subprocess( helpers.run_subprocess(
['./functest_{}_{}'.format(implementation.scheme.name, [os.path.join('..', 'bin', 'functest_{}_{}{}'.format(
implementation.name)], implementation.scheme.name,
implementation.name,
'.exe' if os.name == 'nt' else ''
))],
os.path.join('..', 'bin'), os.path.join('..', 'bin'),
) )
@helpers.skip_windows
def check_functest_sanitizers(implementation): def check_functest_sanitizers(implementation):
env = None env = None
if platform.machine() == 'ppc' and os.environ.get('CC', 'gcc') == 'clang': if platform.machine() == 'ppc' and os.environ.get('CC', 'gcc') == 'clang':
raise unittest.SkipTest() raise unittest.SkipTest("Clang does not support ASAN on ppc")
elif platform.machine() in ['armv7l', 'aarch64']: elif platform.machine() in ['armv7l', 'aarch64']:
env = {'ASAN_OPTIONS': 'detect_leaks=0'} env = {'ASAN_OPTIONS': 'detect_leaks=0'}
else: else:
print("Supported platform: {}".format(platform.machine())) print("Supported platform: {}".format(platform.machine()))
helpers.ensure_available('valgrind')
helpers.make('clean-scheme', 'functest', helpers.make('clean-scheme', 'functest',
TYPE=implementation.scheme.type, TYPE=implementation.scheme.type,
SCHEME=implementation.scheme.name, SCHEME=implementation.scheme.name,
@ -51,8 +58,11 @@ def check_functest_sanitizers(implementation):
working_dir=os.path.join('..', 'test'), working_dir=os.path.join('..', 'test'),
env=env) env=env)
helpers.run_subprocess( helpers.run_subprocess(
['./functest_{}_{}'.format(implementation.scheme.name, [os.path.join('..', 'bin', 'functest_{}_{}{}'.format(
implementation.name)], implementation.scheme.name,
implementation.name,
'.exe' if os.name == 'nt' else ''
))],
os.path.join('..', 'bin'), os.path.join('..', 'bin'),
env=env, env=env,
) )

View File

@ -1,10 +1,8 @@
import os import os
from glob import glob from glob import glob
import shutil
import unittest
import pqclean import pqclean
from helpers import run_subprocess from helpers import run_subprocess, ensure_available
def test_clang_tidy(): def test_clang_tidy():
@ -14,8 +12,7 @@ def test_clang_tidy():
def check_tidy(implementation: pqclean.Implementation): def check_tidy(implementation: pqclean.Implementation):
if shutil.which('clang-tidy') is None: ensure_available('clang-tidy')
raise unittest.SkipTest("clang-tidy unavailable in PATH")
cfiles = glob(os.path.join(implementation.path(), '*.c')) cfiles = glob(os.path.join(implementation.path(), '*.c'))
common_files = glob(os.path.join('..', 'common', '*.c')) common_files = glob(os.path.join('..', 'common', '*.c'))
run_subprocess(['clang-tidy', run_subprocess(['clang-tidy',

View File

@ -19,41 +19,51 @@ def test_makefile_dependencies():
# test case for each candidate file # test case for each candidate file
cfiles = glob.glob(os.path.join(implementation.path(), '*.c')) cfiles = glob.glob(os.path.join(implementation.path(), '*.c'))
hfiles = glob.glob(os.path.join(implementation.path(), '*.h')) hfiles = glob.glob(os.path.join(implementation.path(), '*.h'))
for file in cfiles + hfiles: for file in (cfiles + hfiles):
yield (check_makefile_dependencies, implementation, file) yield (check_makefile_dependencies, implementation, file)
def touch(time, *files):
for path in files:
times = (time.timestamp(), time.timestamp())
os.utime(path, times)
def make_check(path, expect_error=False):
makeflag = '-q' if os.name != 'nt' else '/Q'
expected_returncode = 0
if expect_error:
expected_returncode = 1 if os.name != 'nt' else 255
helpers.make(makeflag, working_dir=path,
expected_returncode=expected_returncode)
def check_makefile_dependencies(implementation, file): def check_makefile_dependencies(implementation, file):
cfiles = glob.glob(os.path.join(implementation.path(), '*.c')) cfiles = implementation.cfiles()
hfiles = glob.glob(os.path.join(implementation.path(), '*.h')) hfiles = implementation.hfiles()
ofiles = glob.glob(os.path.join(implementation.path(), '*.o')) ofiles = implementation.ofiles()
libfile = os.path.join(implementation.path(), implementation.libname()) libfile = os.path.join(implementation.path(), implementation.libname())
# modification time-based calculations is tricky on a sub-second basis # modification time-based calculations is tricky on a sub-second basis
# so we reset all the modification times to a known and "sensible" order # so we reset all the modification times to a known and "sensible" order
now = datetime.datetime.now() now = datetime.datetime.now() - datetime.timedelta(seconds=10)
ago15 = now - datetime.timedelta(seconds=15) ago15 = now - datetime.timedelta(minutes=15)
ago10 = now - datetime.timedelta(seconds=10) ago10 = now - datetime.timedelta(minutes=10)
ago5 = now - datetime.timedelta(seconds=5) ago5 = now - datetime.timedelta(minutes=5)
formatstring = "%Y%m%d%H%M.%S"
helpers.run_subprocess( touch(ago15, *cfiles, *hfiles)
['touch', '-t', ago15.strftime(formatstring)] + cfiles + hfiles) touch(ago10, *ofiles)
helpers.run_subprocess( touch(ago5, libfile)
['touch', '-t', ago10.strftime(formatstring)] + ofiles)
helpers.run_subprocess(
['touch', '-t', ago5.strftime(formatstring), libfile])
# Sanity check: the scheme is up to date # Sanity check: the scheme is up to date
helpers.run_subprocess(['make', '-q'], implementation.path(), make_check(implementation.path())
expected_returncode=0)
# touch the candidate .c / .h file # touch the candidate .c / .h file
helpers.run_subprocess(['touch', '-t', now.strftime(formatstring), file]) touch(now, file)
# check if it needs to be rebuilt using make -q # check if it needs to be rebuilt using make -q
helpers.run_subprocess(['make', '-q'], implementation.path(), make_check(implementation.path(), expect_error=True)
expected_returncode=1)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -23,7 +23,7 @@ def check_metadata(scheme):
specification = itertools.chain(specification, specification = itertools.chain(specification,
SIGNATURE_FIELDS.items()) SIGNATURE_FIELDS.items())
else: else:
assert(False) assert False, "Wrong type of metadata"
check_spec(copy.deepcopy(metadata), specification) check_spec(copy.deepcopy(metadata), specification)

View File

@ -10,18 +10,17 @@ import unittest
def test_symbol_namespace(): def test_symbol_namespace():
if sys.platform not in ['linux', 'darwin']:
raise unittest.SkipTest()
for scheme in pqclean.Scheme.all_schemes(): for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations: for implementation in scheme.implementations:
yield check_symbol_namespace, implementation yield check_symbol_namespace, implementation
def check_symbol_namespace(implementation): def check_symbol_namespace(implementation):
if sys.platform not in ['linux', 'darwin']:
raise unittest.SkipTest("Unsupported platform")
helpers.make(working_dir=implementation.path()) helpers.make(working_dir=implementation.path())
out = helpers.run_subprocess( out = helpers.run_subprocess(
['nm', '-g', 'lib{}_{}.a'.format(implementation.scheme.name, ['nm', '-g', implementation.libname()],
implementation.name)],
implementation.path() implementation.path()
) )

View File

@ -16,15 +16,19 @@ def test_testvectors():
def check_vectors(implementation): def check_vectors(implementation):
helpers.make(TYPE=implementation.scheme.type, helpers.make('testvectors',
TYPE=implementation.scheme.type,
SCHEME=implementation.scheme.name, SCHEME=implementation.scheme.name,
IMPLEMENTATION=implementation.name, IMPLEMENTATION=implementation.name,
working_dir=os.path.join('..', 'test')) working_dir=os.path.join('..', 'test'))
out = helpers.run_subprocess( out = helpers.run_subprocess(
['./testvectors_{}_{}'.format(implementation.scheme.name, [os.path.join('..', 'bin', 'testvectors_{}_{}{}'.format(
implementation.name)], implementation.scheme.name,
implementation.name,
'.exe' if os.name == 'nt' else ''
))],
os.path.join('..', 'bin'), os.path.join('..', 'bin'),
) ).replace('\r', '')
assert(implementation.scheme.metadata()['testvectors-sha256'].lower() assert(implementation.scheme.metadata()['testvectors-sha256'].lower()
== hashlib.sha256(out.encode('utf-8')).hexdigest().lower()) == hashlib.sha256(out.encode('utf-8')).hexdigest().lower())