Skip to content

Commit

Permalink
[cryptotest] Parser for NIST SHA2/3, SHAKE test vectors
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Torok <[email protected]>
  • Loading branch information
RyanTorok committed Feb 14, 2024
1 parent b14e78f commit a5eafda
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
54 changes: 54 additions & 0 deletions sw/host/cryptotest/testvectors/data/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,57 @@ genrule(
("ecdsa_secp384r1_sha3_512_test.json", "wycheproof_ecdsa_p384_sha3_512"),
]
]

[
run_binary(
name = "nist_cavp_{}_{}_{}_json".format(
src_repo,
algorithm.lower(),
msg_type.lower(),
),
srcs = [
"@nist_cavp_{}//:{}{}.rsp".format(src_repo, algorithm, msg_type),
"//sw/host/cryptotest/testvectors/data/schemas:hash_schema.json",
],
outs = [":nist_{}_{}.json".format(
algorithm.lower(),
msg_type.lower(),
)],
args = [
"--src",
"$(location @nist_cavp_{}//:{}{}.rsp)".format(src_repo, algorithm, msg_type),
"--dst",
"$(location :nist_{}_{}.json)".format(
algorithm.lower(),
msg_type.lower(),
),
"--schema",
"$(location //sw/host/cryptotest/testvectors/data/schemas:hash_schema.json)",
"--algorithm",
algorithm,
],
tool = "//sw/host/cryptotest/testvectors/parsers:nist_cavp_hash_parser",
)
for algorithm, src_repo, extra_msg_types in [
("SHA256", "sha2_fips_180_4", []),
("SHA384", "sha2_fips_180_4", []),
("SHA512", "sha2_fips_180_4", []),
("SHA3_256", "sha3_fips_202", []),
("SHA3_384", "sha3_fips_202", []),
("SHA3_512", "sha3_fips_202", []),
(
"SHAKE128",
"shake_fips_202",
["VariableOut"],
),
(
"SHAKE256",
"shake_fips_202",
["VariableOut"],
),
]
for msg_type in [
"ShortMsg",
"LongMsg",
] + extra_msg_types
]
9 changes: 9 additions & 0 deletions sw/host/cryptotest/testvectors/parsers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ py_binary(
],
)

py_binary(
name = "nist_cavp_hash_parser",
srcs = ["nist_cavp_hash_parser.py"],
deps = [
":cryptotest_util",
requirement("jsonschema"),
],
)

py_binary(
name = "nist_cavp_hmac_parser",
srcs = ["nist_cavp_hmac_parser.py"],
Expand Down
107 changes: 107 additions & 0 deletions sw/host/cryptotest/testvectors/parsers/nist_cavp_hash_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

"""Parser for converting NIST CAVP Hash Function test vectors to JSON.
"""

import argparse
import sys
import json
import jsonschema

from cryptotest_util import parse_rsp, str_to_byte_array

# Map hash function names as formatted in the test vectors to their
# JSON schema names
HASH_FUNCTION_NAME_MAPPING = {
"SHA256": "sha-256",
"SHA384": "sha-384",
"SHA512": "sha-512",
"SHA3_256": "sha3-256",
"SHA3_384": "sha3-384",
"SHA3_512": "sha3-512",
"SHAKE128": "shake-128",
"SHAKE256": "shake-256",
}


def parse_testcases(args) -> None:
raw_testcases = parse_rsp(args.src)
test_cases = list()
algorithm = HASH_FUNCTION_NAME_MAPPING[args.algorithm]
# The test vectors for the SHA functions use "MD" as the key for
# the message digest. SHAKE functions use "Output".
if algorithm.startswith("shake"):
digest_key = "Output"
else:
digest_key = "MD"
for section_name in raw_testcases.keys():
count = 1
for test_vec in raw_testcases[section_name]:
# The test vector includes a single placeholder zero byte if its
# intended message length is 0, so we need to ignore that byte.
if "Len" in test_vec and int(test_vec["Len"]) == 0:
message = []
else:
message = str_to_byte_array(test_vec["Msg"])
if "COUNT" in test_vec:
test_case_id = int(test_vec["COUNT"])
else:
test_case_id = count
test_case = {
"vendor": "nist",
"test_case_id": test_case_id,
"algorithm": algorithm,
"message": message,
"digest": str_to_byte_array(test_vec[digest_key]),
# All NIST hash test vectors are expected to have the
# right message digest
"result": True,
}

test_cases.append(test_case)
count += 1

json_filename = f"{args.dst}"
with open(json_filename, "w") as file:
json.dump(test_cases, file, indent=4)

# Validate generated JSON
with open(args.schema) as schema_file:
schema = json.load(schema_file)
jsonschema.validate(test_cases, schema)


def main() -> int:
parser = argparse.ArgumentParser(
description="Parsing utility for NIST CAVP Hash Function test vectors.")

parser.add_argument(
"--src",
help="Source file to import."
)
parser.add_argument(
"--dst",
help="Destination of the output file."
)
parser.add_argument(
"--schema",
type = str,
help = "Test vector schema file"
)
parser.add_argument(
"--algorithm",
type = str,
help = "Hash algorithm the test vectors are testing"
)
args = parser.parse_args()
parse_testcases(args)

return 0


if __name__ == "__main__":
sys.exit(main())

0 comments on commit a5eafda

Please sign in to comment.