Skip to content

Commit 009df18

Browse files
committed
Upstream patch
1 parent 0fe7e4c commit 009df18

7 files changed

Lines changed: 112 additions & 33 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDLYCPSdloA94Te
3+
+68CLqtHJ3qTKttyWyM1uHkcb+AEAExtQGLoKtysmE2QQz8xwpiBKsByAlPTAHZZ
4+
pYIGzof45xEwiRzBqKxE4um1Uhtq+WCth96UeUkjz2G0xMxkFycjHDdpQQ92Hlg+
5+
mvU8VOrs4Xi7PvG5E+lTY7zez89QQ2ZY3OcbFSHVkC1b0COjYqJ7m5TmVWaRyNbW
6+
Nn+00/uZ+hFzcsxAuKa9B9s4ngWELLA1TnWp5yi4q2Pkold4prI3PwF7IfGWtrfo
7+
TgyzCKji12QLLjbd5KiKecGqx9zaV8xs7GnCif47JmOFAEDW5dtUstepH8ysy8wC
8+
IXoZYvOSOsOv2mVz99JikyaWeV0rjNk7p9UPrhiaGKOfzfn+g6bzCJYBlo425pmJ
9+
S/d1EoBoqgeCGniyLkFByQ9zDmzr1NlCkNKtNII1qNoRSMyqJY0qKPAd2bNMHUfT
10+
FB3Eb/AvcOjb4fa5kkxl4gMQwasNaFmDgusqt1JjHK4XcL8UIcvzGPj8LsDrx5iT
11+
HCFSF1gJvAXiLG0o6KFgrHfTBw2V3mxaKZNGr2NA5IMTs2UeGud590VmSvsKhr6R
12+
tekHHGLCrZT9kPxbicBq6OZOhSb0usSpclSvwMzMSIOFNzACXq33nPYqlY0r1vGN
13+
5Gi2P9W2CxAYbnOGgqpzHbt4YoLVrQIDAQABAoICAA2SR3xZPtL8XCGFKhM7NLLK
14+
2k3NI60TPTDt3nxx+sD0RCVbkT4XniI7skauNh6xSFFWSQFSpnADgoz46R8LKTJd
15+
jl1uyOcjb7DTyO9l9e5tixtetbuysZlyJ/2oJFDe5VMHzwAhwfu1NVj4KOVIcA+J
16+
UZqCg3RA9TuwrCnc3uNm8VgnZZn+ZFjxW2raY4ZuTpQbuGlRHvHGNTqWPcS+C0wl
17+
zoRQZNDs1t5GZ+/0m5RLvHZ82zKQpRGts5IjmIgJ7QVCxGvdwqwMBWRl0PMhi3jt
18+
EVVYVXuj2/C3BKAg4NwGf93E6PR9FjoG6x00/HQFYrLZkbVM1JuzUynRPL9KRcvz
19+
It1vxbmW71oWAXXkWcW7MHWR3n7LoQ8x0zcuf2A1PRuAG4zb8hTDyWud9d10NmSd
20+
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
51+
Y3acysRQ2KI9Ero+Ga3hDfaohueVFg==
52+
-----END PRIVATE KEY-----

.ci/assets/keys/test-key.pem

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy2Aj0nZaAPeE3vuvAi6r
3+
Ryd6kyrbclsjNbh5HG/gBABMbUBi6CrcrJhNkEM/McKYgSrAcgJT0wB2WaWCBs6H
4+
+OcRMIkcwaisROLptVIbavlgrYfelHlJI89htMTMZBcnIxw3aUEPdh5YPpr1PFTq
5+
7OF4uz7xuRPpU2O83s/PUENmWNznGxUh1ZAtW9Ajo2Kie5uU5lVmkcjW1jZ/tNP7
6+
mfoRc3LMQLimvQfbOJ4FhCywNU51qecouKtj5KJXeKayNz8BeyHxlra36E4Mswio
7+
4tdkCy423eSoinnBqsfc2lfMbOxpwon+OyZjhQBA1uXbVLLXqR/MrMvMAiF6GWLz
8+
kjrDr9plc/fSYpMmlnldK4zZO6fVD64Ymhijn835/oOm8wiWAZaONuaZiUv3dRKA
9+
aKoHghp4si5BQckPcw5s69TZQpDSrTSCNajaEUjMqiWNKijwHdmzTB1H0xQdxG/w
10+
L3Do2+H2uZJMZeIDEMGrDWhZg4LrKrdSYxyuF3C/FCHL8xj4/C7A68eYkxwhUhdY
11+
CbwF4ixtKOihYKx30wcNld5sWimTRq9jQOSDE7NlHhrnefdFZkr7Coa+kbXpBxxi
12+
wq2U/ZD8W4nAaujmToUm9LrEqXJUr8DMzEiDhTcwAl6t95z2KpWNK9bxjeRotj/V
13+
tgsQGG5zhoKqcx27eGKC1a0CAwEAAQ==
14+
-----END PUBLIC KEY-----

