Skip to content

Commit

Permalink
Add claims to Attestation (#70)
Browse files Browse the repository at this point in the history
Co-authored-by: Facundo Tuesca <facundo.tuesca@trailofbits.com>
  • Loading branch information
DarkaMaul and facutuesca authored Nov 27, 2024
1 parent 5a50381 commit 6e3edcd
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- The `Attestation` type now has a `certificate_claims` property to expose
underlying Fulcio signing certificate extensions
([#70](https://github.com/trailofbits/pypi-attestations/pull/70))

## [0.0.17]

### Fixed
Expand Down
57 changes: 57 additions & 0 deletions src/pypi_attestations/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,45 @@
from sigstore.verify.policy import VerificationPolicy


# List the claims OID supported
# Source: https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md
# We only support the extensions from 1.3.6.1.4.1.57264.1.8 to .22.
# In particular, `1.3.6.1.4.1.57264.1.7 | OtherName SAN` is not supported
# because we believe this is not used in-the-wild.
_FULCIO_CLAIMS_OIDS = [
# 1.3.6.1.4.1.57264.1.8 | Issuer (V2)
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.8"),
# 1.3.6.1.4.1.57264.1.9 | Build Signer URI
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.9"),
# 1.3.6.1.4.1.57264.1.10 | Build Signer Digest
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.10"),
# 1.3.6.1.4.1.57264.1.11 | Runner Environment
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.11"),
# 1.3.6.1.4.1.57264.1.12 | Source Repository URI
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.12"),
# 1.3.6.1.4.1.57264.1.13 | Source Repository Digest
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.13"),
# 1.3.6.1.4.1.57264.1.14 | Source Repository Ref
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.14"),
# 1.3.6.1.4.1.57264.1.15 | Source Repository Identifier
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.15"),
# 1.3.6.1.4.1.57264.1.16 | Source Repository Owner URI
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.16"),
# 1.3.6.1.4.1.57264.1.17 | Source Repository Owner Identifier
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.17"),
# 1.3.6.1.4.1.57264.1.18 | Build Config URI
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.18"),
# 1.3.6.1.4.1.57264.1.19 | Build Config Digest
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.19"),
# 1.3.6.1.4.1.57264.1.20 | Build Trigger
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.20"),
# 1.3.6.1.4.1.57264.1.21 | Run Invocation URI
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.21"),
# 1.3.6.1.4.1.57264.1.22 | Source Repository Visibility At Signing
x509.ObjectIdentifier("1.3.6.1.4.1.57264.1.22"),
]


class Distribution(BaseModel):
"""Represents a Python package distribution.
Expand Down Expand Up @@ -167,6 +206,24 @@ def sign(cls, signer: Signer, dist: Distribution) -> Attestation:
except ConversionError as e:
raise AttestationError(str(e))

@property
def certificate_claims(self) -> dict[str, str]:
"""Return the claims present in the certificate.
We only return claims present in `_FULCIO_CLAIMS_OIDS`.
Values are decoded and returned as strings.
"""
certificate = x509.load_der_x509_certificate(self.verification_material.certificate)
claims = {}
for extension in certificate.extensions:
if extension.oid in _FULCIO_CLAIMS_OIDS:
# 1.3.6.1.4.1.57264.1.8 through 1.3.6.1.4.1.57264.1.22 are formatted as DER-encoded
# strings; the ASN.1 tag is UTF8String (0x0C) and the tag class is universal.
value = extension.value.value
claims[extension.oid.dotted_string] = _der_decode_utf8string(value)

return claims

def verify(
self,
identity: VerificationPolicy | Publisher,
Expand Down
48 changes: 48 additions & 0 deletions test/assets/pypi_attestations-0.0.16.tar.gz.attestation
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"envelope": {
"signature": "MEQCIEK1TlO0FG/yzoXHFu/ML77ATbXSHGgeMOFyT05x9bH3AiA8hubwYj3wV9yznsZkwJjHcjIHGSPgyJ8UZ+QP/o3img==",
"statement": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicHlwaV9hdHRlc3RhdGlvbnMtMC4wLjE2LnRhci5neiIsImRpZ2VzdCI6eyJzaGEyNTYiOiJjYmQyYjk0NmZlMTYwNzkzNjA2ZGVlNDUxNmVmNThhYzU5NTk0NTZlNjk2MzBhN2YwM2U3ZGU3M2FhN2YyNzM3In19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vZG9jcy5weXBpLm9yZy9hdHRlc3RhdGlvbnMvcHVibGlzaC92MSIsInByZWRpY2F0ZSI6bnVsbH0="
},
"verification_material": {
"certificate": "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",
"transparency_entries": [
{
"canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiN2Y0YjdmYzIyMDEwNDQ4ODlkZWZlZDRlNDgzZmQ0NWQ4YWM5MWYwMjcyZTBkOWZjODc3MzAyODY3YzAyYTJmOSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImQ0ZjYxNWJjZDU1YWQzMDUzNDkxNmE1OWQ4N2EyZDBkZDZjNTE2MWE5ODdiMzg5MzY2YWY2ZWE0YjA2YmU2YzUifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVRQ0lFSzFUbE8wRkcveXpvWEhGdS9NTDc3QVRiWFNIR2dlTU9GeVQwNXg5YkgzQWlBOGh1YndZajN3Vjl5em5zWmt3SmpIY2pJSEdTUGd5SjhVWitRUC9vM2ltZz09IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VjdmFrTkRRbTlQWjBGM1NVSkJaMGxWVWpoMWQwMUVLMnRYYVdaNVUxVmlTR1p6U0RFelJFVmlkazg0ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmVFMVVRVE5OYWtrd1RYcEpNVmRvWTA1TmFsRjRUVlJCTTAxcVNURk5la2t4VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjFkRzFxTlVaSGJVTm9hWFZsTVhCaFRIVTRhRTFuVURkQlkzQmxVbmROYUVsc2IwMEtjak15VkdVMlNISlRlREpzT0RCSGVHVmpMMlJDZVVwVE16TlRjR3BCV0ZRMVZVbDZhbmQxWjNOME5rTlhOa2hLVjB0UFEwSmhTWGRuWjFkbFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVlpRbGxIQ213eWFsTTRWVXB4VTFaeFIwTTFWMEl3ZW5RMmNtRXdkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMkpuV1VSV1VqQlNRVkZJTDBKSFVYZFpiMXBuWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBURE5TZVZsWGJITmlNbHBwWVZoU2VncE1NMEkxWTBkcmRGbFlVakJhV0U0d1dWaFNjR0l5TlhwTWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhR015VlhWbFZ6RnpDbEZJU214YWJrMTJaRWRHYm1ONU9USk5RelIzVEdwRk1rMUVhMGREYVhOSFFWRlJRbWMzT0hkQlVVVkZTekpvTUdSSVFucFBhVGgyWkVjNWNscFhOSFVLV1ZkT01HRlhPWFZqZVRWdVlWaFNiMlJYU2pGak1sWjVXVEk1ZFdSSFZuVmtRelZxWWpJd2QwWlJXVXRMZDFsQ1FrRkhSSFo2UVVKQloxRklZMjFXY3dwYVYwWjZXbFJCTWtKbmIzSkNaMFZGUVZsUEwwMUJSVVJDUTJjeFQwZE5ORTU2U214T2FtUnFUVVJPYWs5WFRYZE5la1pwV1ZSamVGbHFSVEpPVkZKdENscHFWVEJOYlZwdFRXcHJkMWt5VVROTlFsVkhRMmx6UjBGUlVVSm5OemgzUVZGUlJVSXpTbXhpUjFab1l6SlZkMHQzV1V0TGQxbENRa0ZIUkhaNlFVSUtRbEZSWkdSSVNtaGhWM2gyV20xS2NHUklUWFpqU0d4M1lWTXhhR1JJVW14ak0xSm9aRWRzZG1KdVRYZElkMWxMUzNkWlFrSkJSMFIyZWtGQ1FtZFJVZ3BqYlZadFkzazVNRmxYWkhwTU0xbDNUR3BCZFUxVVdYZFBkMWxMUzNkWlFrSkJSMFIyZWtGQ1EwRlJkRVJEZEc5a1NGSjNZM3B2ZGt3elVuWmhNbFoxQ2t4dFJtcGtSMngyWW01TmRWb3liREJoU0ZacFpGaE9iR050VG5aaWJsSnNZbTVSZFZreU9YUk5TRUZIUTJselIwRlJVVUpuTnpoM1FWRnJSVmxuZUdjS1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROU2VWbFhiSE5pTWxwcFlWaFNla3d6UWpWalIydDBXVmhTTUZwWVRqQlpXRkp3WWpJMWVncE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFprUjBadVkzazVNazFETkhkTWFrVXlDazFFWjBkRGFYTkhRVkZSUW1jM09IZEJVVzlGUzJkM2IwNVVhR3BQUkdONVdsUlpNMWw2UVhwWmVteHFUVVJOZUZsdFJUTk5WMGw0VG1wVk1GcHRXVEVLVGtSS2JWcHFTVFZOUjA1clRucEJaRUpuYjNKQ1owVkZRVmxQTDAxQlJVeENRVGhOUkZka2NHUkhhREZaYVRGdllqTk9NRnBYVVhkUlFWbExTM2RaUWdwQ1FVZEVkbnBCUWtSQlVYbEVSRUp2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJaRWhLYUdGWGVIWmFiVXB3WkVoTmRtTkliSGRoVXpGb0NtUklVbXhqTTFKb1pFZHNkbUp1VFhkUFFWbExTM2RaUWtKQlIwUjJla0ZDUkZGUmNVUkRaekZQUjAwMFRucEtiRTVxWkdwTlJFNXFUMWROZDAxNlJta0tXVlJqZUZscVJUSk9WRkp0V21wVk1FMXRXbTFOYW10M1dUSlJNMDFEUlVkRGFYTkhRVkZSUW1jM09IZEJVVFJGUlhkM1VtTnRWbTFqZVRrd1dWZGtlZ3BNTTFsM1RHcEJkVTFVV1hkSFVWbExTM2RaUWtKQlIwUjJla0ZDUkhkUlRFUkJhek5PZWtsNVRrUmpNRTFxVFhkTVoxbExTM2RaUWtKQlIwUjJla0ZDQ2tWQlVXZEVRalZ2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJaRWhLYUdGWGVIWmFiVXB3WkVoTmQwWjNXVXRMZDFsQ1FrRkhSSFo2UVVJS1JWRlJTa1JCWTNsTmVrVXdUa1JKZWsxSVFVZERhWE5IUVZGUlFtYzNPSGRCVWtsRldXZDRaMkZJVWpCalNFMDJUSGs1Ym1GWVVtOWtWMGwxV1RJNWRBcE1NMUo1V1Zkc2MySXlXbWxoV0ZKNlRETkNOV05IYTNSWldGSXdXbGhPTUZsWVVuQmlNalY2VEhrMWJtRllVbTlrVjBsMlpESTVlV0V5V25OaU0yUjZDa3d6U214aVIxWm9ZekpWZFdWWE1YTlJTRXBzV201TmRtUkhSbTVqZVRreVRVTTBkMHhxUlRKTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZKTlJVdG5kMjhLVGxSb2FrOUVZM2xhVkZreldYcEJlbGw2YkdwTlJFMTRXVzFGTTAxWFNYaE9hbFV3V20xWk1VNUVTbTFhYWtrMVRVZE9hMDU2UVZoQ1oyOXlRbWRGUlFwQldVOHZUVUZGVlVKQmEwMUNNMHBzWWtkV2FHTXlWWGRhUVZsTFMzZFpRa0pCUjBSMmVrRkNSbEZTVjBSR1VtOWtTRkozWTNwdmRrd3laSEJrUjJneENsbHBOV3BpTWpCMlpFaEthR0ZYZUhaYWJVcHdaRWhOZG1OSWJIZGhVekZvWkVoU2JHTXpVbWhrUjJ4MlltNU5kbGxYVGpCaFZ6bDFZM2s1ZVdSWE5Yb0tUSHBGZUU1NlRYbE9WRmswVFhwbk1Fd3lSakJrUjFaMFkwaFNla3g2UlhkR1oxbExTM2RaUWtKQlIwUjJla0ZDUm1kUlNVUkJXbmRrVjBwellWZE5kd3BuV1d0SFEybHpSMEZSVVVJeGJtdERRa0ZKUldWM1VqVkJTR05CWkZGRVpGQlVRbkY0YzJOU1RXMU5Xa2hvZVZwYWVtTkRiMnR3WlhWT05EaHlaaXRJQ21sdVMwRk1lVzUxYW1kQlFVRmFUVWw1TDI5d1FVRkJSVUYzUWtkTlJWRkRTVVpYUlRNdmQyOTRjM1pEZEN0VFdtMW1NbEpESzFGdk1YZFlabVZLUTJVS0wwaHlOVTVJZW14M1pEQklRV2xDYVdVeFdGSmpkMU5xTkhWbVIwRXlRMEV2ZVRkQ2JuRXhkMVJXYm5NeVVHNVdNRmx2VTNscFVqUnNha0ZMUW1kbmNRcG9hMnBQVUZGUlJFRjNUbkJCUkVKdFFXcEZRV2hJYXpnMlNFVnRkalJyTTJWNk1XcHdOVlIzWm13d2VtcFVVRXRHYWpSaU1HSlpTRzFOVUVaNlNWUkNDbEo2V2t4WGNFZERMMUp4YXpKc2FsVXlObnB1UVdwRlFUaFVWMVpUYldaWWNHMUpXVVUyTlRJck0ybEVNMUpOZHl0NE0zbHBUMlIxYm01SGFVNWlXR1lLVm5Ob04wdHVXbUl2WjNCd09ETnBUakJETkRWRU4yTXhDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifV19fQ==",
"inclusionPromise": {
"signedEntryTimestamp": "MEYCIQCd4F6sF6WMgFMMK4iOV2hpd/IEJ/ScLRKagSPw6Pf8CgIhAJdfBmna8lHVMxrxxJfI8YmHG8VsGm0lb9EcJLJ+BL+n"
},
"inclusionProof": {
"checkpoint": {
"envelope": "rekor.sigstore.dev - 1193050959916656506\n25533066\nqvs8JkP8+3Utq03DxtSYzwsDWKXEAoriQSm5nauVCqY=\n\n— rekor.sigstore.dev wNI9ajBFAiEApqCgo2XcekZNseJlFzWEX0GK8tkBHHhCYmAPU9aSEkECIEHC7q9+y8OA1uKSWZvIWaM2HN+N+TTbIMCRn7ES3o95\n"
},
"hashes": [
"N8S5teoiks1Q2l1X06qKPa5RBAnKLBmz+MbDm0HBQLQ=",
"oR60wUolAs2vAjVLrNo9glJpArDAREyv2P2bj3aJt5w=",
"4ZD3GEL8Ur5l8CIMyjk8ZxkKxzEu9Lc3LwV8DQfSbsY=",
"WF8fC14uf8N7BWexcGPtcn2ukIqswoQlCOl4ffrjUuY=",
"bWVWNxTbzTRrZ8w26ffyWKjSyKuFMvkBwb9IlaeYyfU=",
"0xmVoTBKl7Win61Lt53r5MffAMQUIxTZve8Fta/wZtg=",
"benQsKBgqC82O3PMKh73dzqGPFgjXPZfBvOVBC9f7VE=",
"DvpH9wBCyQKtTWiggquWWE+DEos61Wkar5IFAE5f24M=",
"vjvWRq9utVBPAcT20yr9yyI/ov6jd+e3RpU+SDkDepY=",
"4lUF0YOu9XkIDXKXA0wMSzd6VeDY3TZAgmoOeWmS2+Y=",
"gf+9m552B3PnkWnO0o4KdVvjcT3WVHLrCbf1DoVYKFw="
],
"logIndex": "25533065",
"rootHash": "qvs8JkP8+3Utq03DxtSYzwsDWKXEAoriQSm5nauVCqY=",
"treeSize": "25533066"
},
"integratedTime": "1731019406",
"kindVersion": {
"kind": "dsse",
"version": "0.0.1"
},
"logId": {
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
},
"logIndex": "147437327"
}
]
},
"version": 1
}
35 changes: 35 additions & 0 deletions test/test_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
dist = impl.Distribution.from_file(dist_path)
dist_bundle_path = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl.sigstore"
dist_attestation_path = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl.attestation"
pypi_attestations_attestation = _ASSETS / "pypi_attestations-0.0.16.tar.gz.attestation"

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

def test_certificate_claims(self) -> None:
attestation = impl.Attestation.model_validate_json(
pypi_attestations_attestation.read_text()
)

results = {
("1.3.6.1.4.1.57264.1.8", "https://token.actions.githubusercontent.com"),
(
"1.3.6.1.4.1.57264.1.9",
"https://github.com/trailofbits/pypi-attestations/.github/workflows/release.yml@refs/tags/v0.0.16",
),
("1.3.6.1.4.1.57264.1.10", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
("1.3.6.1.4.1.57264.1.11", "github-hosted"),
("1.3.6.1.4.1.57264.1.12", "https://github.com/trailofbits/pypi-attestations"),
("1.3.6.1.4.1.57264.1.13", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
("1.3.6.1.4.1.57264.1.14", "refs/tags/v0.0.16"),
("1.3.6.1.4.1.57264.1.15", "772247423"),
("1.3.6.1.4.1.57264.1.16", "https://github.com/trailofbits"),
("1.3.6.1.4.1.57264.1.17", "2314423"),
(
"1.3.6.1.4.1.57264.1.18",
"https://github.com/trailofbits/pypi-attestations/.github/workflows/release.yml@refs/tags/v0.0.16",
),
("1.3.6.1.4.1.57264.1.19", "58c872e67c03c9c031ba71b1654ff542ff290cd7"),
("1.3.6.1.4.1.57264.1.20", "release"),
(
"1.3.6.1.4.1.57264.1.21",
"https://github.com/trailofbits/pypi-attestations/actions/runs/11732568384/attempts/1",
),
("1.3.6.1.4.1.57264.1.22", "public"),
}

assert not results ^ set(attestation.certificate_claims.items())


def test_from_bundle_missing_signatures() -> None:
bundle = Bundle.from_json(dist_bundle_path.read_bytes())
Expand Down

0 comments on commit 6e3edcd

Please sign in to comment.