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

Add claims to Attestation #70

Merged
merged 14 commits into from
Nov 27, 2024
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 have a `claims` property to expose underlying Fulcio
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
signing certificate extensions
([#70](https://github.com/trailofbits/pypi-attestations/pull/70))

## [0.0.16]

### Added
Expand Down
27 changes: 26 additions & 1 deletion src/pypi_attestations/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import base64
import json
from enum import Enum
from typing import TYPE_CHECKING, Annotated, Any, Literal, NewType, Optional, Union, get_args
from typing import TYPE_CHECKING, Annotated, Any, Literal, NewType, Optional, Union, cast, get_args

import sigstore.errors
from annotated_types import MinLen # noqa: TCH002
Expand Down Expand Up @@ -191,6 +191,31 @@ def sign(cls, signer: Signer, dist: Distribution) -> Attestation:
except ConversionError as e:
raise AttestationError(str(e))

@property
def claims(self) -> dict[x509.ObjectIdentifier, str]:
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
"""Returns the claims present in the Certificate that matches Fulcio OID.
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved

The complete list is avaible on Fulcio documentation but we only return
non deprecated extensions (from 1.3.6.1.4.1.57264.1.8 to .22):
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md

Values are decoded and returned as strings.
"""
fulcio_oid = x509.ObjectIdentifier("1.3.6.1.4.1.57264.1")
certificate = x509.load_der_x509_certificate(self.verification_material.certificate)
claims = {}
for extension in certificate.extensions:
identifier = int(extension.oid.dotted_string.rsplit(".", 1)[1])
if (
extension.oid.dotted_string.startswith(fulcio_oid.dotted_string)
and 8 <= identifier <= 22
):
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
# 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] = cast(bytes, der_decode(value, UTF8String)[0]).decode()
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
}
37 changes: 37 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 @@ -394,6 +395,42 @@ 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_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 ^ {
(key.dotted_string, value) for key, value in attestation.claims.items()
}


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