Skip to content

Commit 6e3edcd

Browse files
Add claims to Attestation (#70)
Co-authored-by: Facundo Tuesca <[email protected]>
1 parent 5a50381 commit 6e3edcd

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- The `Attestation` type now has a `certificate_claims` property to expose
13+
underlying Fulcio signing certificate extensions
14+
([#70](https://github.com/trailofbits/pypi-attestations/pull/70))
15+
1016
## [0.0.17]
1117

1218
### Fixed

src/pypi_attestations/_impl.py

+57
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,45 @@
3838
from sigstore.verify.policy import VerificationPolicy
3939

4040

41+
# List the claims OID supported
42+
# Source: https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md
43+
# We only support the extensions from 1.3.6.1.4.1.57264.1.8 to .22.
44+
# In particular, `1.3.6.1.4.1.57264.1.7 | OtherName SAN` is not supported
45+
# because we believe this is not used in-the-wild.
46+
_FULCIO_CLAIMS_OIDS = [
47+
# 1.3.6.1.4.1.57264.1.8 | Issuer (V2)
48+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.8"),
49+
# 1.3.6.1.4.1.57264.1.9 | Build Signer URI
50+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.9"),
51+
# 1.3.6.1.4.1.57264.1.10 | Build Signer Digest
52+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.10"),
53+
# 1.3.6.1.4.1.57264.1.11 | Runner Environment
54+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.11"),
55+
# 1.3.6.1.4.1.57264.1.12 | Source Repository URI
56+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.12"),
57+
# 1.3.6.1.4.1.57264.1.13 | Source Repository Digest
58+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.13"),
59+
# 1.3.6.1.4.1.57264.1.14 | Source Repository Ref
60+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.14"),
61+
# 1.3.6.1.4.1.57264.1.15 | Source Repository Identifier
62+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.15"),
63+
# 1.3.6.1.4.1.57264.1.16 | Source Repository Owner URI
64+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.16"),
65+
# 1.3.6.1.4.1.57264.1.17 | Source Repository Owner Identifier
66+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.17"),
67+
# 1.3.6.1.4.1.57264.1.18 | Build Config URI
68+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.18"),
69+
# 1.3.6.1.4.1.57264.1.19 | Build Config Digest
70+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.19"),
71+
# 1.3.6.1.4.1.57264.1.20 | Build Trigger
72+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.20"),
73+
# 1.3.6.1.4.1.57264.1.21 | Run Invocation URI
74+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.21"),
75+
# 1.3.6.1.4.1.57264.1.22 | Source Repository Visibility At Signing
76+
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.22"),
77+
]
78+
79+
4180
class Distribution(BaseModel):
4281
"""Represents a Python package distribution.
4382
@@ -167,6 +206,24 @@ def sign(cls, signer: Signer, dist: Distribution) -> Attestation:
167206
except ConversionError as e:
168207
raise AttestationError(str(e))
169208

209+
@property
210+
def certificate_claims(self) -> dict[str, str]:
211+
"""Return the claims present in the certificate.
212+
213+
We only return claims present in `_FULCIO_CLAIMS_OIDS`.
214+
Values are decoded and returned as strings.
215+
"""
216+
certificate = x509.load_der_x509_certificate(self.verification_material.certificate)
217+
claims = {}
218+
for extension in certificate.extensions:
219+
if extension.oid in _FULCIO_CLAIMS_OIDS:
220+
# 1.3.6.1.4.1.57264.1.8 through 1.3.6.1.4.1.57264.1.22 are formatted as DER-encoded
221+
# strings; the ASN.1 tag is UTF8String (0x0C) and the tag class is universal.
222+
value = extension.value.value
223+
claims[extension.oid.dotted_string] = _der_decode_utf8string(value)
224+
225+
return claims
226+
170227
def verify(
171228
self,
172229
identity: VerificationPolicy | Publisher,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"envelope": {
3+
"signature": "MEQCIEK1TlO0FG/yzoXHFu/ML77ATbXSHGgeMOFyT05x9bH3AiA8hubwYj3wV9yznsZkwJjHcjIHGSPgyJ8UZ+QP/o3img==",
4+
"statement": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicHlwaV9hdHRlc3RhdGlvbnMtMC4wLjE2LnRhci5neiIsImRpZ2VzdCI6eyJzaGEyNTYiOiJjYmQyYjk0NmZlMTYwNzkzNjA2ZGVlNDUxNmVmNThhYzU5NTk0NTZlNjk2MzBhN2YwM2U3ZGU3M2FhN2YyNzM3In19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vZG9jcy5weXBpLm9yZy9hdHRlc3RhdGlvbnMvcHVibGlzaC92MSIsInByZWRpY2F0ZSI6bnVsbH0="
5+
},
6+
"verification_material": {
7+
"certificate": "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",
8+
"transparency_entries": [
9+
{
10+
"canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiN2Y0YjdmYzIyMDEwNDQ4ODlkZWZlZDRlNDgzZmQ0NWQ4YWM5MWYwMjcyZTBkOWZjODc3MzAyODY3YzAyYTJmOSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImQ0ZjYxNWJjZDU1YWQzMDUzNDkxNmE1OWQ4N2EyZDBkZDZjNTE2MWE5ODdiMzg5MzY2YWY2ZWE0YjA2YmU2YzUifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVRQ0lFSzFUbE8wRkcveXpvWEhGdS9NTDc3QVRiWFNIR2dlTU9GeVQwNXg5YkgzQWlBOGh1YndZajN3Vjl5em5zWmt3SmpIY2pJSEdTUGd5SjhVWitRUC9vM2ltZz09IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VjdmFrTkRRbTlQWjBGM1NVSkJaMGxWVWpoMWQwMUVLMnRYYVdaNVUxVmlTR1p6U0RFelJFVmlkazg0ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmVFMVVRVE5OYWtrd1RYcEpNVmRvWTA1TmFsRjRUVlJCTTAxcVNURk5la2t4VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjFkRzFxTlVaSGJVTm9hWFZsTVhCaFRIVTRhRTFuVURkQlkzQmxVbmROYUVsc2IwMEtjak15VkdVMlNISlRlREpzT0RCSGVHVmpMMlJDZVVwVE16TlRjR3BCV0ZRMVZVbDZhbmQxWjNOME5rTlhOa2hLVjB0UFEwSmhTWGRuWjFkbFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVlpRbGxIQ213eWFsTTRWVXB4VTFaeFIwTTFWMEl3ZW5RMmNtRXdkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMkpuV1VSV1VqQlNRVkZJTDBKSFVYZFpiMXBuWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBURE5TZVZsWGJITmlNbHBwWVZoU2VncE1NMEkxWTBkcmRGbFlVakJhV0U0d1dWaFNjR0l5TlhwTWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhR015VlhWbFZ6RnpDbEZJU214YWJrMTJaRWRHYm1ONU9USk5RelIzVEdwRk1rMUVhMGREYVhOSFFWRlJRbWMzT0hkQlVVVkZTekpvTUdSSVFucFBhVGgyWkVjNWNscFhOSFVLV1ZkT01HRlhPWFZqZVRWdVlWaFNiMlJYU2pGak1sWjVXVEk1ZFdSSFZuVmtRelZxWWpJd2QwWlJXVXRMZDFsQ1FrRkhSSFo2UVVKQloxRklZMjFXY3dwYVYwWjZXbFJCTWtKbmIzSkNaMFZGUVZsUEwwMUJSVVJDUTJjeFQwZE5ORTU2U214T2FtUnFUVVJPYWs5WFRYZE5la1pwV1ZSamVGbHFSVEpPVkZKdENscHFWVEJOYlZwdFRXcHJkMWt5VVROTlFsVkhRMmx6UjBGUlVVSm5OemgzUVZGUlJVSXpTbXhpUjFab1l6SlZkMHQzV1V0TGQxbENRa0ZIUkhaNlFVSUtRbEZSWkdSSVNtaGhWM2gyV20xS2NHUklUWFpqU0d4M1lWTXhhR1JJVW14ak0xSm9aRWRzZG1KdVRYZElkMWxMUzNkWlFrSkJSMFIyZWtGQ1FtZFJVZ3BqYlZadFkzazVNRmxYWkhwTU0xbDNUR3BCZFUxVVdYZFBkMWxMUzNkWlFrSkJSMFIyZWtGQ1EwRlJkRVJEZEc5a1NGSjNZM3B2ZGt3elVuWmhNbFoxQ2t4dFJtcGtSMngyWW01TmRWb3liREJoU0ZacFpGaE9iR050VG5aaWJsSnNZbTVSZFZreU9YUk5TRUZIUTJselIwRlJVVUpuTnpoM1FWRnJSVmxuZUdjS1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcFlWaFNla3d6UWpWalIydDBXVmhTTUZwWVRqQlpXRkp3WWpJMWVncE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFprUjBadVkzazVNazFETkhkTWFrVXlDazFFWjBkRGFYTkhRVkZSUW1jM09IZEJVVzlGUzJkM2IwNVVhR3BQUkdONVdsUlpNMWw2UVhwWmVteHFUVVJOZUZsdFJUTk5WMGw0VG1wVk1GcHRXVEVLVGtSS2JWcHFTVFZOUjA1clRucEJaRUpuYjNKQ1owVkZRVmxQTDAxQlJVeENRVGhOUkZka2NHUkhhREZaYVRGdllqTk9NRnBYVVhkUlFWbExTM2RaUWdwQ1FVZEVkbnBCUWtSQlVYbEVSRUp2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJaRWhLYUdGWGVIWmFiVXB3WkVoTmRtTkliSGRoVXpGb0NtUklVbXhqTTFKb1pFZHNkbUp1VFhkUFFWbExTM2RaUWtKQlIwUjJla0ZDUkZGUmNVUkRaekZQUjAwMFRucEtiRTVxWkdwTlJFNXFUMWROZDAxNlJta0tXVlJqZUZscVJUSk9WRkp0V21wVk1FMXRXbTFOYW10M1dUSlJNMDFEUlVkRGFYTkhRVkZSUW1jM09IZEJVVFJGUlhkM1VtTnRWbTFqZVRrd1dWZGtlZ3BNTTFsM1RHcEJkVTFVV1hkSFVWbExTM2RaUWtKQlIwUjJla0ZDUkhkUlRFUkJhek5PZWtsNVRrUmpNRTFxVFhkTVoxbExTM2RaUWtKQlIwUjJla0ZDQ2tWQlVXZEVRalZ2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJaRWhLYUdGWGVIWmFiVXB3WkVoTmQwWjNXVXRMZDFsQ1FrRkhSSFo2UVVJS1JWRlJTa1JCWTNsTmVrVXdUa1JKZWsxSVFVZERhWE5IUVZGUlFtYzNPSGRCVWtsRldXZDRaMkZJVWpCalNFMDJUSGs1Ym1GWVVtOWtWMGwxV1RJNWRBcE1NMUo1V1Zkc2MySXlXbWxoV0ZKNlRETkNOV05IYTNSWldGSXdXbGhPTUZsWVVuQmlNalY2VEhrMWJtRllVbTlrVjBsMlpESTVlV0V5V25OaU0yUjZDa3d6U214aVIxWm9ZekpWZFdWWE1YTlJTRXBzV201TmRtUkhSbTVqZVRreVRVTTBkMHhxUlRKTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZKTlJVdG5kMjhLVGxSb2FrOUVZM2xhVkZreldYcEJlbGw2YkdwTlJFMTRXVzFGTTAxWFNYaE9hbFV3V20xWk1VNUVTbTFhYWtrMVRVZE9hMDU2UVZoQ1oyOXlRbWRGUlFwQldVOHZUVUZGVlVKQmEwMUNNMHBzWWtkV2FHTXlWWGRhUVZsTFMzZFpRa0pCUjBSMmVrRkNSbEZTVjBSR1VtOWtTRkozWTNwdmRrd3laSEJrUjJneENsbHBOV3BpTWpCMlpFaEthR0ZYZUhaYWJVcHdaRWhOZG1OSWJIZGhVekZvWkVoU2JHTXpVbWhrUjJ4MlltNU5kbGxYVGpCaFZ6bDFZM2s1ZVdSWE5Yb0tUSHBGZUU1NlRYbE9WRmswVFhwbk1Fd3lSakJrUjFaMFkwaFNla3g2UlhkR1oxbExTM2RaUWtKQlIwUjJla0ZDUm1kUlNVUkJXbmRrVjBwellWZE5kd3BuV1d0SFEybHpSMEZSVVVJeGJtdERRa0ZKUldWM1VqVkJTR05CWkZGRVpGQlVRbkY0YzJOU1RXMU5Xa2hvZVZwYWVtTkRiMnR3WlhWT05EaHlaaXRJQ21sdVMwRk1lVzUxYW1kQlFVRmFUVWw1TDI5d1FVRkJSVUYzUWtkTlJWRkRTVVpYUlRNdmQyOTRjM1pEZEN0VFdtMW1NbEpESzFGdk1YZFlabVZLUTJVS0wwaHlOVTVJZW14M1pEQklRV2xDYVdVeFdGSmpkMU5xTkhWbVIwRXlRMEV2ZVRkQ2JuRXhkMVJXYm5NeVVHNVdNRmx2VTNscFVqUnNha0ZMUW1kbmNRcG9hMnBQVUZGUlJFRjNUbkJCUkVKdFFXcEZRV2hJYXpnMlNFVnRkalJyTTJWNk1XcHdOVlIzWm13d2VtcFVVRXRHYWpSaU1HSlpTRzFOVUVaNlNWUkNDbEo2V2t4WGNFZERMMUp4YXpKc2FsVXlObnB1UVdwRlFUaFVWMVpUYldaWWNHMUpXVVUyTlRJck0ybEVNMUpOZHl0NE0zbHBUMlIxYm01SGFVNWlXR1lLVm5Ob04wdHVXbUl2WjNCd09ETnBUakJETkRWRU4yTXhDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifV19fQ==",
11+
"inclusionPromise": {
12+
"signedEntryTimestamp": "MEYCIQCd4F6sF6WMgFMMK4iOV2hpd/IEJ/ScLRKagSPw6Pf8CgIhAJdfBmna8lHVMxrxxJfI8YmHG8VsGm0lb9EcJLJ+BL+n"
13+
},
14+
"inclusionProof": {
15+
"checkpoint": {
16+
"envelope": "rekor.sigstore.dev - 1193050959916656506\n25533066\nqvs8JkP8+3Utq03DxtSYzwsDWKXEAoriQSm5nauVCqY=\n\n— rekor.sigstore.dev wNI9ajBFAiEApqCgo2XcekZNseJlFzWEX0GK8tkBHHhCYmAPU9aSEkECIEHC7q9+y8OA1uKSWZvIWaM2HN+N+TTbIMCRn7ES3o95\n"
17+
},
18+
"hashes": [
19+
"N8S5teoiks1Q2l1X06qKPa5RBAnKLBmz+MbDm0HBQLQ=",
20+
"oR60wUolAs2vAjVLrNo9glJpArDAREyv2P2bj3aJt5w=",
21+
"4ZD3GEL8Ur5l8CIMyjk8ZxkKxzEu9Lc3LwV8DQfSbsY=",
22+
"WF8fC14uf8N7BWexcGPtcn2ukIqswoQlCOl4ffrjUuY=",
23+
"bWVWNxTbzTRrZ8w26ffyWKjSyKuFMvkBwb9IlaeYyfU=",
24+
"0xmVoTBKl7Win61Lt53r5MffAMQUIxTZve8Fta/wZtg=",
25+
"benQsKBgqC82O3PMKh73dzqGPFgjXPZfBvOVBC9f7VE=",
26+
"DvpH9wBCyQKtTWiggquWWE+DEos61Wkar5IFAE5f24M=",
27+
"vjvWRq9utVBPAcT20yr9yyI/ov6jd+e3RpU+SDkDepY=",
28+
"4lUF0YOu9XkIDXKXA0wMSzd6VeDY3TZAgmoOeWmS2+Y=",
29+
"gf+9m552B3PnkWnO0o4KdVvjcT3WVHLrCbf1DoVYKFw="
30+
],
31+
"logIndex": "25533065",
32+
"rootHash": "qvs8JkP8+3Utq03DxtSYzwsDWKXEAoriQSm5nauVCqY=",
33+
"treeSize": "25533066"
34+
},
35+
"integratedTime": "1731019406",
36+
"kindVersion": {
37+
"kind": "dsse",
38+
"version": "0.0.1"
39+
},
40+
"logId": {
41+
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
42+
},
43+
"logIndex": "147437327"
44+
}
45+
]
46+
},
47+
"version": 1
48+
}

test/test_impl.py

+35
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
dist = impl.Distribution.from_file(dist_path)
3030
dist_bundle_path = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl.sigstore"
3131
dist_attestation_path = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl.attestation"
32+
pypi_attestations_attestation = _ASSETS / "pypi_attestations-0.0.16.tar.gz.attestation"
3233

3334
# produced by actions/attest@v1
3435
gh_signed_dist_path = _ASSETS / "pypi_attestation_models-0.0.4a2.tar.gz"
@@ -417,6 +418,40 @@ def test_verify_unknown_attestation_type(self, monkeypatch: pytest.MonkeyPatch)
417418
with pytest.raises(impl.VerificationError, match="unknown attestation type: foo"):
418419
attestation.verify(pol, dist, staging=True)
419420

421+
def test_certificate_claims(self) -> None:
422+
attestation = impl.Attestation.model_validate_json(
423+
pypi_attestations_attestation.read_text()
424+
)
425+
426+
results = {
427+
("1.3.6.1.4.1.57264.1.8", "https://token.actions.githubusercontent.com"),
428+
(
429+
"1.3.6.1.4.1.57264.1.9",
430+
"https://github.com/trailofbits/pypi-attestations/.github/workflows/release.yml@refs/tags/v0.0.16",
431+
),
432+
("1.3.6.1.4.1.57264.1.10", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
433+
("1.3.6.1.4.1.57264.1.11", "github-hosted"),
434+
("1.3.6.1.4.1.57264.1.12", "https://github.com/trailofbits/pypi-attestations"),
435+
("1.3.6.1.4.1.57264.1.13", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
436+
("1.3.6.1.4.1.57264.1.14", "refs/tags/v0.0.16"),
437+
("1.3.6.1.4.1.57264.1.15", "772247423"),
438+
("1.3.6.1.4.1.57264.1.16", "https://github.com/trailofbits"),
439+
("1.3.6.1.4.1.57264.1.17", "2314423"),
440+
(
441+
"1.3.6.1.4.1.57264.1.18",
442+
"https://github.com/trailofbits/pypi-attestations/.github/workflows/release.yml@refs/tags/v0.0.16",
443+
),
444+
("1.3.6.1.4.1.57264.1.19", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
445+
("1.3.6.1.4.1.57264.1.20", "release"),
446+
(
447+
"1.3.6.1.4.1.57264.1.21",
448+
"https://github.com/trailofbits/pypi-attestations/actions/runs/11732568384/attempts/1",
449+
),
450+
("1.3.6.1.4.1.57264.1.22", "public"),
451+
}
452+
453+
assert not results ^ set(attestation.certificate_claims.items())
454+
420455

421456
def test_from_bundle_missing_signatures() -> None:
422457
bundle = Bundle.from_json(dist_bundle_path.read_bytes())

0 commit comments

Comments
 (0)