aevum-verify 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aevum_verify-0.8.0/.gitignore +52 -0
- aevum_verify-0.8.0/PKG-INFO +23 -0
- aevum_verify-0.8.0/README.md +0 -0
- aevum_verify-0.8.0/pyproject.toml +75 -0
- aevum_verify-0.8.0/src/aevum/verify/__init__.py +20 -0
- aevum_verify-0.8.0/src/aevum/verify/__main__.py +94 -0
- aevum_verify-0.8.0/src/aevum/verify/_core.py +617 -0
- aevum_verify-0.8.0/src/aevum/verify/py.typed +0 -0
- aevum_verify-0.8.0/tests/test_classical.py +112 -0
- aevum_verify-0.8.0/tests/test_hybrid.py +220 -0
- aevum_verify-0.8.0/tests/test_merkle_sth.py +522 -0
- aevum_verify-0.8.0/tests/test_verify_cli.py +92 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.venv/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
|
|
9
|
+
# Build
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
site/
|
|
13
|
+
|
|
14
|
+
# Tools
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.hypothesis/
|
|
19
|
+
.cache/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
*.swo
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Verify scripts (run locally, never commit)
|
|
32
|
+
verify_*.py
|
|
33
|
+
scripts/verify_*.py
|
|
34
|
+
|
|
35
|
+
# Aevum development — never commit (Phase 0+)
|
|
36
|
+
aevum_principles.key
|
|
37
|
+
signed_principles_draft.yaml
|
|
38
|
+
tools/sign_principles.py
|
|
39
|
+
|
|
40
|
+
# Private keys — never commit
|
|
41
|
+
*.key
|
|
42
|
+
*.pem
|
|
43
|
+
|
|
44
|
+
# OpenSSF Scorecard output (Phase 0+)
|
|
45
|
+
results.sarif
|
|
46
|
+
verify_phase3.py
|
|
47
|
+
verify_phase7.py
|
|
48
|
+
verify_phase8.py
|
|
49
|
+
verify_phase*.py
|
|
50
|
+
|
|
51
|
+
# Maintenance generated files — local only, never commit
|
|
52
|
+
maintenance/generated/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aevum-verify
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Aevum — standalone sigchain verifier.
|
|
5
|
+
Project-URL: Homepage, https://aevum.build
|
|
6
|
+
Project-URL: Repository, https://github.com/aevum-labs/aevum
|
|
7
|
+
License: Apache-2.0
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: aevum-core
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
22
|
+
Provides-Extra: pqc
|
|
23
|
+
Requires-Dist: liboqs-python>=0.14.0; extra == 'pqc'
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
[project]
|
|
3
|
+
name = "aevum-verify"
|
|
4
|
+
version = "0.8.0"
|
|
5
|
+
description = "Aevum — standalone sigchain verifier."
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
license = { text = "Apache-2.0" }
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 4 - Beta",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"License :: OSI Approved :: Apache Software License",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Typing :: Typed",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"aevum-core",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
pqc = [
|
|
24
|
+
"liboqs-python>=0.14.0",
|
|
25
|
+
]
|
|
26
|
+
dev = [
|
|
27
|
+
"pytest>=8.0",
|
|
28
|
+
"pytest-asyncio>=0.23",
|
|
29
|
+
"mypy>=1.10",
|
|
30
|
+
"ruff>=0.9",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
aevum-verify = "aevum.verify.__main__:main"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://aevum.build"
|
|
38
|
+
Repository = "https://github.com/aevum-labs/aevum"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/aevum"]
|
|
46
|
+
|
|
47
|
+
[tool.uv.sources]
|
|
48
|
+
aevum-core = { workspace = true }
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
asyncio_mode = "auto"
|
|
53
|
+
addopts = "--tb=short"
|
|
54
|
+
pythonpath = ["src", "tests"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
strict = true
|
|
58
|
+
python_version = "3.11"
|
|
59
|
+
mypy_path = "src"
|
|
60
|
+
ignore_missing_imports = true
|
|
61
|
+
|
|
62
|
+
[[tool.mypy.overrides]]
|
|
63
|
+
module = "oqs"
|
|
64
|
+
ignore_missing_imports = true
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
line-length = 130
|
|
68
|
+
target-version = "py311"
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = ["E", "F", "UP", "B", "SIM", "I", "ANN"]
|
|
72
|
+
ignore = ["ANN401"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"tests/**" = ["ANN"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
from aevum.verify._core import (
|
|
3
|
+
VerifyResult,
|
|
4
|
+
dump_chain,
|
|
5
|
+
event_from_dict,
|
|
6
|
+
event_to_dict,
|
|
7
|
+
load_chain,
|
|
8
|
+
verify_chain,
|
|
9
|
+
verify_entry,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"VerifyResult",
|
|
14
|
+
"verify_entry",
|
|
15
|
+
"verify_chain",
|
|
16
|
+
"load_chain",
|
|
17
|
+
"dump_chain",
|
|
18
|
+
"event_to_dict",
|
|
19
|
+
"event_from_dict",
|
|
20
|
+
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
aevum-verify — standalone sigchain verifier CLI.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
aevum-verify CHAIN_FILE --ed25519-pub HEX [--mldsa65-pub HEX]
|
|
7
|
+
|
|
8
|
+
CHAIN_FILE Path to a JSON file containing a list of serialised chain entries.
|
|
9
|
+
--ed25519-pub Pinned Ed25519 public key as 64-char hex, or @/path/to/file for
|
|
10
|
+
raw 32-byte binary.
|
|
11
|
+
--mldsa65-pub Pinned ML-DSA-65 public key as hex or @filepath; required for
|
|
12
|
+
hybrid (ed25519+ml-dsa-65) chains.
|
|
13
|
+
|
|
14
|
+
Exit codes:
|
|
15
|
+
0 VERIFIED — all entries intact.
|
|
16
|
+
1 FAILED — chain tampered, signature invalid, or trust-anchor mismatch.
|
|
17
|
+
2 Usage error (bad arguments or unreadable file).
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from aevum.verify._core import load_chain, verify_chain
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_key(value: str) -> bytes:
|
|
29
|
+
"""Load a key from a hex string or @filepath (raw binary)."""
|
|
30
|
+
if value.startswith("@"):
|
|
31
|
+
return Path(value[1:]).read_bytes()
|
|
32
|
+
return bytes.fromhex(value)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main() -> None:
|
|
36
|
+
parser = argparse.ArgumentParser(
|
|
37
|
+
prog="aevum-verify",
|
|
38
|
+
description="Verify an Aevum sigchain export against pinned public keys.",
|
|
39
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
40
|
+
epilog=__doc__,
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument("chain_file", metavar="CHAIN_FILE", help="path to JSON chain file")
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--ed25519-pub",
|
|
45
|
+
required=True,
|
|
46
|
+
metavar="HEX",
|
|
47
|
+
help="pinned Ed25519 public key (64-char hex or @filepath)",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--mldsa65-pub",
|
|
51
|
+
default=None,
|
|
52
|
+
metavar="HEX",
|
|
53
|
+
help="pinned ML-DSA-65 public key (hex or @filepath); required for hybrid chains",
|
|
54
|
+
)
|
|
55
|
+
args = parser.parse_args()
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
ed25519_pub = _load_key(args.ed25519_pub)
|
|
59
|
+
except Exception as exc:
|
|
60
|
+
print(f"ERROR: invalid --ed25519-pub: {exc}", file=sys.stderr)
|
|
61
|
+
sys.exit(2)
|
|
62
|
+
|
|
63
|
+
mldsa65_pub: bytes | None = None
|
|
64
|
+
if args.mldsa65_pub:
|
|
65
|
+
try:
|
|
66
|
+
mldsa65_pub = _load_key(args.mldsa65_pub)
|
|
67
|
+
except Exception as exc:
|
|
68
|
+
print(f"ERROR: invalid --mldsa65-pub: {exc}", file=sys.stderr)
|
|
69
|
+
sys.exit(2)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
chain_path = Path(args.chain_file)
|
|
73
|
+
entries = load_chain(chain_path)
|
|
74
|
+
except Exception as exc:
|
|
75
|
+
print(f"ERROR: could not load chain file: {exc}", file=sys.stderr)
|
|
76
|
+
sys.exit(2)
|
|
77
|
+
|
|
78
|
+
result = verify_chain(entries, ed25519_pub=ed25519_pub, mldsa65_pub=mldsa65_pub)
|
|
79
|
+
|
|
80
|
+
if result.ok:
|
|
81
|
+
print(f"VERIFIED — {len(entries)} entries intact")
|
|
82
|
+
sys.exit(0)
|
|
83
|
+
else:
|
|
84
|
+
idx = result.failing_index
|
|
85
|
+
reason = result.reason
|
|
86
|
+
if idx is not None:
|
|
87
|
+
print(f"FAILED — entry {idx}: {reason}", file=sys.stderr)
|
|
88
|
+
else:
|
|
89
|
+
print(f"FAILED — {reason}", file=sys.stderr)
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
main()
|