Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test, tools: init bundle cert chain tests #78

Merged
merged 9 commits into from
May 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ALL_PY_SRCS := action.py \
sigstore-python-conformance \
$(shell find test/ -name '*.py')
$(shell find test/ -name '*.py') \
$(shell find tools/ -name '*.py')

.PHONY: all
all:
Expand Down
2 changes: 2 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mypy]
ignore_missing_imports = True
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pytest==7.1.3
requests==2.31.0
cryptography>=39
sigstore-protobuf-specs~=0.1.0
woodruffw marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions test/assets/has_root_in_chain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DO NOT MODIFY ME!

this is "has_root_in_chain.txt", a sample input for sigstore-conformance's test suite.

DO NOT MODIFY ME!
1 change: 1 addition & 0 deletions test/assets/has_root_in_chain.txt.sigstore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": {"x509CertificateChain": {"certificates": [{"rawBytes": "MIIC6DCCAm2gAwIBAgIUYSUKGwE4u9WweeQKZDuwQxoMYPowCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNTA1MjAxNDIyWhcNMjMwNTA1MjAyNDIyWjAAMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuIF/kYSSOZk0PpKT91xIswVWquGE/auBrjwKFUqWrZHIeAY9M6QhObabHcRCswJxhu9Jmlx/02Q8Fsw8jZ7/JUNrx2ocb7PM13sJlvz7kqu4Vdun+9UUrqgmg58RZkiro4IBbzCCAWswDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBQ+mIlL//+iuvMIzT7RLzmKu/EaCjAfBgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhkPzAYBgNVHREBAf8EDjAMgQphQHRueS50b3duMCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDAuBgorBgEEAYO/MAEIBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABh+2NIv0AAAQDAEgwRgIhAOe/aKnmAgNde1f+vZyvHMTP7VDCk6eadbW4hCVIOhQZAiEAkG13pJfXdmFHOwuDmQDX/aFRLWPCcXYMES/ZMxH2dScwCgYIKoZIzj0EAwMDaQAwZgIxAPbWoNq/OoFZOsAboK6YsPUX4eJSsuW/mhQ3KcGfT7pn8K7I6L+fBbYOCO2Ic13EhwIxANgHE2rgYDb2m/3I4fdAefwZQd8TcCe4fNfBV2P7dmckQRAlSkqe1Ok1nNIZd9ERFg=="}, {"rawBytes": "MIIB9jCCAXugAwIBAgITDdEJvluliE0AzYaIE4jTMdnFTzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDMyNTE2NTA0NloXDTMyMDMyMjE2NTA0NVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMo9BUNk9QIYisYysC24+2OytoV72YiLonYcqR3yeVnYziPt7Xv++CYE8yoCTiwedUECCWKOcvQKRCJZb9ht4Hzy+VvBx36hK+C6sECCSR0x6pPSiz+cTk1f788ZjBlUZaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9CMrpofas6cK/cDNQa4j6Hj2ZlMB8GA1UdIwQYMBaAFP9CMrpofas6cK/cDNQa4j6Hj2ZlMAoGCCqGSM49BAMDA2kAMGYCMQD+kojuzMwztNay9Ibzjuk//ZL5m6T2OCsm45l1lY004pcb984L926BowodoirFMcMCMQDIJtFHhP/1D3a+M3dAGomOb6O4CmTry3TTPbPsAFnv22YA0Y+P21NVoxKDjdu0tkw="}, {"rawBytes": "MIICGTCCAaCgAwIBAgITJta/okfgHvjabGm1BOzuhrwA1TAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDQxNDIxMzg0MFoXDTMyMDMyMjE2NTA0NVowNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASosAySWJQ/tK5r8T5aHqavk0oI+BKQbnLLdmOMRXHQF/4Hx9KtNfpcdjH9hNKQSBxSlLFFN3tvFCco0qFBzWYwZtsYsBe1l91qYn/9VHFTaEVwYQWIJEEvrs0fvPuAqjajezB5MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRxhjCmFHxib/n31vQFGn9f/+tvrDAfBgNVHSMEGDAWgBT/QjK6aH2rOnCv3AzUGuI+h49mZTAKBggqhkjOPQQDAwNnADBkAjAM1lbKkcqQlE/UspMTbWNo1y2TaJ44tx3l/FJFceTSdDZ+0W1OHHeU4twie/lq8XgCMHQxgEv26xNNiAGyPXbkYgrDPvbOqp0UeWX4mJnLSrBr3aN/KX1SBrKQu220FmVL0Q=="}]}, "tlogEntries": [{"logIndex": "19808100", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1683317662", "inclusionPromise": {"signedEntryTimestamp": "MEQCIBrAVqc3q1+VMG23hvPnHLRT6nZl5KKyWrDLL1p+YwOzAiBIS2RMQ0gPuWQp7bKiK9XEMza+t/fSFdzhrTp7L3icZw=="}, "inclusionProof": {"logIndex": "15644669", "rootHash": "22UV4NXPBQOocU7ACl4zpB57kTBsH38wzL7AXaIG9LQ=", "treeSize": "15644671", "hashes": ["TwftO33MvsVdKbqLYykPuMvrIthVj/mPoaxqKcLpZdg=", "If3570J3g5FxyFhfYCD/3qxM/WwDPG5caEywFssC0zE=", "W3nQ4zteDt5OUP12uV5bYrHcnO26doSKLFeOVBDe2v8=", "B8bXFU5mMTi8Gl0wcKOq8Oj8I1B9kIf7rTvDupEladY=", "YE2xZW1yZd4v6Qo+MQKLIYOZLJhjkhQ/qgoQirGVwgQ=", "nHtutDXuEtHLe59yL3piUNpljDI3s6gpnF4b7LmT+ic=", "sD7KZqybeYHuMSQgYz3JW7+4zTWkz6G217RBB0wsKXY=", "d9BhV+KnVWnl88trDeRD8lSuyaf0aIOgVwcnrjj5qr8=", "tZ+UZOp03fH7Lx/er/8yJAxziC7MkgoGT/WXzsGTqJw=", "aI1irOE0F5RZXQsSpmRwssXbrU2yg11GNAJWIzxc3mg=", "xqG1dQ7nf4Qcphh5HXP+VtIzBqeF/U2j+LHttReQ35g=", "v9zlGsjJIuSDImqlTCN+OVCrq1d/y34BQYZwfRTf6xc=", "bAEBYo+jOMg1C9O/mjDykFP9PqjGoxxHA3fBJG7CyyI=", "TTRIiroWB9wEhVoyRy8fmt4oQjiFycQgOp5G2857Sc4=", "rKxIMjcPAZ0YWXqaJO0aPYLugUNYWFMJWgIahXUGRno=", "eLFN4ryyWkB23M3weZ97MW8ZGGhsyZelnkDEY6htmUk=", "0HFd0CdQfaTcpzWjJfrOZm+NZrwZ+zQqdVftX+2vbdY=", "ARd1+Fh+RXMZz1t6zm8PZlYx9b56mkTnbPMN4yZe4YA=", "1jCSwid4Bdy0yzYb6m4JrH7Z6ekZJyS49R5X5UvfNTE=", "ngQAZt/l8CAEZYOGrGbPC7b/6FftccszfH9VRez0VYs="]}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiNWMwMzdmMzFkNGE4MmMyYmFmMDAyZjA4M2U1Y2IxZGVmNDhjNGFhYjUxZjMzNTQ0ODg1NTBkMWY2YjQwOTAzIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1HVUNNUUR6UVlNc1pLdUVSMjU1S3krYjVkaHo5MUNrM2dpTS80b3o4aUQ0ZDB4UXBZdEZidlBMZjRlMkEyUzNwQlZiWi9RQ01DRnhqVm14MHpFZXpHZ3J1UXlCSEUvaHFXY1lWV1BXR3pmdkdiUGFjZ29KSHhKZUcwd2xKcFFaUjdBdWFkMlB5Zz09IiwicHVibGljS2V5Ijp7ImNvbnRlbnQiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNMlJFTkRRVzB5WjBGM1NVSkJaMGxWV1ZOVlMwZDNSVFIxT1ZkM1pXVlJTMXBFZFhkUmVHOU5XVkJ2ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNVVRVEZOYWtGNFRrUkplVmRvWTA1TmFrMTNUbFJCTVUxcVFYbE9SRWw1VjJwQlFVMUlXWGRGUVZsSUNrdHZXa2w2YWpCRFFWRlpSa3MwUlVWQlEwbEVXV2RCUlhWSlJpOXJXVk5UVDFwck1GQndTMVE1TVhoSmMzZFdWM0YxUjBVdllYVkNjbXAzUzBaVmNWY0tjbHBJU1dWQldUbE5ObEZvVDJKaFlraGpVa056ZDBwNGFIVTVTbTFzZUM4d01sRTRSbk4zT0dwYU55OUtWVTV5ZURKdlkySTNVRTB4TTNOS2JIWjZOd3ByY1hVMFZtUjFiaXM1VlZWeWNXZHRaelU0VWxwcmFYSnZORWxDWW5wRFEwRlhjM2RFWjFsRVZsSXdVRUZSU0M5Q1FWRkVRV2RsUVUxQ1RVZEJNVlZrQ2twUlVVMU5RVzlIUTBOelIwRlJWVVpDZDAxRVRVSXdSMEV4VldSRVoxRlhRa0pSSzIxSmJFd3ZMeXRwZFhaTlNYcFVOMUpNZW0xTGRTOUZZVU5xUVdZS1FtZE9Wa2hUVFVWSFJFRlhaMEpVWmpBcmJsQldhVkZTYkhadGJ6SlBhMjlXWVV4SFRHaG9hMUI2UVZsQ1owNVdTRkpGUWtGbU9FVkVha0ZOWjFGd2FBcFJTRkoxWlZNMU1HSXpaSFZOUTNkSFEybHpSMEZSVVVKbk56aDNRVkZGUlVodGFEQmtTRUo2VDJrNGRsb3liREJoU0ZacFRHMU9kbUpUT1hOaU1tUndDbUpwT1haWldGWXdZVVJCZFVKbmIzSkNaMFZGUVZsUEwwMUJSVWxDUTBGTlNHMW9NR1JJUW5wUGFUaDJXakpzTUdGSVZtbE1iVTUyWWxNNWMySXlaSEFLWW1rNWRsbFlWakJoUkVOQ2FYZFpTMHQzV1VKQ1FVaFhaVkZKUlVGblVqbENTSE5CWlZGQ00wRk9NRGxOUjNKSGVIaEZlVmw0YTJWSVNteHVUbmRMYVFwVGJEWTBNMnA1ZEM4MFpVdGpiMEYyUzJVMlQwRkJRVUpvS3pKT1NYWXdRVUZCVVVSQlJXZDNVbWRKYUVGUFpTOWhTMjV0UVdkT1pHVXhaaXQyV25sMkNraE5WRkEzVmtSRGF6WmxZV1JpVnpSb1ExWkpUMmhSV2tGcFJVRnJSekV6Y0VwbVdHUnRSa2hQZDNWRWJWRkVXQzloUmxKTVYxQkRZMWhaVFVWVEwxb0tUWGhJTW1SVFkzZERaMWxKUzI5YVNYcHFNRVZCZDAxRVlWRkJkMXBuU1hoQlVHSlhiMDV4TDA5dlJscFBjMEZpYjBzMldYTlFWVmcwWlVwVGMzVlhMd3B0YUZFelMyTkhabFEzY0c0NFN6ZEpOa3dyWmtKaVdVOURUekpKWXpFelJXaDNTWGhCVG1kSVJUSnlaMWxFWWpKdEx6TkpOR1prUVdWbWQxcFJaRGhVQ21ORFpUUm1UbVpDVmpKUU4yUnRZMnRSVWtGc1UydHhaVEZQYXpGdVRrbGFaRGxGVWtablBUMEtMUzB0TFMxRlRrUWdRMFZTVkVsR1NVTkJWRVV0TFMwdExRbz0ifX19fQ=="}]}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "tcA38x1KgsK68ALwg+XLHe9IxKq1HzNUSIVQ0fa0CQM="}, "signature": "MGUCMQDzQYMsZKuER255Ky+b5dhz91Ck3giM/4oz8iD4d0xQpYtFbvPLf4e2A2S3pBVbZ/QCMCFxjVmx0zEezGgruQyBHE/hqWcYVWPWGzfvGbPacgoJHxJeG0wlJpQZR7Auad2Pyg=="}}
8 changes: 6 additions & 2 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@

