summaryrefslogtreecommitdiffstats
path: root/lib/Crypto/SelfTest/loader.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Crypto/SelfTest/loader.py')
-rw-r--r--lib/Crypto/SelfTest/loader.py206
1 files changed, 206 insertions, 0 deletions
diff --git a/lib/Crypto/SelfTest/loader.py b/lib/Crypto/SelfTest/loader.py
new file mode 100644
index 0000000..18be270
--- /dev/null
+++ b/lib/Crypto/SelfTest/loader.py
@@ -0,0 +1,206 @@
+# ===================================================================
+#
+# Copyright (c) 2016, Legrandin <helderijs@gmail.com>
+# 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
+