.github/workflows/scripts/before_install.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ legacy_component_name: "pulp_python"
5050
component_name: "python"
5151
component_version: "${COMPONENT_VERSION}"
5252
pulp_env: {}
53-
pulp_settings: {"allowed_export_paths": "/tmp", "allowed_import_paths": "/tmp", "api_root": "/pulp/", "orphan_protection_time": 0, "pypi_api_hostname": "https://pulp:443"}
53+
pulp_settings: {"allowed_export_paths": "/tmp", "allowed_import_paths": "/tmp", "api_root": "/pulp/", "attestation_verification_key": "/etc/pki/attestation/test-key.pem", "orphan_protection_time": 0, "pypi_api_hostname": "https://pulp:443"}
5454
pulp_scheme: "https"
5555
image:
5656
name: "pulp"
@@ -64,6 +64,10 @@ image:
6464
extra_files:
6565
- origin: "pulp_python"
6666
destination: "pulp_python"
67+
- origin: "pulp_python/.ci/assets/keys/test-key.pem"
68+
destination: "/etc/pki/attestation/test-key.pem"
69+
- origin: "pulp_python/.ci/assets/keys/test-key-private.pem"
70+
destination: "/etc/pki/attestation/test-key-private.pem"
6771
services:
6872
- name: "pulp"
6973
image: "pulp:ci_build"

CHANGES/1340.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for verifying SLSA attestations with a configured public key.

