guanghu-ice-heart/server-tools/enterprise-lighthouse/test_domain_manifest_signing.py

148 lines
5.6 KiB
Python

#!/usr/bin/env python3
import base64
import subprocess
import tempfile
import unittest
from pathlib import Path
from domain_manifest_signing import (
SignerConfigurationError,
canonical_manifest_payload,
load_signer_config_from_environment,
sign_domain_manifest,
)
class DomainManifestSigningTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.root = Path(self.temp.name)
self.private_key = self.root / "signer.pem"
self.public_key = self.root / "signer-public.pem"
subprocess.run(
["openssl", "genpkey", "-algorithm", "ED25519", "-out", self.private_key],
check=True,
capture_output=True,
)
self.private_key.chmod(0o600)
subprocess.run(
["openssl", "pkey", "-in", self.private_key, "-pubout", "-out", self.public_key],
check=True,
capture_output=True,
)
def tearDown(self):
self.temp.cleanup()
def test_canonical_payload_matches_the_hololake_client_contract(self):
encoded = canonical_manifest_payload(
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="b" * 40,
)
self.assertEqual(
encoded,
b'{"domainId":"DOM-FIFTH-0001","repositoryId":"REPO-012",'
b'"schema":"gh-aios.domain-manifest/v1","signerId":"GH-LIGHTHOUSE-001",'
b'"sourceCommit":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}',
)
def test_signs_a_digest_bound_ed25519_envelope(self):
envelope = sign_domain_manifest(
private_key_path=self.private_key,
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="b" * 40,
)
payload = canonical_manifest_payload(
domain_id=envelope["domainId"],
repository_id=envelope["repositoryId"],
signer_id=envelope["signerId"],
source_commit=envelope["sourceCommit"],
)
signature_path = self.root / "signature.bin"
payload_path = self.root / "payload.json"
payload_path.write_bytes(payload)
signature_path.write_bytes(base64.b64decode(envelope["signature"], validate=True))
verified = subprocess.run(
[
"openssl", "pkeyutl", "-verify", "-rawin", "-pubin",
"-inkey", self.public_key, "-sigfile", signature_path, "-in", payload_path,
],
capture_output=True,
)
self.assertEqual(verified.returncode, 0, verified.stderr.decode())
self.assertEqual(len(envelope["digest"]), 64)
self.assertEqual(set(envelope), {
"digest", "domainId", "repositoryId", "schema", "signature", "signerId", "sourceCommit",
})
def test_payload_tampering_does_not_verify(self):
envelope = sign_domain_manifest(
private_key_path=self.private_key,
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="b" * 40,
)
signature_path = self.root / "signature.bin"
tampered_path = self.root / "tampered.json"
signature_path.write_bytes(base64.b64decode(envelope["signature"], validate=True))
tampered = canonical_manifest_payload(
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="c" * 40,
)
tampered_path.write_bytes(tampered)
verified = subprocess.run(
[
"openssl", "pkeyutl", "-verify", "-rawin", "-pubin",
"-inkey", self.public_key, "-sigfile", signature_path, "-in", tampered_path,
],
capture_output=True,
)
self.assertNotEqual(verified.returncode, 0)
def test_signing_is_unavailable_without_complete_environment_configuration(self):
self.assertIsNone(load_signer_config_from_environment({}))
with self.assertRaises(SignerConfigurationError):
load_signer_config_from_environment({"LIGHTHOUSE_DOMAIN_SIGNING_KEY_PATH": str(self.private_key)})
def test_rejects_relative_or_permissive_private_key_files(self):
with self.assertRaises(SignerConfigurationError):
sign_domain_manifest(
private_key_path=Path("relative.pem"),
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="b" * 40,
)
self.private_key.chmod(0o644)
with self.assertRaises(SignerConfigurationError):
sign_domain_manifest(
private_key_path=self.private_key,
domain_id="DOM-FIFTH-0001",
repository_id="REPO-012",
signer_id="GH-LIGHTHOUSE-001",
source_commit="b" * 40,
)
def test_rejects_unbounded_identifiers_and_invalid_source_commits(self):
for field, value in (("domain_id", "../../escape"), ("source_commit", "not-a-commit")):
arguments = {
"private_key_path": self.private_key,
"domain_id": "DOM-FIFTH-0001",
"repository_id": "REPO-012",
"signer_id": "GH-LIGHTHOUSE-001",
"source_commit": "b" * 40,
}
arguments[field] = value
with self.assertRaises(SignerConfigurationError):
sign_domain_manifest(**arguments)
if __name__ == "__main__":
unittest.main()