Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f057b42
implement conversion from Wycheproof JSON to file_test.h format
sgmenda-aws Nov 11, 2025
457be79
migrate aes_gcm_test to new vector management system
sgmenda-aws Nov 11, 2025
c93f02c
clean up convert_vector.py
sgmenda-aws Nov 11, 2025
611c099
Add requirements traceability for test vectors using duvet
sgmenda-aws Nov 12, 2025
fbb0949
fix: set up directory paths in sync_sources to support skipping phases
sgmenda-aws Nov 17, 2025
3d51844
add convert_vector and generate_spec to __all__
sgmenda-aws Nov 17, 2025
60cac56
move imports to top of file
sgmenda-aws Nov 17, 2025
7fc6b0d
simplify sync_sources by passing args directly
sgmenda-aws Nov 17, 2025
db830e0
remove using_custom_clone_dir parameter, derive from args
sgmenda-aws Nov 17, 2025
a295f64
rename using_custom_clone_dir to reuse_existing and make it required
sgmenda-aws Nov 17, 2025
d4a906b
add comments explaining reuse_existing logic
sgmenda-aws Nov 17, 2025
471acba
add type annotations to convert_vector.py
sgmenda-aws Nov 17, 2025
62d6968
refine write_test_group with clearer comments on skipped keys
sgmenda-aws Nov 17, 2025
39b60be
acknowledge inspiration from convert_wycheproof.go
sgmenda-aws Nov 17, 2025
185bb0e
simplify spec language: 'test against' instead of 'test against the t…
sgmenda-aws Nov 17, 2025
0c47d48
Merge branch 'main' into sync-vectors-convert
sgmenda-aws Nov 17, 2025
ab86705
narrow duvet source pattern to only test files
sgmenda-aws Nov 17, 2025
c31089d
update the cached crypto_test_data archive
sgmenda-aws Nov 18, 2025
564d3bc
Merge branch 'main' into sync-vectors-convert
sgmenda-aws Nov 18, 2025
977e313
Merge branch 'main' into sync-vectors-convert
sgmenda-aws Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crypto/cipher_extra/aead_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1199,8 +1199,11 @@ TEST(AEADTest, WycheproofAESGCMSIV) {
});
}

