-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
Add a separate module for SHAKE vectors, including a protobuf descriptor and classes, a parsing script, and the source files. The protobuf uses the new format with some differences from the one included in the SHA module: we combine the tests from ShortMsg, LongMsg, and VariableOut files into the `tests` field, and add the `mc_test` field containing the Monte-Carlo test. This way, all tests for a given algorithm and orientation fit in one single instance of ShakeVectors. Note that users of these vectors (us) are expected to check whether this is a valid test before using it, as sources other than NIST CAVP are not expected to provide this one.
- Loading branch information
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"SHAKE256": { | ||
"byte": [ | ||
"cavp-SHAKE256-byte.pb2" | ||
], | ||
"bit": [ | ||
"cavp-SHAKE256-bit.pb2" | ||
] | ||
}, | ||
"SHAKE128": { | ||
"byte": [ | ||
"cavp-SHAKE128-byte.pb2" | ||
], | ||
"bit": [ | ||
"cavp-SHAKE128-bit.pb2" | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
syntax = "proto3"; | ||
|
||
package crypto_condor; | ||
|
||
// A single SHAKE test vector. | ||
message ShakeTest { | ||
// The test ID, unique in its set of vectors. | ||
int32 id = 1; | ||
// The type of test. One of: valid, invalid, acceptable. | ||
string type = 2; | ||
// A comment on the test. | ||
string comment = 3; | ||
// Flags that categorize this test. | ||
repeated string flags = 4; | ||
|
||
// The input message. | ||
bytes msg = 5; | ||
// The resulting digest. | ||
bytes out = 6; | ||
} | ||
|
||
// A Monte-Carlo test -- refer to SHA3VS from CAVP for usage instructions. | ||
message ShakeMcTest { | ||
// The test ID, unique in its set of vectors. | ||
int32 id = 1; | ||
// The type of test. One of: valid, invalid, acceptable. | ||
string type = 2; | ||
// A comment on the test. | ||
string comment = 3; | ||
// Flags that categorize this test. | ||
repeated string flags = 4; | ||
|
||
// The initial message. | ||
bytes seed = 5; | ||
// A dictionary of checkpoints: the indexes are the keys, the checkpoints | ||
// are the values. | ||
map<int32, bytes> checkpoints = 6; | ||
} | ||
|
||
// A set of SHAKE test vectors. | ||
message ShakeVectors { | ||
// The source of the test vectors. | ||
string source = 1; | ||
// Description of the source. | ||
string source_desc = 2; | ||
// The URL of the source. | ||
string source_url = 3; | ||
// Whether these are compliance test vectors or not. | ||
bool compliance = 4; | ||
// A dictionary of test flags and their description. | ||
map<string, string> notes = 5; | ||
// The test vectors. | ||
repeated ShakeTest tests = 6; | ||
// The Monte-Carlo test. This field is used for NIST CAVP tests and is not required. | ||
// Users of this class are expected to check the presence of this field. | ||
ShakeMcTest mc_test = 7; | ||
|
||
// The SHAKE variant. | ||
string algorithm = 8; | ||
// The orientation of the implementation: bit- or byte-oriented. | ||
string orientation = 9; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
"""Module to import SHAKE test vectors. | ||
.. caution:: | ||
This module is intended for developers of this tool, as it's only used for | ||
testing and packaging, has hard-coded filenames, and uses relative paths. | ||
""" | ||
|
||
import json | ||
from collections import defaultdict | ||
from pathlib import Path | ||
|
||
from crypto_condor.vectors._shake.shake_pb2 import ShakeVectors | ||
|
||
VECTORS_DIR = Path("crypto_condor/vectors/_shake") | ||
|
||
|
||
def parse_cavp(algorithm: str, orientation: str): | ||
"""Parses SHAKE test vectors from NIST CAVP.""" | ||
assert algorithm in {"SHAKE128", "SHAKE256"} | ||
assert orientation in {"bit", "byte"} | ||
file = VECTORS_DIR / "cavp" / orientation / f"{algorithm}ShortMsg.rsp" | ||
blocks = file.read_text().split("\n\n") | ||
file = VECTORS_DIR / "cavp" / orientation / f"{algorithm}LongMsg.rsp" | ||
blocks += file.read_text().split("\n\n") | ||
file = VECTORS_DIR / "cavp" / orientation / f"{algorithm}VariableOut.rsp" | ||
blocks += file.read_text().split("\n\n") | ||
|
||
vectors = ShakeVectors( | ||
source="NIST CAVP", | ||
source_desc="Vectors from the ShortMsg and LongMsg files.", | ||
source_url="https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Secure-Hashing#sha3vsha3vss", | ||
compliance=True, | ||
algorithm=algorithm, | ||
orientation=orientation, | ||
) | ||
|
||
count = 0 | ||
for block in blocks: | ||
block = block.strip() | ||
if not block or block.startswith(("#", "[")): | ||
continue | ||
|
||
count += 1 | ||
|
||
test = vectors.tests.add() | ||
test.id = count | ||
test.type = "valid" | ||
|
||
is_null = False | ||
lines = block.split("\n") | ||
for line in lines: | ||
key, value = line.split(" = ") | ||
match key: | ||
case "Len": | ||
is_null = int(value) == 0 | ||
case "Msg": | ||
if is_null: | ||
test.msg = b"" | ||
is_null = False | ||
else: | ||
test.msg = bytes.fromhex(value) | ||
case "Output": | ||
test.out = bytes.fromhex(value) | ||
case "COUNT": | ||
# Since we are combining the files we do not use the included count. | ||
continue | ||
case "Outputlen": | ||
# We recompute the output length at runtime from the output. | ||
continue | ||
case _: | ||
raise ValueError("Unknown key '%s'" % key) | ||
|
||
# Now parse Monte-Carlo vectors. | ||
file = VECTORS_DIR / "cavp" / orientation / f"{algorithm}Monte.rsp" | ||
blocks = file.read_text().split("\n\n") | ||
|
||
vectors.mc_test.id = 1 | ||
vectors.mc_test.type = "valid" | ||
vectors.mc_test.flags.extend(["MonteCarlo"]) | ||
|
||
count = 0 | ||
for block in blocks: | ||
block = block.strip() | ||
if block.startswith(("[", "#")) or not block: | ||
continue | ||
|
||
for line in block.split("\n"): | ||
key, value = line.split(" = ") | ||
match key: | ||
case "Msg": | ||
vectors.mc_test.seed = bytes.fromhex(value) | ||
case "COUNT": | ||
count = int(value) | ||
case "Output": | ||
vectors.mc_test.checkpoints[count] = bytes.fromhex(value) | ||
case "Outputlen": | ||
continue | ||
case _: | ||
raise ValueError(f"Unknown key {key}") | ||
|
||
# Finally, write the vectors to a file. | ||
file = VECTORS_DIR / "pb2" / f"cavp-{algorithm}-{orientation}.pb2" | ||
file.write_bytes(vectors.SerializeToString()) | ||
|
||
|
||
def generate_json() -> None: | ||
"""Generates the JSON file categorizing test vectors.""" | ||
pb2_dir = VECTORS_DIR / "pb2" | ||
vectors: dict[str, dict[str, list[str]]] = defaultdict(lambda: defaultdict(list)) | ||
|
||
for file in pb2_dir.iterdir(): | ||
_vec = ShakeVectors() | ||
_vec.ParseFromString(file.read_bytes()) | ||
vectors[_vec.algorithm][_vec.orientation].append(file.name) | ||
|
||
out = VECTORS_DIR / "shake.json" | ||
with out.open("w") as fp: | ||
json.dump(vectors, fp, indent=2) | ||
|
||
|
||
def main(): | ||
"""Imports SHA test vectors.""" | ||
pb2_dir = VECTORS_DIR / "pb2" | ||
pb2_dir.mkdir(exist_ok=True) | ||
|
||
params = [ | ||
(algo, orient) | ||
for algo in {"SHAKE128", "SHAKE256"} | ||
for orient in {"bit", "byte"} | ||
] | ||
|
||
for algo, orient in params: | ||
parse_cavp(algo, orient) | ||
|
||
generate_json() | ||
|
||
imported_marker = VECTORS_DIR / "shake.imported" | ||
imported_marker.touch() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.