pulp_python/app/provenance.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
from django.conf import settings
1111
from pydantic import BaseModel, ConfigDict, Field
1212
from pydantic.alias_generators import to_snake
13+
from pypi_attestations import Attestation as _UpstreamAttestation
1314
from pypi_attestations import (
1415
Distribution,
15-
Envelope,
16+
Envelope, # noqa - needed in module namespace for Pydantic model rebuild
1617
Publisher,
1718
VerificationError,
1819
VerificationMaterial,
@@ -57,24 +58,20 @@ def _as_policy(self):
5758
ExtendedPublisher = Annotated[_ExtendedPublisherUnion, Field(union_mode="left_to_right")]
5859

5960

60-
class Attestation(BaseModel):
61-
"""Attestation object as defined in PEP 740."""
62-
63-
version: Literal[1]
61+
class Attestation(_UpstreamAttestation):
6462
"""
65-
The attestation format's version, which is always 1.
63+
Attestation object as defined in PEP 740.
64+
65+
Inherits from the upstream pypi_attestations.Attestation to keep Sigstore
66+
verification methods (to_bundle, verify), but makes verification_material
67+
optional to support attestations signed with a custom key instead of Sigstore.
6668
"""
6769

6870
verification_material: VerificationMaterial | None = None
6971
"""
7072
Cryptographic materials used to verify `message_signature`.
7173
"""
7274

73-
envelope: Envelope
74-
"""
75-
The enveloped attestation statement and signature.
76-
"""
77-
7875

7976
class AttestationBundle(BaseModel):
8077
"""
@@ -184,7 +181,14 @@ def _verify_signature(attestation, public_key):
184181

185182

186183
def verify_provenance(filename, sha256, provenance, offline=True):
187-
"""Verify the provenance object is valid for the package."""
184+
"""Verify the provenance object is valid for the package.
185+
186+
Attestations with valid Sigstore certificates are verified through the
187+
standard Sigstore path. Attestations without certificates are verified
188+
against a custom public key configured via ATTESTATION_VERIFICATION_KEY.
189+
Currently, it supports RSA PKCS1v15 signatures and SLSA v0.2 provenance
190+
publisher enrichment.
191+
"""
188192
dist = Distribution(name=filename, digest=sha256)
189193
verification_key = _load_verification_key()
190194
for bundle in provenance.attestation_bundles:
@@ -202,7 +206,7 @@ def verify_provenance(filename, sha256, provenance, offline=True):
202206
if verification_key:
203207
_verify_signature(attestation, verification_key)
204208
else:
205-
log.warning(
206-
"Attestation without valid certificate accepted without "
207-
"signature verification (ATTESTATION_VERIFICATION_KEY not set)"
209+
raise VerificationError(
210+
"Attestation has no Sigstore certificate and no custom "
211+
"verification key is configured (ATTESTATION_VERIFICATION_KEY)"
208212
)

pulp_python/tests/functional/api/test_konflux_attestations.py renamed to pulp_python/tests/functional/api/test_slsa_attestations.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
"""Functional tests for Konflux-style attestation verification.
1+
"""Functional tests for SLSA provenance attestation verification.
22
33
These tests exercise the attestation / provenance upload paths with
4-
attestations that carry an RSA signature instead of a Sigstore certificate,
5-
mirroring the format produced by Konflux / Calunga builds.
4+
attestations that carry an RSA signature instead of a Sigstore certificate.
65
7-
The test signing key is generated at image build time and the matching
6+
A static test keypair is shipped in .ci/assets/keys/ and the matching
87
public key is configured as PULP_ATTESTATION_VERIFICATION_KEY so that
98
signature verification is fully exercised end-to-end.
109
"""
@@ -31,7 +30,7 @@
3130

3231

3332
def _build_statement(filename, sha256):
34-
"""Build a minimal in-toto statement for a Konflux attestation."""
33+
"""Build a minimal in-toto statement for an SLSA attestation."""
3534
return json.dumps(
3635
{
3736
"_type": "https://in-toto.io/Statement/v0.1",
@@ -69,7 +68,7 @@ def _b64(data: bytes) -> str:
6968

7069

7170
def _make_attestation(statement_bytes, signature_bytes):
72-
"""Return a single PEP-740 Attestation dict (Konflux flavour)."""
71+
"""Return a single PEP-740 Attestation dict (SLSA flavour)."""
7372
return {
7473
"version": 1,
7574
"verification_material": None,
@@ -128,10 +127,10 @@ def _write(provenance_dict):
128127
# ---------------------------------------------------------------------------
129128

130129

131-
def test_konflux_provenance_stored(
130+
def test_slsa_provenance_stored(
132131
python_bindings, python_content_factory, monitor_task, test_private_key, _provenance_file
133132
):
134-
"""A Konflux-style provenance is accepted and stored when verify=True."""
133+
"""An SLSA provenance is accepted and stored when verify=True."""
135134
content = python_content_factory()
136135

137136
stmt = _build_statement(content.filename, content.sha256)
@@ -155,10 +154,10 @@ def test_konflux_provenance_stored(
155154
assert publisher["kind"] == "Konflux"
156155

157156

158-
def test_konflux_wrong_subject_name_rejected(
157+
def test_slsa_wrong_subject_name_rejected(
159158
python_bindings, python_content_factory, monitor_task, test_private_key, _provenance_file
160159
):
161-
"""Verification rejects a Konflux attestation whose subject name does not match."""
160+
"""Verification rejects an SLSA attestation whose subject name does not match."""
162161
content = python_content_factory()
163162

164163
wrong_name = "wrong-package-0.1.tar.gz"
@@ -177,10 +176,10 @@ def test_konflux_wrong_subject_name_rejected(
177176
assert "subject does not match distribution name" in exc_info.value.task.error["description"]
178177

179178

180-
def test_konflux_wrong_digest_rejected(
179+
def test_slsa_wrong_digest_rejected(
181180
python_bindings, python_content_factory, monitor_task, test_private_key, _provenance_file
182181
):
183-
"""Verification rejects a Konflux attestation whose digest does not match."""
182+
"""Verification rejects an SLSA attestation whose digest does not match."""
184183
content = python_content_factory()
185184

186185
bad_digest = "0" * 64
@@ -199,10 +198,10 @@ def test_konflux_wrong_digest_rejected(
199198
assert "subject does not match distribution digest" in exc_info.value.task.error["description"]
200199

201200

202-
def test_konflux_bad_signature_rejected(
201+
def test_slsa_bad_signature_rejected(
203202
python_bindings, python_content_factory, monitor_task, test_private_key, _provenance_file
204203
):
205-
"""An attestation with a valid subject but tampered signature is rejected."""
204+
"""An SLSA attestation with a valid subject but tampered signature is rejected."""
206205
content = python_content_factory()
207206

208207
stmt = _build_statement(content.filename, content.sha256)
@@ -221,10 +220,10 @@ def test_konflux_bad_signature_rejected(
221220
assert "signature verification failed" in exc_info.value.task.error["description"]
222221

223222

224-
def test_konflux_attestation_via_content_upload(
223+
def test_slsa_attestation_via_content_upload(
225224
python_bindings, python_content_factory, monitor_task, test_private_key
226225
):
227-
"""Konflux-style attestations can be uploaded alongside a package via the content API."""
226+
"""SLSA attestations can be uploaded alongside a package via the content API."""
228227
content = python_content_factory()
229228

230229
stmt = _build_statement(content.filename, content.sha256)

template_config.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ deploy_client_to_rubygems: true
2020
deploy_to_pypi: true
2121
disabled_redis_runners: []
2222
docker_fixtures: false
23-
extra_files: []
23+
extra_files:
24+
- origin: "pulp_python/.ci/assets/keys/test-key.pem"
25+
destination: "/etc/pki/attestation/test-key.pem"
26+
- origin: "pulp_python/.ci/assets/keys/test-key-private.pem"
27+
destination: "/etc/pki/attestation/test-key-private.pem"
2428
github_org: "pulp"
2529
latest_release_branch: "3.33"
2630
lint_ignore: []
@@ -43,6 +47,7 @@ pulp_settings:
4347
allowed_import_paths: "/tmp"
4448
api_root: "/pulp/"
4549
orphan_protection_time: 0
50+
attestation_verification_key: "/etc/pki/attestation/test-key.pem"
4651
pypi_api_hostname: "https://pulp:443"
4752
pulp_settings_azure:
4853
MEDIA_ROOT: ""

0 commit comments

Comments
 (0)