import pytest # type: ignore

from .client import (BundleMaterials, SignatureCertificateMaterials,
SigstoreClient, VerificationMaterials)
from .client import (
BundleMaterials,
SignatureCertificateMaterials,
SigstoreClient,
VerificationMaterials,
)

_M = TypeVar("_M", bound=VerificationMaterials)
_MakeMaterialsByType = Callable[[str, _M], Tuple[Path, _M]]
Expand Down
55 changes: 55 additions & 0 deletions test/test_bundle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from subprocess import CalledProcessError
from test.client import BundleMaterials, SigstoreClient
from test.conftest import _MakeMaterialsByType

import pytest # type: ignore
from cryptography import x509
from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle


def test_verify_rejects_root(
client: SigstoreClient, make_materials_by_type: _MakeMaterialsByType
) -> None:
"""
Check that the client rejects a bundle that contains a root certificate.
"""

materials: BundleMaterials
input_path, materials = make_materials_by_type(
"has_root_in_chain.txt", BundleMaterials
)

with pytest.raises(CalledProcessError):
client.verify(materials, input_path)


def test_sign_does_not_produce_root(
client: SigstoreClient, make_materials_by_type: _MakeMaterialsByType
) -> None:
"""
Check that the client does not produce a bundle that contains a root
certificate.
"""

