Spaces:
Running
Running
# =================================================================== | |
# | |
# Copyright (c) 2016, Legrandin <[email protected]> | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions | |
# are met: | |
# | |
# 1. Redistributions of source code must retain the above copyright | |
# notice, this list of conditions and the following disclaimer. | |
# 2. Redistributions in binary form must reproduce the above copyright | |
# notice, this list of conditions and the following disclaimer in | |
# the documentation and/or other materials provided with the | |
# distribution. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
# =================================================================== | |
import os | |
import re | |
import json | |
import errno | |
import binascii | |
import warnings | |
from binascii import unhexlify | |
from Crypto.Util.py3compat import FileNotFoundError | |
try: | |
import pycryptodome_test_vectors # type: ignore | |
test_vectors_available = True | |
except ImportError: | |
test_vectors_available = False | |
def _load_tests(dir_comps, file_in, description, conversions): | |
"""Load and parse a test vector file | |
Return a list of objects, one per group of adjacent | |
KV lines or for a single line in the form "[.*]". | |
For a group of lines, the object has one attribute per line. | |
""" | |
line_number = 0 | |
results = [] | |
class TestVector(object): | |
def __init__(self, description, count): | |
self.desc = description | |
self.count = count | |
self.others = [] | |
test_vector = None | |
count = 0 | |
new_group = True | |
while True: | |
line_number += 1 | |
line = file_in.readline() | |
if not line: | |
if test_vector is not None: | |
results.append(test_vector) | |
break | |
line = line.strip() | |
# Skip comments and empty lines | |
if line.startswith('#') or not line: | |
new_group = True | |
continue | |
if line.startswith("["): | |
if test_vector is not None: | |
results.append(test_vector) | |
test_vector = None | |
results.append(line) | |
continue | |
if new_group: | |
count += 1 | |
new_group = False | |
if test_vector is not None: | |
results.append(test_vector) | |
test_vector = TestVector("%s (#%d)" % (description, count), count) | |
res = re.match("([A-Za-z0-9]+) = ?(.*)", line) | |
if not res: | |
test_vector.others += [line] | |
else: | |
token = res.group(1).lower() | |
data = res.group(2).lower() | |
conversion = conversions.get(token, None) | |
if conversion is None: | |
if len(data) % 2 != 0: | |
data = "0" + data | |
setattr(test_vector, token, binascii.unhexlify(data)) | |
else: | |
setattr(test_vector, token, conversion(data)) | |
# This line is ignored | |
return results | |
def load_test_vectors(dir_comps, file_name, description, conversions): | |
"""Load and parse a test vector file | |
This function returns a list of objects, one per group of adjacent | |
KV lines or for a single line in the form "[.*]". | |
For a group of lines, the object has one attribute per line. | |
""" | |
results = None | |
try: | |
if not test_vectors_available: | |
raise FileNotFoundError(errno.ENOENT, | |
os.strerror(errno.ENOENT), | |
file_name) | |
description = "%s test (%s)" % (description, file_name) | |
init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) | |
full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) | |
with open(full_file_name) as file_in: | |
results = _load_tests(dir_comps, file_in, description, conversions) | |
except FileNotFoundError: | |
warnings.warn("Warning: skipping extended tests for " + description, | |
UserWarning, | |
stacklevel=2) | |
return results | |
def load_test_vectors_wycheproof(dir_comps, file_name, description, | |
root_tag={}, group_tag={}, unit_tag={}): | |
result = [] | |
try: | |
if not test_vectors_available: | |
raise FileNotFoundError(errno.ENOENT, | |
os.strerror(errno.ENOENT), | |
file_name) | |
init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) | |
full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) | |
with open(full_file_name) as file_in: | |
tv_tree = json.load(file_in) | |
except FileNotFoundError: | |
warnings.warn("Warning: skipping extended tests for " + description, | |
UserWarning, | |
stacklevel=2) | |
return result | |
class TestVector(object): | |
pass | |
common_root = {} | |
for k, v in root_tag.items(): | |
common_root[k] = v(tv_tree) | |
for group in tv_tree['testGroups']: | |
common_group = {} | |
for k, v in group_tag.items(): | |
common_group[k] = v(group) | |
for test in group['tests']: | |
tv = TestVector() | |
for k, v in common_root.items(): | |
setattr(tv, k, v) | |
for k, v in common_group.items(): | |
setattr(tv, k, v) | |
tv.id = test['tcId'] | |
tv.comment = test['comment'] | |
for attr in 'key', 'iv', 'aad', 'msg', 'ct', 'tag', 'label', 'ikm', 'salt', 'info', 'okm', 'sig': | |
if attr in test: | |
setattr(tv, attr, unhexlify(test[attr])) | |
tv.filename = file_name | |
for k, v in unit_tag.items(): | |
setattr(tv, k, v(test)) | |
tv.valid = test['result'] != "invalid" | |
tv.warning = test['result'] == "acceptable" | |
result.append(tv) | |
return result | |