blob: 497fde83f0a546eacca54c195c599d0350d031da [file] [log] [blame]
Alex Gaynor5951f462014-11-16 09:08:42 -08001# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
Alex Gaynorf312a5c2013-08-10 15:23:38 -04004
Alex Gaynorc37feed2014-03-08 08:32:56 -08005from __future__ import absolute_import, division, print_function
6
Alex Stapletonc387cf72014-04-13 13:58:02 +01007import binascii
Alex Gaynor36e651c2014-01-27 10:08:35 -08008import collections
Alex Gaynor2e85a922018-07-16 11:18:33 -04009import json
Alex Gaynor2e85a922018-07-16 11:18:33 -040010import os
Alex Stapletonc387cf72014-04-13 13:58:02 +010011import re
Alex Stapleton707b0082014-04-20 22:24:41 +010012from contextlib import contextmanager
Paul Kehrer90450f32014-03-19 12:37:17 -040013
Alex Stapletona39a3192014-03-14 20:03:12 +000014import pytest
15
Paul Kehrerafc1ccd2014-03-19 11:49:32 -040016import six
Alex Gaynor2b3f9422013-12-24 21:55:24 -080017
Alex Gaynor7a489db2014-03-22 15:09:34 -070018from cryptography.exceptions import UnsupportedAlgorithm
Alex Gaynor07c4dcc2014-04-05 11:22:07 -070019
Alex Stapletona39a3192014-03-14 20:03:12 +000020import cryptography_vectors
Matthew Iversen68e77c72014-03-13 08:54:43 +110021
Alex Gaynor2b3f9422013-12-24 21:55:24 -080022
Alex Gaynor36e651c2014-01-27 10:08:35 -080023HashVector = collections.namedtuple("HashVector", ["message", "digest"])
24KeyedHashVector = collections.namedtuple(
25 "KeyedHashVector", ["message", "digest", "key"]
26)
27
28
Alex Gaynore6055fb2017-06-03 22:02:50 -040029def check_backend_support(backend, item):
Paul Kehrer03229622018-09-06 22:56:46 -050030 for mark in item.node.iter_markers("supported"):
31 if not mark.kwargs["only_if"](backend):
Lucia Lic6ba99d2021-11-08 22:06:11 +080032 pytest.skip("{} ({})".format(mark.kwargs["skip_message"], backend))
Paul Kehrer5a8fdf82013-12-26 20:13:45 -060033
34
Alex Gaynor7a489db2014-03-22 15:09:34 -070035@contextmanager
Alex Stapleton5e4c8c32014-03-27 16:38:00 +000036def raises_unsupported_algorithm(reason):
Alex Gaynor7a489db2014-03-22 15:09:34 -070037 with pytest.raises(UnsupportedAlgorithm) as exc_info:
Alex Stapleton112963e2014-03-26 17:39:29 +000038 yield exc_info
Alex Stapleton5e4c8c32014-03-27 16:38:00 +000039
Alex Stapleton85a791f2014-03-27 16:55:41 +000040 assert exc_info.value._reason is reason
Alex Gaynor7a489db2014-03-22 15:09:34 -070041
42
Paul Kehrerfdae0702014-11-27 07:50:46 -100043def load_vectors_from_file(filename, loader, mode="r"):
44 with cryptography_vectors.open_vector_file(filename, mode) as vector_file:
Alex Stapletona39a3192014-03-14 20:03:12 +000045 return loader(vector_file)
Paul Kehrerf7f6a9f2013-11-11 20:43:52 -060046
47
Alex Gaynord3ce7032013-11-11 14:46:20 -080048def load_nist_vectors(vector_data):
Paul Kehrer749ac5b2013-11-18 18:12:41 -060049 test_data = None
50 data = []
Donald Stufft9e1a48b2013-08-09 00:32:30 -040051
52 for line in vector_data:
53 line = line.strip()
54
Paul Kehrer749ac5b2013-11-18 18:12:41 -060055 # Blank lines, comments, and section headers are ignored
Lucia Lic6ba99d2021-11-08 22:06:11 +080056 if (
57 not line
58 or line.startswith("#")
59 or (line.startswith("[") and line.endswith("]"))
60 ):
Alex Gaynor521c42d2013-11-11 14:25:59 -080061 continue
62
Paul Kehrera43b6692013-11-12 15:35:49 -060063 if line.strip() == "FAIL":
Paul Kehrer749ac5b2013-11-18 18:12:41 -060064 test_data["fail"] = True
Paul Kehrera43b6692013-11-12 15:35:49 -060065 continue
66
Donald Stufft9e1a48b2013-08-09 00:32:30 -040067 # Build our data using a simple Key = Value format
Paul Kehrera43b6692013-11-12 15:35:49 -060068 name, value = [c.strip() for c in line.split("=")]
Donald Stufft9e1a48b2013-08-09 00:32:30 -040069
Paul Kehrer1050ddf2014-01-27 21:04:03 -060070 # Some tests (PBKDF2) contain \0, which should be interpreted as a
71 # null character rather than literal.
72 value = value.replace("\\0", "\0")
73
Donald Stufft9e1a48b2013-08-09 00:32:30 -040074 # COUNT is a special token that indicates a new block of data
75 if name.upper() == "COUNT":
Paul Kehrer749ac5b2013-11-18 18:12:41 -060076 test_data = {}
77 data.append(test_data)
78 continue
Donald Stufft9e1a48b2013-08-09 00:32:30 -040079 # For all other tokens we simply want the name, value stored in
80 # the dictionary
81 else:
Paul Kehrer749ac5b2013-11-18 18:12:41 -060082 test_data[name.lower()] = value.encode("ascii")
Donald Stufft9e1a48b2013-08-09 00:32:30 -040083
Paul Kehrer749ac5b2013-11-18 18:12:41 -060084 return data
Donald Stufft9e1a48b2013-08-09 00:32:30 -040085
86
Paul Kehrer1951bf62013-09-15 12:05:43 -050087def load_cryptrec_vectors(vector_data):
Paul Kehrere5805982013-09-27 11:26:01 -050088 cryptrec_list = []
Paul Kehrer1951bf62013-09-15 12:05:43 -050089
90 for line in vector_data:
91 line = line.strip()
92
93 # Blank lines and comments are ignored
94 if not line or line.startswith("#"):
95 continue
96
97 if line.startswith("K"):
Paul Kehrere5805982013-09-27 11:26:01 -050098 key = line.split(" : ")[1].replace(" ", "").encode("ascii")
Paul Kehrer1951bf62013-09-15 12:05:43 -050099 elif line.startswith("P"):
Paul Kehrere5805982013-09-27 11:26:01 -0500100 pt = line.split(" : ")[1].replace(" ", "").encode("ascii")
Paul Kehrer1951bf62013-09-15 12:05:43 -0500101 elif line.startswith("C"):
Paul Kehrere5805982013-09-27 11:26:01 -0500102 ct = line.split(" : ")[1].replace(" ", "").encode("ascii")
103 # after a C is found the K+P+C tuple is complete
104 # there are many P+C pairs for each K
Lucia Lic6ba99d2021-11-08 22:06:11 +0800105 cryptrec_list.append(
106 {"key": key, "plaintext": pt, "ciphertext": ct}
107 )
Donald Stufft3359d7e2013-10-19 19:33:06 -0400108 else:
109 raise ValueError("Invalid line in file '{}'".format(line))
Paul Kehrer1951bf62013-09-15 12:05:43 -0500110 return cryptrec_list
111
112
Paul Kehrer69e06522013-10-18 17:28:39 -0500113def load_hash_vectors(vector_data):
114 vectors = []
Paul Kehrer1bb8b712013-10-27 17:00:14 -0500115 key = None
116 msg = None
117 md = None
Paul Kehrer69e06522013-10-18 17:28:39 -0500118
119 for line in vector_data:
120 line = line.strip()
121
Paul Kehrer87cd0db2013-10-18 18:01:26 -0500122 if not line or line.startswith("#") or line.startswith("["):
Paul Kehrer69e06522013-10-18 17:28:39 -0500123 continue
124
125 if line.startswith("Len"):
126 length = int(line.split(" = ")[1])
Paul Kehrer0317b042013-10-28 17:34:27 -0500127 elif line.startswith("Key"):
Alex Gaynor36e651c2014-01-27 10:08:35 -0800128 # HMAC vectors contain a key attribute. Hash vectors do not.
Paul Kehrer0317b042013-10-28 17:34:27 -0500129 key = line.split(" = ")[1].encode("ascii")
Paul Kehrer69e06522013-10-18 17:28:39 -0500130 elif line.startswith("Msg"):
Alex Gaynor36e651c2014-01-27 10:08:35 -0800131 # In the NIST vectors they have chosen to represent an empty
132 # string as hex 00, which is of course not actually an empty
133 # string. So we parse the provided length and catch this edge case.
Paul Kehrer69e06522013-10-18 17:28:39 -0500134 msg = line.split(" = ")[1].encode("ascii") if length > 0 else b""
Paul Kehrer5fe88ea2019-01-19 00:52:43 -0600135 elif line.startswith("MD") or line.startswith("Output"):
Paul Kehrer69e06522013-10-18 17:28:39 -0500136 md = line.split(" = ")[1]
Paul Kehrer0317b042013-10-28 17:34:27 -0500137 # after MD is found the Msg+MD (+ potential key) tuple is complete
Paul Kehrer00dd5092013-10-23 09:41:49 -0500138 if key is not None:
Alex Gaynor36e651c2014-01-27 10:08:35 -0800139 vectors.append(KeyedHashVector(msg, md, key))
Paul Kehrer1bb8b712013-10-27 17:00:14 -0500140 key = None
141 msg = None
142 md = None
Paul Kehrer00dd5092013-10-23 09:41:49 -0500143 else:
Alex Gaynor36e651c2014-01-27 10:08:35 -0800144 vectors.append(HashVector(msg, md))
Paul Kehrer1bb8b712013-10-27 17:00:14 -0500145 msg = None
146 md = None
Paul Kehrer69e06522013-10-18 17:28:39 -0500147 else:
148 raise ValueError("Unknown line in hash vector")
149 return vectors
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000150
151
152def load_pkcs1_vectors(vector_data):
153 """
154 Loads data out of RSA PKCS #1 vector files.
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000155 """
156 private_key_vector = None
157 public_key_vector = None
158 attr = None
159 key = None
Paul Kehrerefca2802014-02-17 20:55:13 -0600160 example_vector = None
161 examples = []
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000162 vectors = []
163 for line in vector_data:
Paul Kehrer7774a032014-02-17 22:56:55 -0600164 if (
Lucia Lic6ba99d2021-11-08 22:06:11 +0800165 line.startswith("# PSS Example")
166 or line.startswith("# OAEP Example")
167 or line.startswith("# PKCS#1 v1.5")
Paul Kehrer7774a032014-02-17 22:56:55 -0600168 ):
Paul Kehrerefca2802014-02-17 20:55:13 -0600169 if example_vector:
170 for key, value in six.iteritems(example_vector):
Paul Kehrer26811802014-02-19 16:32:11 -0600171 hex_str = "".join(value).replace(" ", "").encode("ascii")
Paul Kehrerefca2802014-02-17 20:55:13 -0600172 example_vector[key] = hex_str
173 examples.append(example_vector)
174
175 attr = None
176 example_vector = collections.defaultdict(list)
177
Paul Kehrer3fe91502014-03-29 12:08:39 -0500178 if line.startswith("# Message"):
Paul Kehrer7d9c3062014-02-18 08:27:39 -0600179 attr = "message"
Paul Kehrerefca2802014-02-17 20:55:13 -0600180 continue
181 elif line.startswith("# Salt"):
182 attr = "salt"
183 continue
Paul Kehrer3fe91502014-03-29 12:08:39 -0500184 elif line.startswith("# Seed"):
185 attr = "seed"
186 continue
Paul Kehrerefca2802014-02-17 20:55:13 -0600187 elif line.startswith("# Signature"):
188 attr = "signature"
189 continue
Paul Kehrer3fe91502014-03-29 12:08:39 -0500190 elif line.startswith("# Encryption"):
191 attr = "encryption"
192 continue
Lucia Lic6ba99d2021-11-08 22:06:11 +0800193 elif example_vector and line.startswith(
194 "# ============================================="
Paul Kehrerefca2802014-02-17 20:55:13 -0600195 ):
196 for key, value in six.iteritems(example_vector):
Paul Kehrer26811802014-02-19 16:32:11 -0600197 hex_str = "".join(value).replace(" ", "").encode("ascii")
Paul Kehrerefca2802014-02-17 20:55:13 -0600198 example_vector[key] = hex_str
199 examples.append(example_vector)
200 example_vector = None
201 attr = None
202 elif example_vector and line.startswith("#"):
203 continue
204 else:
205 if attr is not None and example_vector is not None:
206 example_vector[attr].append(line.strip())
207 continue
208
Lucia Lic6ba99d2021-11-08 22:06:11 +0800209 if line.startswith("# Example") or line.startswith(
210 "# ============================================="
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000211 ):
212 if key:
213 assert private_key_vector
214 assert public_key_vector
215
216 for key, value in six.iteritems(public_key_vector):
217 hex_str = "".join(value).replace(" ", "")
218 public_key_vector[key] = int(hex_str, 16)
219
220 for key, value in six.iteritems(private_key_vector):
221 hex_str = "".join(value).replace(" ", "")
222 private_key_vector[key] = int(hex_str, 16)
223
Paul Kehrerefca2802014-02-17 20:55:13 -0600224 private_key_vector["examples"] = examples
225 examples = []
226
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000227 assert (
Lucia Lic6ba99d2021-11-08 22:06:11 +0800228 private_key_vector["public_exponent"]
229 == public_key_vector["public_exponent"]
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000230 )
231
232 assert (
Lucia Lic6ba99d2021-11-08 22:06:11 +0800233 private_key_vector["modulus"]
234 == public_key_vector["modulus"]
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000235 )
236
Lucia Lic6ba99d2021-11-08 22:06:11 +0800237 vectors.append((private_key_vector, public_key_vector))
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000238
239 public_key_vector = collections.defaultdict(list)
240 private_key_vector = collections.defaultdict(list)
241 key = None
242 attr = None
243
244 if private_key_vector is None or public_key_vector is None:
Alex Gaynora87daea2017-10-11 21:36:30 -0400245 # Random garbage to defeat CPython's peephole optimizer so that
246 # coverage records correctly: https://bugs.python.org/issue2506
247 1 + 1
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000248 continue
249
250 if line.startswith("# Private key"):
251 key = private_key_vector
252 elif line.startswith("# Public key"):
253 key = public_key_vector
254 elif line.startswith("# Modulus:"):
255 attr = "modulus"
256 elif line.startswith("# Public exponent:"):
257 attr = "public_exponent"
258 elif line.startswith("# Exponent:"):
259 if key is public_key_vector:
260 attr = "public_exponent"
261 else:
262 assert key is private_key_vector
263 attr = "private_exponent"
264 elif line.startswith("# Prime 1:"):
265 attr = "p"
266 elif line.startswith("# Prime 2:"):
267 attr = "q"
Paul Kehrer09328bb2014-02-12 23:57:27 -0600268 elif line.startswith("# Prime exponent 1:"):
269 attr = "dmp1"
270 elif line.startswith("# Prime exponent 2:"):
271 attr = "dmq1"
272 elif line.startswith("# Coefficient:"):
273 attr = "iqmp"
Alex Stapleton58f27ac2014-02-02 19:30:03 +0000274 elif line.startswith("#"):
275 attr = None
276 else:
277 if key is not None and attr is not None:
278 key[attr].append(line.strip())
279 return vectors
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400280
281
282def load_rsa_nist_vectors(vector_data):
283 test_data = None
Paul Kehrer62707f12014-03-18 07:19:14 -0400284 p = None
Paul Kehrerafc25182014-03-18 07:51:56 -0400285 salt_length = None
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400286 data = []
287
288 for line in vector_data:
289 line = line.strip()
290
291 # Blank lines and section headers are ignored
292 if not line or line.startswith("["):
293 continue
294
295 if line.startswith("# Salt len:"):
296 salt_length = int(line.split(":")[1].strip())
297 continue
298 elif line.startswith("#"):
299 continue
300
301 # Build our data using a simple Key = Value format
302 name, value = [c.strip() for c in line.split("=")]
303
304 if name == "n":
305 n = int(value, 16)
Paul Kehrer62707f12014-03-18 07:19:14 -0400306 elif name == "e" and p is None:
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400307 e = int(value, 16)
Paul Kehrer62707f12014-03-18 07:19:14 -0400308 elif name == "p":
309 p = int(value, 16)
310 elif name == "q":
311 q = int(value, 16)
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400312 elif name == "SHAAlg":
Paul Kehrer62707f12014-03-18 07:19:14 -0400313 if p is None:
314 test_data = {
315 "modulus": n,
316 "public_exponent": e,
317 "salt_length": salt_length,
Paul Kehrere66f69a2014-03-18 07:57:26 -0400318 "algorithm": value,
Lucia Lic6ba99d2021-11-08 22:06:11 +0800319 "fail": False,
Paul Kehrer62707f12014-03-18 07:19:14 -0400320 }
321 else:
Lucia Lic6ba99d2021-11-08 22:06:11 +0800322 test_data = {"modulus": n, "p": p, "q": q, "algorithm": value}
Paul Kehrerafc25182014-03-18 07:51:56 -0400323 if salt_length is not None:
324 test_data["salt_length"] = salt_length
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400325 data.append(test_data)
Paul Kehrer62707f12014-03-18 07:19:14 -0400326 elif name == "e" and p is not None:
327 test_data["public_exponent"] = int(value, 16)
328 elif name == "d":
329 test_data["private_exponent"] = int(value, 16)
330 elif name == "Result":
331 test_data["fail"] = value.startswith("F")
Paul Kehrer2f2a2062014-03-10 23:30:28 -0400332 # For all other tokens we simply want the name, value stored in
333 # the dictionary
334 else:
335 test_data[name.lower()] = value.encode("ascii")
336
337 return data
Mohammed Attia987cc702014-03-12 16:07:21 +0200338
339
340def load_fips_dsa_key_pair_vectors(vector_data):
341 """
342 Loads data out of the FIPS DSA KeyPair vector files.
343 """
344 vectors = []
Mohammed Attia987cc702014-03-12 16:07:21 +0200345 for line in vector_data:
346 line = line.strip()
347
Paul Kehrer47a66f12018-03-18 10:12:14 -0400348 if not line or line.startswith("#") or line.startswith("[mod"):
Mohammed Attia987cc702014-03-12 16:07:21 +0200349 continue
Alex Gaynor0fe7db62015-06-27 17:20:59 -0400350
Paul Kehrer47a66f12018-03-18 10:12:14 -0400351 if line.startswith("P"):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800352 vectors.append({"p": int(line.split("=")[1], 16)})
Paul Kehrer47a66f12018-03-18 10:12:14 -0400353 elif line.startswith("Q"):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800354 vectors[-1]["q"] = int(line.split("=")[1], 16)
Paul Kehrer47a66f12018-03-18 10:12:14 -0400355 elif line.startswith("G"):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800356 vectors[-1]["g"] = int(line.split("=")[1], 16)
357 elif line.startswith("X") and "x" not in vectors[-1]:
358 vectors[-1]["x"] = int(line.split("=")[1], 16)
359 elif line.startswith("X") and "x" in vectors[-1]:
360 vectors.append(
361 {
362 "p": vectors[-1]["p"],
363 "q": vectors[-1]["q"],
364 "g": vectors[-1]["g"],
365 "x": int(line.split("=")[1], 16),
366 }
367 )
Paul Kehrer47a66f12018-03-18 10:12:14 -0400368 elif line.startswith("Y"):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800369 vectors[-1]["y"] = int(line.split("=")[1], 16)
Mohammed Attia987cc702014-03-12 16:07:21 +0200370
371 return vectors
Alex Stapletoncf048602014-04-12 12:48:59 +0100372
373
Mohammed Attia3c9e1582014-04-22 14:24:44 +0200374def load_fips_dsa_sig_vectors(vector_data):
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200375 """
376 Loads data out of the FIPS DSA SigVer vector files.
377 """
378 vectors = []
379 sha_regex = re.compile(
380 r"\[mod = L=...., N=..., SHA-(?P<sha>1|224|256|384|512)\]"
381 )
Mohammed Attia3c9e1582014-04-22 14:24:44 +0200382
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200383 for line in vector_data:
384 line = line.strip()
385
386 if not line or line.startswith("#"):
387 continue
388
389 sha_match = sha_regex.match(line)
390 if sha_match:
Lucia Lic6ba99d2021-11-08 22:06:11 +0800391 digest_algorithm = "SHA-{}".format(sha_match.group("sha"))
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200392
Paul Kehrer47a66f12018-03-18 10:12:14 -0400393 if line.startswith("[mod"):
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200394 continue
395
396 name, value = [c.strip() for c in line.split("=")]
397
398 if name == "P":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800399 vectors.append(
400 {"p": int(value, 16), "digest_algorithm": digest_algorithm}
401 )
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200402 elif name == "Q":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800403 vectors[-1]["q"] = int(value, 16)
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200404 elif name == "G":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800405 vectors[-1]["g"] = int(value, 16)
406 elif name == "Msg" and "msg" not in vectors[-1]:
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200407 hexmsg = value.strip().encode("ascii")
Lucia Lic6ba99d2021-11-08 22:06:11 +0800408 vectors[-1]["msg"] = binascii.unhexlify(hexmsg)
409 elif name == "Msg" and "msg" in vectors[-1]:
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200410 hexmsg = value.strip().encode("ascii")
Lucia Lic6ba99d2021-11-08 22:06:11 +0800411 vectors.append(
412 {
413 "p": vectors[-1]["p"],
414 "q": vectors[-1]["q"],
415 "g": vectors[-1]["g"],
416 "digest_algorithm": vectors[-1]["digest_algorithm"],
417 "msg": binascii.unhexlify(hexmsg),
418 }
419 )
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200420 elif name == "X":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800421 vectors[-1]["x"] = int(value, 16)
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200422 elif name == "Y":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800423 vectors[-1]["y"] = int(value, 16)
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200424 elif name == "R":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800425 vectors[-1]["r"] = int(value, 16)
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200426 elif name == "S":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800427 vectors[-1]["s"] = int(value, 16)
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200428 elif name == "Result":
Lucia Lic6ba99d2021-11-08 22:06:11 +0800429 vectors[-1]["result"] = value.split("(")[0].strip()
Mohammed Attia0fb5d852014-04-21 10:31:15 +0200430
431 return vectors
432
433
Alex Gaynorf7567f62018-12-31 10:10:09 -0600434# https://tools.ietf.org/html/rfc4492#appendix-A
Alex Stapletonc387cf72014-04-13 13:58:02 +0100435_ECDSA_CURVE_NAMES = {
436 "P-192": "secp192r1",
437 "P-224": "secp224r1",
Alex Stapleton39e300f2014-04-18 22:44:02 +0100438 "P-256": "secp256r1",
Alex Stapletonc387cf72014-04-13 13:58:02 +0100439 "P-384": "secp384r1",
440 "P-521": "secp521r1",
441 "K-163": "sect163k1",
442 "K-233": "sect233k1",
Alex Stapletonf6a1cf62015-05-03 12:16:19 +0100443 "K-256": "secp256k1",
Alex Stapleton39e300f2014-04-18 22:44:02 +0100444 "K-283": "sect283k1",
Alex Stapletonc387cf72014-04-13 13:58:02 +0100445 "K-409": "sect409k1",
446 "K-571": "sect571k1",
Alex Stapleton44fe82d2014-04-19 09:44:26 +0100447 "B-163": "sect163r2",
Alex Stapletonc387cf72014-04-13 13:58:02 +0100448 "B-233": "sect233r1",
449 "B-283": "sect283r1",
450 "B-409": "sect409r1",
451 "B-571": "sect571r1",
452}
453
454
Alex Stapletoncf048602014-04-12 12:48:59 +0100455def load_fips_ecdsa_key_pair_vectors(vector_data):
456 """
457 Loads data out of the FIPS ECDSA KeyPair vector files.
458 """
459 vectors = []
460 key_data = None
Alex Stapletoncf048602014-04-12 12:48:59 +0100461 for line in vector_data:
462 line = line.strip()
463
464 if not line or line.startswith("#"):
465 continue
466
Alex Stapletonc387cf72014-04-13 13:58:02 +0100467 if line[1:-1] in _ECDSA_CURVE_NAMES:
468 curve_name = _ECDSA_CURVE_NAMES[line[1:-1]]
Alex Stapletoncf048602014-04-12 12:48:59 +0100469
470 elif line.startswith("d = "):
471 if key_data is not None:
472 vectors.append(key_data)
473
Lucia Lic6ba99d2021-11-08 22:06:11 +0800474 key_data = {"curve": curve_name, "d": int(line.split("=")[1], 16)}
Alex Stapletoncf048602014-04-12 12:48:59 +0100475
476 elif key_data is not None:
477 if line.startswith("Qx = "):
478 key_data["x"] = int(line.split("=")[1], 16)
479 elif line.startswith("Qy = "):
480 key_data["y"] = int(line.split("=")[1], 16)
481
Paul Kehrerb60b8dd2015-08-01 19:47:22 +0100482 assert key_data is not None
483 vectors.append(key_data)
Alex Stapletoncf048602014-04-12 12:48:59 +0100484
485 return vectors
Alex Stapletonc387cf72014-04-13 13:58:02 +0100486
487
488def load_fips_ecdsa_signing_vectors(vector_data):
489 """
490 Loads data out of the FIPS ECDSA SigGen vector files.
491 """
492 vectors = []
493
494 curve_rx = re.compile(
495 r"\[(?P<curve>[PKB]-[0-9]{3}),SHA-(?P<sha>1|224|256|384|512)\]"
496 )
497
498 data = None
499 for line in vector_data:
500 line = line.strip()
501
Alex Stapletonc387cf72014-04-13 13:58:02 +0100502 curve_match = curve_rx.match(line)
503 if curve_match:
504 curve_name = _ECDSA_CURVE_NAMES[curve_match.group("curve")]
Lucia Lic6ba99d2021-11-08 22:06:11 +0800505 digest_name = "SHA-{}".format(curve_match.group("sha"))
Alex Stapletonc387cf72014-04-13 13:58:02 +0100506
507 elif line.startswith("Msg = "):
508 if data is not None:
509 vectors.append(data)
510
511 hexmsg = line.split("=")[1].strip().encode("ascii")
512
513 data = {
514 "curve": curve_name,
515 "digest_algorithm": digest_name,
Lucia Lic6ba99d2021-11-08 22:06:11 +0800516 "message": binascii.unhexlify(hexmsg),
Alex Stapletonc387cf72014-04-13 13:58:02 +0100517 }
518
519 elif data is not None:
520 if line.startswith("Qx = "):
521 data["x"] = int(line.split("=")[1], 16)
522 elif line.startswith("Qy = "):
523 data["y"] = int(line.split("=")[1], 16)
524 elif line.startswith("R = "):
525 data["r"] = int(line.split("=")[1], 16)
526 elif line.startswith("S = "):
527 data["s"] = int(line.split("=")[1], 16)
528 elif line.startswith("d = "):
529 data["d"] = int(line.split("=")[1], 16)
Alex Stapleton6f729492014-04-19 09:01:25 +0100530 elif line.startswith("Result = "):
531 data["fail"] = line.split("=")[1].strip()[0] == "F"
Alex Stapletonc387cf72014-04-13 13:58:02 +0100532
Paul Kehrerb60b8dd2015-08-01 19:47:22 +0100533 assert data is not None
534 vectors.append(data)
Alex Stapletonc387cf72014-04-13 13:58:02 +0100535 return vectors
Alex Stapleton839c09d2014-08-10 12:18:02 +0100536
537
538def load_kasvs_dh_vectors(vector_data):
539 """
540 Loads data out of the KASVS key exchange vector data
541 """
542
543 result_rx = re.compile(r"([FP]) \(([0-9]+) -")
544
545 vectors = []
Lucia Lic6ba99d2021-11-08 22:06:11 +0800546 data = {"fail_z": False, "fail_agree": False}
Alex Stapleton839c09d2014-08-10 12:18:02 +0100547
548 for line in vector_data:
549 line = line.strip()
550
551 if not line or line.startswith("#"):
552 continue
553
554 if line.startswith("P = "):
555 data["p"] = int(line.split("=")[1], 16)
556 elif line.startswith("Q = "):
557 data["q"] = int(line.split("=")[1], 16)
558 elif line.startswith("G = "):
559 data["g"] = int(line.split("=")[1], 16)
560 elif line.startswith("Z = "):
561 z_hex = line.split("=")[1].strip().encode("ascii")
562 data["z"] = binascii.unhexlify(z_hex)
563 elif line.startswith("XstatCAVS = "):
564 data["x1"] = int(line.split("=")[1], 16)
565 elif line.startswith("YstatCAVS = "):
566 data["y1"] = int(line.split("=")[1], 16)
567 elif line.startswith("XstatIUT = "):
568 data["x2"] = int(line.split("=")[1], 16)
569 elif line.startswith("YstatIUT = "):
570 data["y2"] = int(line.split("=")[1], 16)
571 elif line.startswith("Result = "):
572 result_str = line.split("=")[1].strip()
573 match = result_rx.match(result_str)
574
575 if match.group(1) == "F":
576 if int(match.group(2)) in (5, 10):
577 data["fail_z"] = True
578 else:
579 data["fail_agree"] = True
580
581 vectors.append(data)
582
583 data = {
584 "p": data["p"],
585 "q": data["q"],
586 "g": data["g"],
587 "fail_z": False,
Lucia Lic6ba99d2021-11-08 22:06:11 +0800588 "fail_agree": False,
Alex Stapleton839c09d2014-08-10 12:18:02 +0100589 }
590
591 return vectors
Simo Sorce917addb2015-04-29 19:41:26 -0400592
593
594def load_kasvs_ecdh_vectors(vector_data):
595 """
596 Loads data out of the KASVS key exchange vector data
597 """
598
599 curve_name_map = {
600 "P-192": "secp192r1",
601 "P-224": "secp224r1",
602 "P-256": "secp256r1",
603 "P-384": "secp384r1",
604 "P-521": "secp521r1",
605 }
606
607 result_rx = re.compile(r"([FP]) \(([0-9]+) -")
608
609 tags = []
Alex Gaynorace036d2015-09-24 20:23:08 -0400610 sets = {}
Simo Sorce917addb2015-04-29 19:41:26 -0400611 vectors = []
612
613 # find info in header
614 for line in vector_data:
615 line = line.strip()
616
617 if line.startswith("#"):
618 parm = line.split("Parameter set(s) supported:")
619 if len(parm) == 2:
620 names = parm[1].strip().split()
621 for n in names:
622 tags.append("[%s]" % n)
623 break
624
625 # Sets Metadata
626 tag = None
627 curve = None
628 for line in vector_data:
629 line = line.strip()
630
631 if not line or line.startswith("#"):
632 continue
633
634 if line in tags:
635 tag = line
636 curve = None
637 elif line.startswith("[Curve selected:"):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800638 curve = curve_name_map[line.split(":")[1].strip()[:-1]]
Simo Sorce917addb2015-04-29 19:41:26 -0400639
640 if tag is not None and curve is not None:
641 sets[tag.strip("[]")] = curve
642 tag = None
643 if len(tags) == len(sets):
644 break
645
646 # Data
647 data = {
Alex Gaynorace036d2015-09-24 20:23:08 -0400648 "CAVS": {},
649 "IUT": {},
Simo Sorce917addb2015-04-29 19:41:26 -0400650 }
651 tag = None
652 for line in vector_data:
653 line = line.strip()
654
655 if not line or line.startswith("#"):
656 continue
657
658 if line.startswith("["):
659 tag = line.split()[0][1:]
660 elif line.startswith("COUNT = "):
Simo Sorce6e3b1552015-10-13 14:45:21 -0400661 data["COUNT"] = int(line.split("=")[1])
Simo Sorce917addb2015-04-29 19:41:26 -0400662 elif line.startswith("dsCAVS = "):
663 data["CAVS"]["d"] = int(line.split("=")[1], 16)
664 elif line.startswith("QsCAVSx = "):
665 data["CAVS"]["x"] = int(line.split("=")[1], 16)
666 elif line.startswith("QsCAVSy = "):
667 data["CAVS"]["y"] = int(line.split("=")[1], 16)
668 elif line.startswith("dsIUT = "):
669 data["IUT"]["d"] = int(line.split("=")[1], 16)
670 elif line.startswith("QsIUTx = "):
671 data["IUT"]["x"] = int(line.split("=")[1], 16)
672 elif line.startswith("QsIUTy = "):
673 data["IUT"]["y"] = int(line.split("=")[1], 16)
Simo Sorce83e563e2015-05-06 10:56:31 -0400674 elif line.startswith("OI = "):
675 data["OI"] = int(line.split("=")[1], 16)
Simo Sorce917addb2015-04-29 19:41:26 -0400676 elif line.startswith("Z = "):
677 data["Z"] = int(line.split("=")[1], 16)
Simo Sorce83e563e2015-05-06 10:56:31 -0400678 elif line.startswith("DKM = "):
679 data["DKM"] = int(line.split("=")[1], 16)
Simo Sorce917addb2015-04-29 19:41:26 -0400680 elif line.startswith("Result = "):
681 result_str = line.split("=")[1].strip()
682 match = result_rx.match(result_str)
683
684 if match.group(1) == "F":
685 data["fail"] = True
686 else:
687 data["fail"] = False
688 data["errno"] = int(match.group(2))
689
690 data["curve"] = sets[tag]
691
692 vectors.append(data)
693
694 data = {
Alex Gaynorace036d2015-09-24 20:23:08 -0400695 "CAVS": {},
696 "IUT": {},
Simo Sorce917addb2015-04-29 19:41:26 -0400697 }
698
699 return vectors
Simo Sorce7600dee2015-09-22 21:56:20 -0400700
701
702def load_x963_vectors(vector_data):
703 """
704 Loads data out of the X9.63 vector data
705 """
706
707 vectors = []
708
709 # Sets Metadata
710 hashname = None
Alex Gaynorace036d2015-09-24 20:23:08 -0400711 vector = {}
Simo Sorce7600dee2015-09-22 21:56:20 -0400712 for line in vector_data:
713 line = line.strip()
714
715 if line.startswith("[SHA"):
716 hashname = line[1:-1]
717 shared_secret_len = 0
718 shared_info_len = 0
719 key_data_len = 0
720 elif line.startswith("[shared secret length"):
721 shared_secret_len = int(line[1:-1].split("=")[1].strip())
722 elif line.startswith("[SharedInfo length"):
723 shared_info_len = int(line[1:-1].split("=")[1].strip())
724 elif line.startswith("[key data length"):
725 key_data_len = int(line[1:-1].split("=")[1].strip())
726 elif line.startswith("COUNT"):
727 count = int(line.split("=")[1].strip())
728 vector["hash"] = hashname
729 vector["count"] = count
Alex Gaynorace036d2015-09-24 20:23:08 -0400730 vector["shared_secret_length"] = shared_secret_len
731 vector["sharedinfo_length"] = shared_info_len
732 vector["key_data_length"] = key_data_len
Simo Sorce7600dee2015-09-22 21:56:20 -0400733 elif line.startswith("Z"):
734 vector["Z"] = line.split("=")[1].strip()
Lucia Lic6ba99d2021-11-08 22:06:11 +0800735 assert ((shared_secret_len + 7) // 8) * 2 == len(vector["Z"])
Simo Sorce7600dee2015-09-22 21:56:20 -0400736 elif line.startswith("SharedInfo"):
737 if shared_info_len != 0:
Alex Gaynorace036d2015-09-24 20:23:08 -0400738 vector["sharedinfo"] = line.split("=")[1].strip()
739 silen = len(vector["sharedinfo"])
Lucia Lic6ba99d2021-11-08 22:06:11 +0800740 assert ((shared_info_len + 7) // 8) * 2 == silen
Simo Sorce7600dee2015-09-22 21:56:20 -0400741 elif line.startswith("key_data"):
742 vector["key_data"] = line.split("=")[1].strip()
Lucia Lic6ba99d2021-11-08 22:06:11 +0800743 assert ((key_data_len + 7) // 8) * 2 == len(vector["key_data"])
Simo Sorce7600dee2015-09-22 21:56:20 -0400744 vectors.append(vector)
Alex Gaynorace036d2015-09-24 20:23:08 -0400745 vector = {}
Simo Sorce7600dee2015-09-22 21:56:20 -0400746
747 return vectors
Jaredcd258d52016-04-13 14:03:52 -0700748
749
750def load_nist_kbkdf_vectors(vector_data):
751 """
752 Load NIST SP 800-108 KDF Vectors
753 """
754 vectors = []
755 test_data = None
756 tag = {}
757
758 for line in vector_data:
759 line = line.strip()
760
761 if not line or line.startswith("#"):
762 continue
763
764 if line.startswith("[") and line.endswith("]"):
765 tag_data = line[1:-1]
766 name, value = [c.strip() for c in tag_data.split("=")]
Lucia Lic6ba99d2021-11-08 22:06:11 +0800767 if value.endswith("_BITS"):
768 value = int(value.split("_")[0])
Jaredcd258d52016-04-13 14:03:52 -0700769 tag.update({name.lower(): value})
770 continue
771
772 tag.update({name.lower(): value.lower()})
773 elif line.startswith("COUNT="):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800774 test_data = {}
Jaredcd258d52016-04-13 14:03:52 -0700775 test_data.update(tag)
776 vectors.append(test_data)
777 elif line.startswith("L"):
778 name, value = [c.strip() for c in line.split("=")]
779 test_data[name.lower()] = int(value)
780 else:
781 name, value = [c.strip() for c in line.split("=")]
782 test_data[name.lower()] = value.encode("ascii")
783
784 return vectors
Paul Kehrera923b002017-06-20 01:12:35 -1000785
786
787def load_ed25519_vectors(vector_data):
788 data = []
789 for line in vector_data:
Lucia Lic6ba99d2021-11-08 22:06:11 +0800790 secret_key, public_key, message, signature, _ = line.split(":")
Paul Kehrera923b002017-06-20 01:12:35 -1000791 # In the vectors the first element is secret key + public key
792 secret_key = secret_key[0:64]
793 # In the vectors the signature section is signature + message
794 signature = signature[0:128]
Lucia Lic6ba99d2021-11-08 22:06:11 +0800795 data.append(
796 {
797 "secret_key": secret_key,
798 "public_key": public_key,
799 "message": message,
800 "signature": signature,
801 }
802 )
Paul Kehrera923b002017-06-20 01:12:35 -1000803 return data
Paul Kehrer33a41e72017-06-21 01:39:20 -1000804
805
806def load_nist_ccm_vectors(vector_data):
807 test_data = None
808 section_data = None
809 global_data = {}
810 new_section = False
811 data = []
812
813 for line in vector_data:
814 line = line.strip()
815
816 # Blank lines and comments should be ignored
817 if not line or line.startswith("#"):
818 continue
819
820 # Some of the CCM vectors have global values for this. They are always
821 # at the top before the first section header (see: VADT, VNT, VPT)
822 if line.startswith(("Alen", "Plen", "Nlen", "Tlen")):
823 name, value = [c.strip() for c in line.split("=")]
824 global_data[name.lower()] = int(value)
825 continue
826
827 # section headers contain length data we might care about
828 if line.startswith("["):
829 new_section = True
830 section_data = {}
831 section = line[1:-1]
832 items = [c.strip() for c in section.split(",")]
833 for item in items:
834 name, value = [c.strip() for c in item.split("=")]
835 section_data[name.lower()] = int(value)
836 continue
837
838 name, value = [c.strip() for c in line.split("=")]
839
840 if name.lower() in ("key", "nonce") and new_section:
841 section_data[name.lower()] = value.encode("ascii")
842 continue
843
844 new_section = False
845
846 # Payload is sometimes special because these vectors are absurd. Each
847 # example may or may not have a payload. If it does not then the
848 # previous example's payload should be used. We accomplish this by
849 # writing it into the section_data. Because we update each example
850 # with the section data it will be overwritten if a new payload value
851 # is present. NIST should be ashamed of their vector creation.
852 if name.lower() == "payload":
853 section_data[name.lower()] = value.encode("ascii")
854
855 # Result is a special token telling us if the test should pass/fail.
856 # This is only present in the DVPT CCM tests
857 if name.lower() == "result":
858 if value.lower() == "pass":
859 test_data["fail"] = False
860 else:
861 test_data["fail"] = True
862 continue
863
864 # COUNT is a special token that indicates a new block of data
865 if name.lower() == "count":
866 test_data = {}
867 test_data.update(global_data)
868 test_data.update(section_data)
869 data.append(test_data)
870 continue
871 # For all other tokens we simply want the name, value stored in
872 # the dictionary
873 else:
874 test_data[name.lower()] = value.encode("ascii")
875
876 return data
Alex Gaynor2e85a922018-07-16 11:18:33 -0400877
878
879class WycheproofTest(object):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800880 def __init__(self, testfiledata, testgroup, testcase):
881 self.testfiledata = testfiledata
Alex Gaynor2e85a922018-07-16 11:18:33 -0400882 self.testgroup = testgroup
883 self.testcase = testcase
884
885 def __repr__(self):
Lucia Lic6ba99d2021-11-08 22:06:11 +0800886 return "<WycheproofTest({!r}, {!r}, {!r}, tcId={})>".format(
887 self.testfiledata,
888 self.testgroup,
889 self.testcase,
890 self.testcase["tcId"],
Alex Gaynor2e85a922018-07-16 11:18:33 -0400891 )
892
893 @property
894 def valid(self):
895 return self.testcase["result"] == "valid"
896
897 @property
898 def acceptable(self):
899 return self.testcase["result"] == "acceptable"
900
Alex Gaynorfeb13452018-07-18 05:20:48 -0500901 @property
902 def invalid(self):
903 return self.testcase["result"] == "invalid"
904
Alex Gaynor2e85a922018-07-16 11:18:33 -0400905 def has_flag(self, flag):
906 return flag in self.testcase["flags"]
907
908
Alex Gaynor2e85a922018-07-16 11:18:33 -0400909def load_wycheproof_tests(wycheproof, test_file):
910 path = os.path.join(wycheproof, "testvectors", test_file)
911 with open(path) as f:
912 data = json.load(f)
Lucia Lic6ba99d2021-11-08 22:06:11 +0800913 for group in data.pop("testGroups"):
Alex Gaynor2e85a922018-07-16 11:18:33 -0400914 cases = group.pop("tests")
915 for c in cases:
Lucia Lic6ba99d2021-11-08 22:06:11 +0800916 yield WycheproofTest(data, group, c)