materials: BundleMaterials
input_path, materials = make_materials_by_type("a.txt", BundleMaterials)
assert not materials.exists()

# Sign for our input.
client.sign(materials, input_path)

# Parse the output bundle.
bundle_contents = materials.bundle.read_bytes()
bundle = Bundle().from_json(bundle_contents)

# Iterate over our cert chain and check for roots.
certs = bundle.verification_material.x509_certificate_chain
for cert in certs.certificates:
cert = x509.load_der_x509_certificate(cert.raw_bytes)

try:
constraints = cert.extensions.get_extension_for_class(x509.BasicConstraints)
assert not constraints.value.ca
# BasicConstraints isn't required to appear in leaf certificates.
except x509.ExtensionNotFound:
pass
47 changes: 47 additions & 0 deletions tools/build_bundle_testcases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python

from pathlib import Path
from sys import argv

from cryptography.hazmat.primitives.serialization import Encoding
from sigstore._internal.tuf import TrustUpdater
from sigstore.oidc import Issuer
from sigstore.sign import Signer
from sigstore_protobuf_specs.dev.sigstore.common.v1 import X509Certificate

if len(argv) < 2:
print(f"usage: {argv[0]} [data directory]")
exit(-1)

_DATA_DIRECTORY = Path(argv[1])
assert _DATA_DIRECTORY.exists() and _DATA_DIRECTORY.is_dir

_SIGNER = Signer.production()
_ISSUER = Issuer.production()
_TOKEN = _ISSUER.identity_token()


def _build_has_root_in_chain():
input = _DATA_DIRECTORY / "has_root_in_chain.txt"
assert input.exists()

result = _SIGNER.sign(input.open("rb"), _TOKEN)
bundle = result._to_bundle()

# XX: this may return certificates for more than one path, which should still
# be fine for our use case. We just need at least one root certificate in the
# bundle chain.
chain_certs = TrustUpdater.staging().get_fulcio_certs()
bundle_chain = bundle.verification_material.x509_certificate_chain
bundle_chain.certificates.extend(
[
X509Certificate(raw_bytes=cert.public_bytes(encoding=Encoding.DER))
for cert in chain_certs
]
)

output = _DATA_DIRECTORY / "has_root_in_chain.txt.sigstore"
print(bundle.to_json(), file=output.open("w"))


_build_has_root_in_chain()
3 changes: 3 additions & 0 deletions tools/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-r ../requirements.txt

sigstore~=1.1.2