//= third_party/vectors/vectors_spec.md#wycheproof
//= type=test
//# AWS-LC MUST test against `testvectors_v1/aes_gcm_test.txt`.
TEST(AEADTest, WycheproofAESGCM) {
FileTestGTest("third_party/wycheproof_testvectors/aes_gcm_test.txt",
FileTestGTest("third_party/vectors/converted/wycheproof/testvectors_v1/aes_gcm_test.txt",
[](FileTest *t) {
std::string key_size_str;
ASSERT_TRUE(t->GetInstruction(&key_size_str, "keySize"));
Expand Down
Binary file modified generated-src/crypto_test_data.cc.tar.bz2
Binary file not shown.
2 changes: 1 addition & 1 deletion sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,11 @@ set(
crypto/x509/test/trailing_data_leaf_name_constraints.pem
crypto/x509/test/trailing_data_leaf_subject_alt_name.pem
crypto/x509/test/trailing_data_leaf_subject_key_identifier.pem
third_party/vectors/converted/wycheproof/testvectors_v1/aes_gcm_test.txt
third_party/wycheproof_testvectors/aes_cbc_pkcs5_test.txt
third_party/wycheproof_testvectors/aes_ccm_test.txt
third_party/wycheproof_testvectors/aes_cmac_test.txt
third_party/wycheproof_testvectors/aes_gcm_siv_test.txt
third_party/wycheproof_testvectors/aes_gcm_test.txt
third_party/wycheproof_testvectors/chacha20_poly1305_test.txt
third_party/wycheproof_testvectors/dsa_test.txt
third_party/wycheproof_testvectors/ecdh_secp224r1_test.txt
Expand Down
2 changes: 2 additions & 0 deletions third_party/vectors/.duvet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reports/
requirements/
13 changes: 13 additions & 0 deletions third_party/vectors/.duvet/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"

[[source]]
pattern = "../../crypto/**/*test.cc"

[[specification]]
source = "vectors_spec.md"

[report.html]
enabled = true

[report.snapshot]
enabled = true
7 changes: 7 additions & 0 deletions third_party/vectors/.duvet/snapshot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SPECIFICATION: [Test Vector Specification](vectors_spec.md)
SECTION: [wycheproof](#wycheproof)
TEXT[test]: AWS-LC MUST test against `testvectors_v1/aes_gcm_test.txt`.

SPECIFICATION: [Test Vector Specification](vectors_spec.md)
SECTION: [wycheproof](#wycheproof)
TEXT[!MUST]: AWS-LC MUST test against `testvectors_v1/aes_gcm_test.txt`.
24 changes: 22 additions & 2 deletions third_party/vectors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This directory contains cryptographic test vectors from external sources for use

- `upstream/` - Raw test vectors from third-party sources (checked in for transparency)
- `converted/` - Test vectors converted to our `file_test.h` format (consumed by tests)
- `vectors_spec.md` - Listing of included test vectors in [RFC2119](https://www.rfc-editor.org/rfc/rfc2119) format for use by the `duvet` tool
- `sync.py` - Script that updates upstream test vectors, converts them to the `file_test.h` format, and updates the `vectors_spec.md` specification.
- `vectors_spec.md` - Auto-generated specification listing all test vectors
- `sync.py` - Script that updates upstream test vectors, converts them to the `file_test.h` format, and generates `vectors_spec.md`

## Workflow

Expand All @@ -28,3 +28,23 @@ The following command adds a new vector and re-runs sync:
./sync.py --new [SOURCE_NAME]/[UPSTREAM_REPO_PATH_TO_FILE]
```
The path needs to include the source name from `sources.toml` and the file path relative to the upstream repository, for example `wycheproof/testvectors_v1/aes_gcm_test.json`.

## Requirements Traceability

The `vectors_spec.md` file lists all test vectors using the MUST keyword. Test files include duvet annotations linking them to the specification:

```cpp
//= third_party/vectors/vectors_spec.md#wycheproof
//= type=test
//# AWS-LC MUST test against `testvectors_v1/aes_gcm_test.txt`.
TEST(AEADTest, WycheproofAESGCM) { ... }
```

The [duvet](https://github.com/awslabs/duvet) tool tracks which test vectors are used and where. It verifies that annotations haven't been removed, but does not verify test coverage. This helps document which vectors we use and maintain traceability.

Duvet runs automatically when `sync.py` executes. To manually verify:

```bash
cd third_party/vectors
duvet report --ci
```

Large diffs are not rendered by default.

107 changes: 70 additions & 37 deletions third_party/vectors/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@
import shutil
import filecmp

from vectorslib import utils
from vectorslib import utils, convert_vector, generate_spec
from vectorslib.utils import SyncError


def fetch_sources(
clone_dir: pathlib.Path,
sources: dict,
using_custom_clone_dir: bool = False,
reuse_existing: bool,
):
for source_name, source_info in sources.items():
source_clone_dir = clone_dir / source_name

# Reuse existing clone if present, otherwise clone fresh
if source_clone_dir.is_dir():
assert using_custom_clone_dir
assert reuse_existing # Should only happen with --clone-dir, not with new temp dir
utils.warning(
f"using existing, potentially stale upstream clone of {source_name} at {source_clone_dir}"
)
Expand Down Expand Up @@ -55,11 +56,8 @@ def update_sources(
sources: dict,
new_file: typing.Optional[str],
):
upstream_dir = cwd / "upstream"
upstream_dir.mkdir(parents=True, exist_ok=True)

# Ensure upstream directories exist
for source_name, source_info in sources.items():
source_info["upstream_path"] = upstream_dir / source_name
source_info["upstream_path"].mkdir(parents=True, exist_ok=True)

# Add new file first to catch invalid file names and sources early
Expand Down Expand Up @@ -119,36 +117,81 @@ def convert_sources(
clone_dir: pathlib.Path,
sources: dict,
):
condir = cwd / "converted"
condir.mkdir(parents=True, exist_ok=True)
converted_dir = cwd / "converted"
converted_dir.mkdir(parents=True, exist_ok=True)

for source_name, source_info in sources.items():
source_info["converted_path"] = condir / source_name
source_info["converted_path"] = converted_dir / source_name
source_info["converted_path"].mkdir(parents=True, exist_ok=True)
assert source_info["converted_path"].is_dir()

utils.warning("convert_sources isn't yet fully implemented")
for source_name, source_info in sources.items():
upstream_path = source_info["upstream_path"]
converted_path = source_info["converted_path"]

for upstream_file in upstream_path.rglob("*.json"):
relative_path = upstream_file.relative_to(upstream_path)
converted_file = converted_path / relative_path.with_suffix(".txt")

converted_file.parent.mkdir(parents=True, exist_ok=True)

try:
convert_vector.convert_file(upstream_file, converted_file)
utils.info(f"converted {source_name}/{relative_path}")
except Exception as e:
error_msg = f"failed to convert {source_name}/{relative_path}: {e}"
utils.error(error_msg)
raise SyncError(error_msg)


def generate_and_verify_spec(
cwd: pathlib.Path,
sources: dict,
):
generate_spec.write_spec(cwd, sources)
utils.info("generated vectors_spec.md")

duvet_result = subprocess.run(
["duvet", "report", "--ci"],
cwd=cwd,
capture_output=True,
text=True,
)
if duvet_result.returncode != 0:
utils.error("duvet verification failed")
utils.error(duvet_result.stderr)
raise SyncError("duvet verification failed")
utils.info("duvet verification passed")


def sync_sources(
cwd: pathlib.Path,
clone_dir: pathlib.Path,
sources: dict,
new_file: typing.Optional[str],
skip_update: bool,
skip_convert: bool,
using_custom_clone_dir: bool = False,
args: argparse.Namespace,
):
if not skip_update:
fetch_sources(clone_dir, sources, using_custom_clone_dir)
update_sources(cwd, sources, new_file)
# Set up directory paths that other phases depend on
upstream_dir = cwd / "upstream"
for source_name, source_info in sources.items():
source_info["upstream_path"] = upstream_dir / source_name
source_info["local_path"] = clone_dir / source_name

if not args.skip_update:
reuse_existing = args.clone_dir is not None
fetch_sources(clone_dir, sources, reuse_existing)
update_sources(cwd, sources, args.new)
else:
utils.info("skipping update")

if not skip_convert:
if not args.skip_convert:
convert_sources(cwd, clone_dir, sources)
else:
utils.info("skipping convert")

if not args.skip_spec:
generate_and_verify_spec(cwd, sources)
else:
utils.info("skipping spec generation")


def main() -> int:
cwd = pathlib.Path.cwd()
Expand Down Expand Up @@ -184,6 +227,11 @@ def main() -> int:
action="store_true",
help="skip converting vectors to file_test.h format",
)
parser.add_argument(
"--skip-spec",
action="store_true",
help="skip generating vectors_spec.md and duvet verification",
)
parser.add_argument(
"--clone-dir",
metavar="DIR",
Expand All @@ -198,26 +246,11 @@ def main() -> int:
if args.clone_dir:
clone_dir = pathlib.Path(args.clone_dir)
clone_dir.mkdir(parents=True, exist_ok=True)
sync_sources(
cwd,
clone_dir,
sources,
args.new,
args.skip_update,
args.skip_convert,
using_custom_clone_dir=True,
)
sync_sources(cwd, clone_dir, sources, args)
else:
with tempfile.TemporaryDirectory() as temp_clone_dir:
clone_dir = pathlib.Path(temp_clone_dir)
sync_sources(
cwd,
clone_dir,
sources,
args.new,
args.skip_update,
args.skip_convert,
)
sync_sources(cwd, clone_dir, sources, args)
except SyncError as e:
utils.error(str(e))
return 1
Expand Down
8 changes: 8 additions & 0 deletions third_party/vectors/vectors_spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Test Vector Specification

*This file is generated by sync.py. Do not edit by hand.*

## wycheproof

AWS-LC MUST test against `testvectors_v1/aes_gcm_test.txt`.

2 changes: 1 addition & 1 deletion third_party/vectors/vectorslib/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__all__ = ["utils"]
__all__ = ["utils", "convert_vector", "generate_spec"]
Loading
Loading